git subrepo clone (merge) https://github.com/kubernetes-incubator/metrics-server.git metrics-server
subrepo:
subdir: "metrics-server"
merged: "92d8412"
upstream:
origin: "https://github.com/kubernetes-incubator/metrics-server.git"
branch: "master"
commit: "92d8412"
git-subrepo:
version: "0.4.0"
origin: "???"
commit: "???"
diff --git a/metrics-server/vendor/k8s.io/apimachinery/pkg/util/cache/cache.go b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/cache/cache.go
new file mode 100644
index 0000000..9a09fe5
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/cache/cache.go
@@ -0,0 +1,83 @@
+/*
+Copyright 2014 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package cache
+
+import (
+ "sync"
+)
+
+const (
+ shardsCount int = 32
+)
+
+type Cache []*cacheShard
+
+func NewCache(maxSize int) Cache {
+ if maxSize < shardsCount {
+ maxSize = shardsCount
+ }
+ cache := make(Cache, shardsCount)
+ for i := 0; i < shardsCount; i++ {
+ cache[i] = &cacheShard{
+ items: make(map[uint64]interface{}),
+ maxSize: maxSize / shardsCount,
+ }
+ }
+ return cache
+}
+
+func (c Cache) getShard(index uint64) *cacheShard {
+ return c[index%uint64(shardsCount)]
+}
+
+// Returns true if object already existed, false otherwise.
+func (c *Cache) Add(index uint64, obj interface{}) bool {
+ return c.getShard(index).add(index, obj)
+}
+
+func (c *Cache) Get(index uint64) (obj interface{}, found bool) {
+ return c.getShard(index).get(index)
+}
+
+type cacheShard struct {
+ items map[uint64]interface{}
+ sync.RWMutex
+ maxSize int
+}
+
+// Returns true if object already existed, false otherwise.
+func (s *cacheShard) add(index uint64, obj interface{}) bool {
+ s.Lock()
+ defer s.Unlock()
+ _, isOverwrite := s.items[index]
+ if !isOverwrite && len(s.items) >= s.maxSize {
+ var randomKey uint64
+ for randomKey = range s.items {
+ break
+ }
+ delete(s.items, randomKey)
+ }
+ s.items[index] = obj
+ return isOverwrite
+}
+
+func (s *cacheShard) get(index uint64) (obj interface{}, found bool) {
+ s.RLock()
+ defer s.RUnlock()
+ obj, found = s.items[index]
+ return
+}
diff --git a/metrics-server/vendor/k8s.io/apimachinery/pkg/util/cache/lruexpirecache.go b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/cache/lruexpirecache.go
new file mode 100644
index 0000000..f6b307a
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/cache/lruexpirecache.go
@@ -0,0 +1,102 @@
+/*
+Copyright 2016 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package cache
+
+import (
+ "sync"
+ "time"
+
+ "github.com/hashicorp/golang-lru"
+)
+
+// Clock defines an interface for obtaining the current time
+type Clock interface {
+ Now() time.Time
+}
+
+// realClock implements the Clock interface by calling time.Now()
+type realClock struct{}
+
+func (realClock) Now() time.Time { return time.Now() }
+
+// LRUExpireCache is a cache that ensures the mostly recently accessed keys are returned with
+// a ttl beyond which keys are forcibly expired.
+type LRUExpireCache struct {
+ // clock is used to obtain the current time
+ clock Clock
+
+ cache *lru.Cache
+ lock sync.Mutex
+}
+
+// NewLRUExpireCache creates an expiring cache with the given size
+func NewLRUExpireCache(maxSize int) *LRUExpireCache {
+ return NewLRUExpireCacheWithClock(maxSize, realClock{})
+}
+
+// NewLRUExpireCacheWithClock creates an expiring cache with the given size, using the specified clock to obtain the current time.
+func NewLRUExpireCacheWithClock(maxSize int, clock Clock) *LRUExpireCache {
+ cache, err := lru.New(maxSize)
+ if err != nil {
+ // if called with an invalid size
+ panic(err)
+ }
+ return &LRUExpireCache{clock: clock, cache: cache}
+}
+
+type cacheEntry struct {
+ value interface{}
+ expireTime time.Time
+}
+
+// Add adds the value to the cache at key with the specified maximum duration.
+func (c *LRUExpireCache) Add(key interface{}, value interface{}, ttl time.Duration) {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+ c.cache.Add(key, &cacheEntry{value, c.clock.Now().Add(ttl)})
+}
+
+// Get returns the value at the specified key from the cache if it exists and is not
+// expired, or returns false.
+func (c *LRUExpireCache) Get(key interface{}) (interface{}, bool) {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+ e, ok := c.cache.Get(key)
+ if !ok {
+ return nil, false
+ }
+ if c.clock.Now().After(e.(*cacheEntry).expireTime) {
+ c.cache.Remove(key)
+ return nil, false
+ }
+ return e.(*cacheEntry).value, true
+}
+
+// Remove removes the specified key from the cache if it exists
+func (c *LRUExpireCache) Remove(key interface{}) {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+ c.cache.Remove(key)
+}
+
+// Keys returns all the keys in the cache, even if they are expired. Subsequent calls to
+// get may return not found. It returns all keys from oldest to newest.
+func (c *LRUExpireCache) Keys() []interface{} {
+ c.lock.Lock()
+ defer c.lock.Unlock()
+ return c.cache.Keys()
+}
diff --git a/metrics-server/vendor/k8s.io/apimachinery/pkg/util/clock/clock.go b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/clock/clock.go
new file mode 100644
index 0000000..9567f90
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/clock/clock.go
@@ -0,0 +1,348 @@
+/*
+Copyright 2014 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package clock
+
+import (
+ "sync"
+ "time"
+)
+
+// Clock allows for injecting fake or real clocks into code that
+// needs to do arbitrary things based on time.
+type Clock interface {
+ Now() time.Time
+ Since(time.Time) time.Duration
+ After(time.Duration) <-chan time.Time
+ NewTimer(time.Duration) Timer
+ Sleep(time.Duration)
+ NewTicker(time.Duration) Ticker
+}
+
+// RealClock really calls time.Now()
+type RealClock struct{}
+
+// Now returns the current time.
+func (RealClock) Now() time.Time {
+ return time.Now()
+}
+
+// Since returns time since the specified timestamp.
+func (RealClock) Since(ts time.Time) time.Duration {
+ return time.Since(ts)
+}
+
+// Same as time.After(d).
+func (RealClock) After(d time.Duration) <-chan time.Time {
+ return time.After(d)
+}
+
+func (RealClock) NewTimer(d time.Duration) Timer {
+ return &realTimer{
+ timer: time.NewTimer(d),
+ }
+}
+
+func (RealClock) NewTicker(d time.Duration) Ticker {
+ return &realTicker{
+ ticker: time.NewTicker(d),
+ }
+}
+
+func (RealClock) Sleep(d time.Duration) {
+ time.Sleep(d)
+}
+
+// FakeClock implements Clock, but returns an arbitrary time.
+type FakeClock struct {
+ lock sync.RWMutex
+ time time.Time
+
+ // waiters are waiting for the fake time to pass their specified time
+ waiters []fakeClockWaiter
+}
+
+type fakeClockWaiter struct {
+ targetTime time.Time
+ stepInterval time.Duration
+ skipIfBlocked bool
+ destChan chan time.Time
+ fired bool
+}
+
+func NewFakeClock(t time.Time) *FakeClock {
+ return &FakeClock{
+ time: t,
+ }
+}
+
+// Now returns f's time.
+func (f *FakeClock) Now() time.Time {
+ f.lock.RLock()
+ defer f.lock.RUnlock()
+ return f.time
+}
+
+// Since returns time since the time in f.
+func (f *FakeClock) Since(ts time.Time) time.Duration {
+ f.lock.RLock()
+ defer f.lock.RUnlock()
+ return f.time.Sub(ts)
+}
+
+// Fake version of time.After(d).
+func (f *FakeClock) After(d time.Duration) <-chan time.Time {
+ f.lock.Lock()
+ defer f.lock.Unlock()
+ stopTime := f.time.Add(d)
+ ch := make(chan time.Time, 1) // Don't block!
+ f.waiters = append(f.waiters, fakeClockWaiter{
+ targetTime: stopTime,
+ destChan: ch,
+ })
+ return ch
+}
+
+// Fake version of time.NewTimer(d).
+func (f *FakeClock) NewTimer(d time.Duration) Timer {
+ f.lock.Lock()
+ defer f.lock.Unlock()
+ stopTime := f.time.Add(d)
+ ch := make(chan time.Time, 1) // Don't block!
+ timer := &fakeTimer{
+ fakeClock: f,
+ waiter: fakeClockWaiter{
+ targetTime: stopTime,
+ destChan: ch,
+ },
+ }
+ f.waiters = append(f.waiters, timer.waiter)
+ return timer
+}
+
+func (f *FakeClock) NewTicker(d time.Duration) Ticker {
+ f.lock.Lock()
+ defer f.lock.Unlock()
+ tickTime := f.time.Add(d)
+ ch := make(chan time.Time, 1) // hold one tick
+ f.waiters = append(f.waiters, fakeClockWaiter{
+ targetTime: tickTime,
+ stepInterval: d,
+ skipIfBlocked: true,
+ destChan: ch,
+ })
+
+ return &fakeTicker{
+ c: ch,
+ }
+}
+
+// Move clock by Duration, notify anyone that's called After, Tick, or NewTimer
+func (f *FakeClock) Step(d time.Duration) {
+ f.lock.Lock()
+ defer f.lock.Unlock()
+ f.setTimeLocked(f.time.Add(d))
+}
+
+// Sets the time.
+func (f *FakeClock) SetTime(t time.Time) {
+ f.lock.Lock()
+ defer f.lock.Unlock()
+ f.setTimeLocked(t)
+}
+
+// Actually changes the time and checks any waiters. f must be write-locked.
+func (f *FakeClock) setTimeLocked(t time.Time) {
+ f.time = t
+ newWaiters := make([]fakeClockWaiter, 0, len(f.waiters))
+ for i := range f.waiters {
+ w := &f.waiters[i]
+ if !w.targetTime.After(t) {
+
+ if w.skipIfBlocked {
+ select {
+ case w.destChan <- t:
+ w.fired = true
+ default:
+ }
+ } else {
+ w.destChan <- t
+ w.fired = true
+ }
+
+ if w.stepInterval > 0 {
+ for !w.targetTime.After(t) {
+ w.targetTime = w.targetTime.Add(w.stepInterval)
+ }
+ newWaiters = append(newWaiters, *w)
+ }
+
+ } else {
+ newWaiters = append(newWaiters, f.waiters[i])
+ }
+ }
+ f.waiters = newWaiters
+}
+
+// Returns true if After has been called on f but not yet satisfied (so you can
+// write race-free tests).
+func (f *FakeClock) HasWaiters() bool {
+ f.lock.RLock()
+ defer f.lock.RUnlock()
+ return len(f.waiters) > 0
+}
+
+func (f *FakeClock) Sleep(d time.Duration) {
+ f.Step(d)
+}
+
+// IntervalClock implements Clock, but each invocation of Now steps the clock forward the specified duration
+type IntervalClock struct {
+ Time time.Time
+ Duration time.Duration
+}
+
+// Now returns i's time.
+func (i *IntervalClock) Now() time.Time {
+ i.Time = i.Time.Add(i.Duration)
+ return i.Time
+}
+
+// Since returns time since the time in i.
+func (i *IntervalClock) Since(ts time.Time) time.Duration {
+ return i.Time.Sub(ts)
+}
+
+// Unimplemented, will panic.
+// TODO: make interval clock use FakeClock so this can be implemented.
+func (*IntervalClock) After(d time.Duration) <-chan time.Time {
+ panic("IntervalClock doesn't implement After")
+}
+
+// Unimplemented, will panic.
+// TODO: make interval clock use FakeClock so this can be implemented.
+func (*IntervalClock) NewTimer(d time.Duration) Timer {
+ panic("IntervalClock doesn't implement NewTimer")
+}
+
+// Unimplemented, will panic.
+// TODO: make interval clock use FakeClock so this can be implemented.
+func (*IntervalClock) NewTicker(d time.Duration) Ticker {
+ panic("IntervalClock doesn't implement NewTicker")
+}
+
+func (*IntervalClock) Sleep(d time.Duration) {
+ panic("IntervalClock doesn't implement Sleep")
+}
+
+// Timer allows for injecting fake or real timers into code that
+// needs to do arbitrary things based on time.
+type Timer interface {
+ C() <-chan time.Time
+ Stop() bool
+ Reset(d time.Duration) bool
+}
+
+// realTimer is backed by an actual time.Timer.
+type realTimer struct {
+ timer *time.Timer
+}
+
+// C returns the underlying timer's channel.
+func (r *realTimer) C() <-chan time.Time {
+ return r.timer.C
+}
+
+// Stop calls Stop() on the underlying timer.
+func (r *realTimer) Stop() bool {
+ return r.timer.Stop()
+}
+
+// Reset calls Reset() on the underlying timer.
+func (r *realTimer) Reset(d time.Duration) bool {
+ return r.timer.Reset(d)
+}
+
+// fakeTimer implements Timer based on a FakeClock.
+type fakeTimer struct {
+ fakeClock *FakeClock
+ waiter fakeClockWaiter
+}
+
+// C returns the channel that notifies when this timer has fired.
+func (f *fakeTimer) C() <-chan time.Time {
+ return f.waiter.destChan
+}
+
+// Stop stops the timer and returns true if the timer has not yet fired, or false otherwise.
+func (f *fakeTimer) Stop() bool {
+ f.fakeClock.lock.Lock()
+ defer f.fakeClock.lock.Unlock()
+
+ newWaiters := make([]fakeClockWaiter, 0, len(f.fakeClock.waiters))
+ for i := range f.fakeClock.waiters {
+ w := &f.fakeClock.waiters[i]
+ if w != &f.waiter {
+ newWaiters = append(newWaiters, *w)
+ }
+ }
+
+ f.fakeClock.waiters = newWaiters
+
+ return !f.waiter.fired
+}
+
+// Reset resets the timer to the fake clock's "now" + d. It returns true if the timer has not yet
+// fired, or false otherwise.
+func (f *fakeTimer) Reset(d time.Duration) bool {
+ f.fakeClock.lock.Lock()
+ defer f.fakeClock.lock.Unlock()
+
+ active := !f.waiter.fired
+
+ f.waiter.fired = false
+ f.waiter.targetTime = f.fakeClock.time.Add(d)
+
+ return active
+}
+
+type Ticker interface {
+ C() <-chan time.Time
+ Stop()
+}
+
+type realTicker struct {
+ ticker *time.Ticker
+}
+
+func (t *realTicker) C() <-chan time.Time {
+ return t.ticker.C
+}
+
+func (t *realTicker) Stop() {
+ t.ticker.Stop()
+}
+
+type fakeTicker struct {
+ c <-chan time.Time
+}
+
+func (t *fakeTicker) C() <-chan time.Time {
+ return t.c
+}
+
+func (t *fakeTicker) Stop() {
+}
diff --git a/metrics-server/vendor/k8s.io/apimachinery/pkg/util/diff/diff.go b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/diff/diff.go
new file mode 100644
index 0000000..bce95ba
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/diff/diff.go
@@ -0,0 +1,305 @@
+/*
+Copyright 2014 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package diff
+
+import (
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "reflect"
+ "sort"
+ "strings"
+ "text/tabwriter"
+
+ "github.com/davecgh/go-spew/spew"
+
+ "k8s.io/apimachinery/pkg/util/validation/field"
+)
+
+// StringDiff diffs a and b and returns a human readable diff.
+func StringDiff(a, b string) string {
+ ba := []byte(a)
+ bb := []byte(b)
+ out := []byte{}
+ i := 0
+ for ; i < len(ba) && i < len(bb); i++ {
+ if ba[i] != bb[i] {
+ break
+ }
+ out = append(out, ba[i])
+ }
+ out = append(out, []byte("\n\nA: ")...)
+ out = append(out, ba[i:]...)
+ out = append(out, []byte("\n\nB: ")...)
+ out = append(out, bb[i:]...)
+ out = append(out, []byte("\n\n")...)
+ return string(out)
+}
+
+// ObjectDiff writes the two objects out as JSON and prints out the identical part of
+// the objects followed by the remaining part of 'a' and finally the remaining part of 'b'.
+// For debugging tests.
+func ObjectDiff(a, b interface{}) string {
+ ab, err := json.Marshal(a)
+ if err != nil {
+ panic(fmt.Sprintf("a: %v", err))
+ }
+ bb, err := json.Marshal(b)
+ if err != nil {
+ panic(fmt.Sprintf("b: %v", err))
+ }
+ return StringDiff(string(ab), string(bb))
+}
+
+// ObjectGoPrintDiff is like ObjectDiff, but uses go-spew to print the objects,
+// which shows absolutely everything by recursing into every single pointer
+// (go's %#v formatters OTOH stop at a certain point). This is needed when you
+// can't figure out why reflect.DeepEqual is returning false and nothing is
+// showing you differences. This will.
+func ObjectGoPrintDiff(a, b interface{}) string {
+ s := spew.ConfigState{DisableMethods: true}
+ return StringDiff(
+ s.Sprintf("%#v", a),
+ s.Sprintf("%#v", b),
+ )
+}
+
+func ObjectReflectDiff(a, b interface{}) string {
+ vA, vB := reflect.ValueOf(a), reflect.ValueOf(b)
+ if vA.Type() != vB.Type() {
+ return fmt.Sprintf("type A %T and type B %T do not match", a, b)
+ }
+ diffs := objectReflectDiff(field.NewPath("object"), vA, vB)
+ if len(diffs) == 0 {
+ return "<no diffs>"
+ }
+ out := []string{""}
+ for _, d := range diffs {
+ elidedA, elidedB := limit(d.a, d.b, 80)
+ out = append(out,
+ fmt.Sprintf("%s:", d.path),
+ fmt.Sprintf(" a: %s", elidedA),
+ fmt.Sprintf(" b: %s", elidedB),
+ )
+ }
+ return strings.Join(out, "\n")
+}
+
+// limit:
+// 1. stringifies aObj and bObj
+// 2. elides identical prefixes if either is too long
+// 3. elides remaining content from the end if either is too long
+func limit(aObj, bObj interface{}, max int) (string, string) {
+ elidedPrefix := ""
+ elidedASuffix := ""
+ elidedBSuffix := ""
+ a, b := fmt.Sprintf("%#v", aObj), fmt.Sprintf("%#v", bObj)
+ for {
+ switch {
+ case len(a) > max && len(a) > 4 && len(b) > 4 && a[:4] == b[:4]:
+ // a is too long, b has data, and the first several characters are the same
+ elidedPrefix = "..."
+ a = a[2:]
+ b = b[2:]
+
+ case len(b) > max && len(b) > 4 && len(a) > 4 && a[:4] == b[:4]:
+ // b is too long, a has data, and the first several characters are the same
+ elidedPrefix = "..."
+ a = a[2:]
+ b = b[2:]
+
+ case len(a) > max:
+ a = a[:max]
+ elidedASuffix = "..."
+
+ case len(b) > max:
+ b = b[:max]
+ elidedBSuffix = "..."
+
+ default:
+ // both are short enough
+ return elidedPrefix + a + elidedASuffix, elidedPrefix + b + elidedBSuffix
+ }
+ }
+}
+
+func public(s string) bool {
+ if len(s) == 0 {
+ return false
+ }
+ return s[:1] == strings.ToUpper(s[:1])
+}
+
+type diff struct {
+ path *field.Path
+ a, b interface{}
+}
+
+type orderedDiffs []diff
+
+func (d orderedDiffs) Len() int { return len(d) }
+func (d orderedDiffs) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
+func (d orderedDiffs) Less(i, j int) bool {
+ a, b := d[i].path.String(), d[j].path.String()
+ if a < b {
+ return true
+ }
+ return false
+}
+
+func objectReflectDiff(path *field.Path, a, b reflect.Value) []diff {
+ switch a.Type().Kind() {
+ case reflect.Struct:
+ var changes []diff
+ for i := 0; i < a.Type().NumField(); i++ {
+ if !public(a.Type().Field(i).Name) {
+ if reflect.DeepEqual(a.Interface(), b.Interface()) {
+ continue
+ }
+ return []diff{{path: path, a: fmt.Sprintf("%#v", a), b: fmt.Sprintf("%#v", b)}}
+ }
+ if sub := objectReflectDiff(path.Child(a.Type().Field(i).Name), a.Field(i), b.Field(i)); len(sub) > 0 {
+ changes = append(changes, sub...)
+ }
+ }
+ return changes
+ case reflect.Ptr, reflect.Interface:
+ if a.IsNil() || b.IsNil() {
+ switch {
+ case a.IsNil() && b.IsNil():
+ return nil
+ case a.IsNil():
+ return []diff{{path: path, a: nil, b: b.Interface()}}
+ default:
+ return []diff{{path: path, a: a.Interface(), b: nil}}
+ }
+ }
+ return objectReflectDiff(path, a.Elem(), b.Elem())
+ case reflect.Chan:
+ if !reflect.DeepEqual(a.Interface(), b.Interface()) {
+ return []diff{{path: path, a: a.Interface(), b: b.Interface()}}
+ }
+ return nil
+ case reflect.Slice:
+ lA, lB := a.Len(), b.Len()
+ l := lA
+ if lB < lA {
+ l = lB
+ }
+ if lA == lB && lA == 0 {
+ if a.IsNil() != b.IsNil() {
+ return []diff{{path: path, a: a.Interface(), b: b.Interface()}}
+ }
+ return nil
+ }
+ var diffs []diff
+ for i := 0; i < l; i++ {
+ if !reflect.DeepEqual(a.Index(i), b.Index(i)) {
+ diffs = append(diffs, objectReflectDiff(path.Index(i), a.Index(i), b.Index(i))...)
+ }
+ }
+ for i := l; i < lA; i++ {
+ diffs = append(diffs, diff{path: path.Index(i), a: a.Index(i), b: nil})
+ }
+ for i := l; i < lB; i++ {
+ diffs = append(diffs, diff{path: path.Index(i), a: nil, b: b.Index(i)})
+ }
+ return diffs
+ case reflect.Map:
+ if reflect.DeepEqual(a.Interface(), b.Interface()) {
+ return nil
+ }
+ aKeys := make(map[interface{}]interface{})
+ for _, key := range a.MapKeys() {
+ aKeys[key.Interface()] = a.MapIndex(key).Interface()
+ }
+ var missing []diff
+ for _, key := range b.MapKeys() {
+ if _, ok := aKeys[key.Interface()]; ok {
+ delete(aKeys, key.Interface())
+ if reflect.DeepEqual(a.MapIndex(key).Interface(), b.MapIndex(key).Interface()) {
+ continue
+ }
+ missing = append(missing, objectReflectDiff(path.Key(fmt.Sprintf("%s", key.Interface())), a.MapIndex(key), b.MapIndex(key))...)
+ continue
+ }
+ missing = append(missing, diff{path: path.Key(fmt.Sprintf("%s", key.Interface())), a: nil, b: b.MapIndex(key).Interface()})
+ }
+ for key, value := range aKeys {
+ missing = append(missing, diff{path: path.Key(fmt.Sprintf("%s", key)), a: value, b: nil})
+ }
+ if len(missing) == 0 {
+ missing = append(missing, diff{path: path, a: a.Interface(), b: b.Interface()})
+ }
+ sort.Sort(orderedDiffs(missing))
+ return missing
+ default:
+ if reflect.DeepEqual(a.Interface(), b.Interface()) {
+ return nil
+ }
+ if !a.CanInterface() {
+ return []diff{{path: path, a: fmt.Sprintf("%#v", a), b: fmt.Sprintf("%#v", b)}}
+ }
+ return []diff{{path: path, a: a.Interface(), b: b.Interface()}}
+ }
+}
+
+// ObjectGoPrintSideBySide prints a and b as textual dumps side by side,
+// enabling easy visual scanning for mismatches.
+func ObjectGoPrintSideBySide(a, b interface{}) string {
+ s := spew.ConfigState{
+ Indent: " ",
+ // Extra deep spew.
+ DisableMethods: true,
+ }
+ sA := s.Sdump(a)
+ sB := s.Sdump(b)
+
+ linesA := strings.Split(sA, "\n")
+ linesB := strings.Split(sB, "\n")
+ width := 0
+ for _, s := range linesA {
+ l := len(s)
+ if l > width {
+ width = l
+ }
+ }
+ for _, s := range linesB {
+ l := len(s)
+ if l > width {
+ width = l
+ }
+ }
+ buf := &bytes.Buffer{}
+ w := tabwriter.NewWriter(buf, width, 0, 1, ' ', 0)
+ max := len(linesA)
+ if len(linesB) > max {
+ max = len(linesB)
+ }
+ for i := 0; i < max; i++ {
+ var a, b string
+ if i < len(linesA) {
+ a = linesA[i]
+ }
+ if i < len(linesB) {
+ b = linesB[i]
+ }
+ fmt.Fprintf(w, "%s\t%s\n", a, b)
+ }
+ w.Flush()
+ return buf.String()
+}
diff --git a/metrics-server/vendor/k8s.io/apimachinery/pkg/util/errors/doc.go b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/errors/doc.go
new file mode 100644
index 0000000..5d4d625
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/errors/doc.go
@@ -0,0 +1,18 @@
+/*
+Copyright 2015 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Package errors implements various utility functions and types around errors.
+package errors // import "k8s.io/apimachinery/pkg/util/errors"
diff --git a/metrics-server/vendor/k8s.io/apimachinery/pkg/util/errors/errors.go b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/errors/errors.go
new file mode 100644
index 0000000..88e9376
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/errors/errors.go
@@ -0,0 +1,201 @@
+/*
+Copyright 2015 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package errors
+
+import (
+ "errors"
+ "fmt"
+)
+
+// MessageCountMap contains occurrence for each error message.
+type MessageCountMap map[string]int
+
+// Aggregate represents an object that contains multiple errors, but does not
+// necessarily have singular semantic meaning.
+type Aggregate interface {
+ error
+ Errors() []error
+}
+
+// NewAggregate converts a slice of errors into an Aggregate interface, which
+// is itself an implementation of the error interface. If the slice is empty,
+// this returns nil.
+// It will check if any of the element of input error list is nil, to avoid
+// nil pointer panic when call Error().
+func NewAggregate(errlist []error) Aggregate {
+ if len(errlist) == 0 {
+ return nil
+ }
+ // In case of input error list contains nil
+ var errs []error
+ for _, e := range errlist {
+ if e != nil {
+ errs = append(errs, e)
+ }
+ }
+ if len(errs) == 0 {
+ return nil
+ }
+ return aggregate(errs)
+}
+
+// This helper implements the error and Errors interfaces. Keeping it private
+// prevents people from making an aggregate of 0 errors, which is not
+// an error, but does satisfy the error interface.
+type aggregate []error
+
+// Error is part of the error interface.
+func (agg aggregate) Error() string {
+ if len(agg) == 0 {
+ // This should never happen, really.
+ return ""
+ }
+ if len(agg) == 1 {
+ return agg[0].Error()
+ }
+ result := fmt.Sprintf("[%s", agg[0].Error())
+ for i := 1; i < len(agg); i++ {
+ result += fmt.Sprintf(", %s", agg[i].Error())
+ }
+ result += "]"
+ return result
+}
+
+// Errors is part of the Aggregate interface.
+func (agg aggregate) Errors() []error {
+ return []error(agg)
+}
+
+// Matcher is used to match errors. Returns true if the error matches.
+type Matcher func(error) bool
+
+// FilterOut removes all errors that match any of the matchers from the input
+// error. If the input is a singular error, only that error is tested. If the
+// input implements the Aggregate interface, the list of errors will be
+// processed recursively.
+//
+// This can be used, for example, to remove known-OK errors (such as io.EOF or
+// os.PathNotFound) from a list of errors.
+func FilterOut(err error, fns ...Matcher) error {
+ if err == nil {
+ return nil
+ }
+ if agg, ok := err.(Aggregate); ok {
+ return NewAggregate(filterErrors(agg.Errors(), fns...))
+ }
+ if !matchesError(err, fns...) {
+ return err
+ }
+ return nil
+}
+
+// matchesError returns true if any Matcher returns true
+func matchesError(err error, fns ...Matcher) bool {
+ for _, fn := range fns {
+ if fn(err) {
+ return true
+ }
+ }
+ return false
+}
+
+// filterErrors returns any errors (or nested errors, if the list contains
+// nested Errors) for which all fns return false. If no errors
+// remain a nil list is returned. The resulting silec will have all
+// nested slices flattened as a side effect.
+func filterErrors(list []error, fns ...Matcher) []error {
+ result := []error{}
+ for _, err := range list {
+ r := FilterOut(err, fns...)
+ if r != nil {
+ result = append(result, r)
+ }
+ }
+ return result
+}
+
+// Flatten takes an Aggregate, which may hold other Aggregates in arbitrary
+// nesting, and flattens them all into a single Aggregate, recursively.
+func Flatten(agg Aggregate) Aggregate {
+ result := []error{}
+ if agg == nil {
+ return nil
+ }
+ for _, err := range agg.Errors() {
+ if a, ok := err.(Aggregate); ok {
+ r := Flatten(a)
+ if r != nil {
+ result = append(result, r.Errors()...)
+ }
+ } else {
+ if err != nil {
+ result = append(result, err)
+ }
+ }
+ }
+ return NewAggregate(result)
+}
+
+// CreateAggregateFromMessageCountMap converts MessageCountMap Aggregate
+func CreateAggregateFromMessageCountMap(m MessageCountMap) Aggregate {
+ if m == nil {
+ return nil
+ }
+ result := make([]error, 0, len(m))
+ for errStr, count := range m {
+ var countStr string
+ if count > 1 {
+ countStr = fmt.Sprintf(" (repeated %v times)", count)
+ }
+ result = append(result, fmt.Errorf("%v%v", errStr, countStr))
+ }
+ return NewAggregate(result)
+}
+
+// Reduce will return err or, if err is an Aggregate and only has one item,
+// the first item in the aggregate.
+func Reduce(err error) error {
+ if agg, ok := err.(Aggregate); ok && err != nil {
+ switch len(agg.Errors()) {
+ case 1:
+ return agg.Errors()[0]
+ case 0:
+ return nil
+ }
+ }
+ return err
+}
+
+// AggregateGoroutines runs the provided functions in parallel, stuffing all
+// non-nil errors into the returned Aggregate.
+// Returns nil if all the functions complete successfully.
+func AggregateGoroutines(funcs ...func() error) Aggregate {
+ errChan := make(chan error, len(funcs))
+ for _, f := range funcs {
+ go func(f func() error) { errChan <- f() }(f)
+ }
+ errs := make([]error, 0)
+ for i := 0; i < cap(errChan); i++ {
+ if err := <-errChan; err != nil {
+ errs = append(errs, err)
+ }
+ }
+ return NewAggregate(errs)
+}
+
+// ErrPreconditionViolated is returned when the precondition is violated
+var ErrPreconditionViolated = errors.New("precondition is violated")
diff --git a/metrics-server/vendor/k8s.io/apimachinery/pkg/util/framer/framer.go b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/framer/framer.go
new file mode 100644
index 0000000..066680f
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/framer/framer.go
@@ -0,0 +1,167 @@
+/*
+Copyright 2015 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Package framer implements simple frame decoding techniques for an io.ReadCloser
+package framer
+
+import (
+ "encoding/binary"
+ "encoding/json"
+ "io"
+)
+
+type lengthDelimitedFrameWriter struct {
+ w io.Writer
+ h [4]byte
+}
+
+func NewLengthDelimitedFrameWriter(w io.Writer) io.Writer {
+ return &lengthDelimitedFrameWriter{w: w}
+}
+
+// Write writes a single frame to the nested writer, prepending it with the length in
+// in bytes of data (as a 4 byte, bigendian uint32).
+func (w *lengthDelimitedFrameWriter) Write(data []byte) (int, error) {
+ binary.BigEndian.PutUint32(w.h[:], uint32(len(data)))
+ n, err := w.w.Write(w.h[:])
+ if err != nil {
+ return 0, err
+ }
+ if n != len(w.h) {
+ return 0, io.ErrShortWrite
+ }
+ return w.w.Write(data)
+}
+
+type lengthDelimitedFrameReader struct {
+ r io.ReadCloser
+ remaining int
+}
+
+// NewLengthDelimitedFrameReader returns an io.Reader that will decode length-prefixed
+// frames off of a stream.
+//
+// The protocol is:
+//
+// stream: message ...
+// message: prefix body
+// prefix: 4 byte uint32 in BigEndian order, denotes length of body
+// body: bytes (0..prefix)
+//
+// If the buffer passed to Read is not long enough to contain an entire frame, io.ErrShortRead
+// will be returned along with the number of bytes read.
+func NewLengthDelimitedFrameReader(r io.ReadCloser) io.ReadCloser {
+ return &lengthDelimitedFrameReader{r: r}
+}
+
+// Read attempts to read an entire frame into data. If that is not possible, io.ErrShortBuffer
+// is returned and subsequent calls will attempt to read the last frame. A frame is complete when
+// err is nil.
+func (r *lengthDelimitedFrameReader) Read(data []byte) (int, error) {
+ if r.remaining <= 0 {
+ header := [4]byte{}
+ n, err := io.ReadAtLeast(r.r, header[:4], 4)
+ if err != nil {
+ return 0, err
+ }
+ if n != 4 {
+ return 0, io.ErrUnexpectedEOF
+ }
+ frameLength := int(binary.BigEndian.Uint32(header[:]))
+ r.remaining = frameLength
+ }
+
+ expect := r.remaining
+ max := expect
+ if max > len(data) {
+ max = len(data)
+ }
+ n, err := io.ReadAtLeast(r.r, data[:max], int(max))
+ r.remaining -= n
+ if err == io.ErrShortBuffer || r.remaining > 0 {
+ return n, io.ErrShortBuffer
+ }
+ if err != nil {
+ return n, err
+ }
+ if n != expect {
+ return n, io.ErrUnexpectedEOF
+ }
+
+ return n, nil
+}
+
+func (r *lengthDelimitedFrameReader) Close() error {
+ return r.r.Close()
+}
+
+type jsonFrameReader struct {
+ r io.ReadCloser
+ decoder *json.Decoder
+ remaining []byte
+}
+
+// NewJSONFramedReader returns an io.Reader that will decode individual JSON objects off
+// of a wire.
+//
+// The boundaries between each frame are valid JSON objects. A JSON parsing error will terminate
+// the read.
+func NewJSONFramedReader(r io.ReadCloser) io.ReadCloser {
+ return &jsonFrameReader{
+ r: r,
+ decoder: json.NewDecoder(r),
+ }
+}
+
+// ReadFrame decodes the next JSON object in the stream, or returns an error. The returned
+// byte slice will be modified the next time ReadFrame is invoked and should not be altered.
+func (r *jsonFrameReader) Read(data []byte) (int, error) {
+ // Return whatever remaining data exists from an in progress frame
+ if n := len(r.remaining); n > 0 {
+ if n <= len(data) {
+ data = append(data[0:0], r.remaining...)
+ r.remaining = nil
+ return n, nil
+ }
+
+ n = len(data)
+ data = append(data[0:0], r.remaining[:n]...)
+ r.remaining = r.remaining[n:]
+ return n, io.ErrShortBuffer
+ }
+
+ // RawMessage#Unmarshal appends to data - we reset the slice down to 0 and will either see
+ // data written to data, or be larger than data and a different array.
+ n := len(data)
+ m := json.RawMessage(data[:0])
+ if err := r.decoder.Decode(&m); err != nil {
+ return 0, err
+ }
+
+ // If capacity of data is less than length of the message, decoder will allocate a new slice
+ // and set m to it, which means we need to copy the partial result back into data and preserve
+ // the remaining result for subsequent reads.
+ if len(m) > n {
+ data = append(data[0:0], m[:n]...)
+ r.remaining = m[n:]
+ return n, io.ErrShortBuffer
+ }
+ return len(m), nil
+}
+
+func (r *jsonFrameReader) Close() error {
+ return r.r.Close()
+}
diff --git a/metrics-server/vendor/k8s.io/apimachinery/pkg/util/intstr/generated.pb.go b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/intstr/generated.pb.go
new file mode 100644
index 0000000..5c2ac4f
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/intstr/generated.pb.go
@@ -0,0 +1,381 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by protoc-gen-gogo.
+// source: k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/util/intstr/generated.proto
+// DO NOT EDIT!
+
+/*
+ Package intstr is a generated protocol buffer package.
+
+ It is generated from these files:
+ k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/util/intstr/generated.proto
+
+ It has these top-level messages:
+ IntOrString
+*/
+package intstr
+
+import proto "github.com/gogo/protobuf/proto"
+import fmt "fmt"
+import math "math"
+
+import io "io"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
+
+func (m *IntOrString) Reset() { *m = IntOrString{} }
+func (*IntOrString) ProtoMessage() {}
+func (*IntOrString) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{0} }
+
+func init() {
+ proto.RegisterType((*IntOrString)(nil), "k8s.io.apimachinery.pkg.util.intstr.IntOrString")
+}
+func (m *IntOrString) Marshal() (dAtA []byte, err error) {
+ size := m.Size()
+ dAtA = make([]byte, size)
+ n, err := m.MarshalTo(dAtA)
+ if err != nil {
+ return nil, err
+ }
+ return dAtA[:n], nil
+}
+
+func (m *IntOrString) MarshalTo(dAtA []byte) (int, error) {
+ var i int
+ _ = i
+ var l int
+ _ = l
+ dAtA[i] = 0x8
+ i++
+ i = encodeVarintGenerated(dAtA, i, uint64(m.Type))
+ dAtA[i] = 0x10
+ i++
+ i = encodeVarintGenerated(dAtA, i, uint64(m.IntVal))
+ dAtA[i] = 0x1a
+ i++
+ i = encodeVarintGenerated(dAtA, i, uint64(len(m.StrVal)))
+ i += copy(dAtA[i:], m.StrVal)
+ return i, nil
+}
+
+func encodeFixed64Generated(dAtA []byte, offset int, v uint64) int {
+ dAtA[offset] = uint8(v)
+ dAtA[offset+1] = uint8(v >> 8)
+ dAtA[offset+2] = uint8(v >> 16)
+ dAtA[offset+3] = uint8(v >> 24)
+ dAtA[offset+4] = uint8(v >> 32)
+ dAtA[offset+5] = uint8(v >> 40)
+ dAtA[offset+6] = uint8(v >> 48)
+ dAtA[offset+7] = uint8(v >> 56)
+ return offset + 8
+}
+func encodeFixed32Generated(dAtA []byte, offset int, v uint32) int {
+ dAtA[offset] = uint8(v)
+ dAtA[offset+1] = uint8(v >> 8)
+ dAtA[offset+2] = uint8(v >> 16)
+ dAtA[offset+3] = uint8(v >> 24)
+ return offset + 4
+}
+func encodeVarintGenerated(dAtA []byte, offset int, v uint64) int {
+ for v >= 1<<7 {
+ dAtA[offset] = uint8(v&0x7f | 0x80)
+ v >>= 7
+ offset++
+ }
+ dAtA[offset] = uint8(v)
+ return offset + 1
+}
+func (m *IntOrString) Size() (n int) {
+ var l int
+ _ = l
+ n += 1 + sovGenerated(uint64(m.Type))
+ n += 1 + sovGenerated(uint64(m.IntVal))
+ l = len(m.StrVal)
+ n += 1 + l + sovGenerated(uint64(l))
+ return n
+}
+
+func sovGenerated(x uint64) (n int) {
+ for {
+ n++
+ x >>= 7
+ if x == 0 {
+ break
+ }
+ }
+ return n
+}
+func sozGenerated(x uint64) (n int) {
+ return sovGenerated(uint64((x << 1) ^ uint64((int64(x) >> 63))))
+}
+func (m *IntOrString) Unmarshal(dAtA []byte) error {
+ l := len(dAtA)
+ iNdEx := 0
+ for iNdEx < l {
+ preIndex := iNdEx
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ if wireType == 4 {
+ return fmt.Errorf("proto: IntOrString: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: IntOrString: illegal tag %d (wire type %d)", fieldNum, wire)
+ }
+ switch fieldNum {
+ case 1:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field Type", wireType)
+ }
+ m.Type = 0
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ m.Type |= (Type(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 2:
+ if wireType != 0 {
+ return fmt.Errorf("proto: wrong wireType = %d for field IntVal", wireType)
+ }
+ m.IntVal = 0
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ m.IntVal |= (int32(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ case 3:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field StrVal", wireType)
+ }
+ var stringLen uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ stringLen |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ intStringLen := int(stringLen)
+ if intStringLen < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ postIndex := iNdEx + intStringLen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ m.StrVal = string(dAtA[iNdEx:postIndex])
+ iNdEx = postIndex
+ default:
+ iNdEx = preIndex
+ skippy, err := skipGenerated(dAtA[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if skippy < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ if iNdEx > l {
+ return io.ErrUnexpectedEOF
+ }
+ return nil
+}
+func skipGenerated(dAtA []byte) (n int, err error) {
+ l := len(dAtA)
+ iNdEx := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return 0, ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ wireType := int(wire & 0x7)
+ switch wireType {
+ case 0:
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return 0, ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ iNdEx++
+ if dAtA[iNdEx-1] < 0x80 {
+ break
+ }
+ }
+ return iNdEx, nil
+ case 1:
+ iNdEx += 8
+ return iNdEx, nil
+ case 2:
+ var length int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return 0, ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ length |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ iNdEx += length
+ if length < 0 {
+ return 0, ErrInvalidLengthGenerated
+ }
+ return iNdEx, nil
+ case 3:
+ for {
+ var innerWire uint64
+ var start int = iNdEx
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return 0, ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := dAtA[iNdEx]
+ iNdEx++
+ innerWire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ innerWireType := int(innerWire & 0x7)
+ if innerWireType == 4 {
+ break
+ }
+ next, err := skipGenerated(dAtA[start:])
+ if err != nil {
+ return 0, err
+ }
+ iNdEx = start + next
+ }
+ return iNdEx, nil
+ case 4:
+ return iNdEx, nil
+ case 5:
+ iNdEx += 4
+ return iNdEx, nil
+ default:
+ return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
+ }
+ }
+ panic("unreachable")
+}
+
+var (
+ ErrInvalidLengthGenerated = fmt.Errorf("proto: negative length found during unmarshaling")
+ ErrIntOverflowGenerated = fmt.Errorf("proto: integer overflow")
+)
+
+func init() {
+ proto.RegisterFile("k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/util/intstr/generated.proto", fileDescriptorGenerated)
+}
+
+var fileDescriptorGenerated = []byte{
+ // 292 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x4c, 0x8f, 0x31, 0x4b, 0x33, 0x31,
+ 0x1c, 0xc6, 0x93, 0xb7, 0x7d, 0x8b, 0x9e, 0xe0, 0x50, 0x1c, 0x8a, 0x43, 0x7a, 0x28, 0xc8, 0x0d,
+ 0x9a, 0xac, 0xe2, 0xd8, 0xad, 0x20, 0x08, 0x57, 0x71, 0x70, 0xbb, 0x6b, 0x63, 0x1a, 0xae, 0x4d,
+ 0x42, 0xee, 0x7f, 0xc2, 0x6d, 0xfd, 0x08, 0xba, 0x39, 0xfa, 0x71, 0x6e, 0xec, 0xd8, 0x41, 0x8a,
+ 0x17, 0xbf, 0x85, 0x93, 0x5c, 0xee, 0x40, 0xa7, 0xe4, 0x79, 0x9e, 0xdf, 0x2f, 0x90, 0xe0, 0x36,
+ 0xbb, 0xce, 0xa9, 0xd4, 0x2c, 0x2b, 0x52, 0x6e, 0x15, 0x07, 0x9e, 0xb3, 0x67, 0xae, 0x16, 0xda,
+ 0xb2, 0x6e, 0x48, 0x8c, 0x5c, 0x27, 0xf3, 0xa5, 0x54, 0xdc, 0x96, 0xcc, 0x64, 0x82, 0x15, 0x20,
+ 0x57, 0x4c, 0x2a, 0xc8, 0xc1, 0x32, 0xc1, 0x15, 0xb7, 0x09, 0xf0, 0x05, 0x35, 0x56, 0x83, 0x1e,
+ 0x9e, 0xb7, 0x12, 0xfd, 0x2b, 0x51, 0x93, 0x09, 0xda, 0x48, 0xb4, 0x95, 0x4e, 0xaf, 0x84, 0x84,
+ 0x65, 0x91, 0xd2, 0xb9, 0x5e, 0x33, 0xa1, 0x85, 0x66, 0xde, 0x4d, 0x8b, 0x27, 0x9f, 0x7c, 0xf0,
+ 0xb7, 0xf6, 0xcd, 0xb3, 0x57, 0x1c, 0x1c, 0x4d, 0x15, 0xdc, 0xd9, 0x19, 0x58, 0xa9, 0xc4, 0x30,
+ 0x0a, 0xfa, 0x50, 0x1a, 0x3e, 0xc2, 0x21, 0x8e, 0x7a, 0x93, 0x93, 0x6a, 0x3f, 0x46, 0x6e, 0x3f,
+ 0xee, 0xdf, 0x97, 0x86, 0x7f, 0x77, 0x67, 0xec, 0x89, 0xe1, 0x45, 0x30, 0x90, 0x0a, 0x1e, 0x92,
+ 0xd5, 0xe8, 0x5f, 0x88, 0xa3, 0xff, 0x93, 0xe3, 0x8e, 0x1d, 0x4c, 0x7d, 0x1b, 0x77, 0x6b, 0xc3,
+ 0xe5, 0x60, 0x1b, 0xae, 0x17, 0xe2, 0xe8, 0xf0, 0x97, 0x9b, 0xf9, 0x36, 0xee, 0xd6, 0x9b, 0x83,
+ 0xb7, 0xf7, 0x31, 0xda, 0x7c, 0x84, 0x68, 0x72, 0x59, 0xd5, 0x04, 0x6d, 0x6b, 0x82, 0x76, 0x35,
+ 0x41, 0x1b, 0x47, 0x70, 0xe5, 0x08, 0xde, 0x3a, 0x82, 0x77, 0x8e, 0xe0, 0x4f, 0x47, 0xf0, 0xcb,
+ 0x17, 0x41, 0x8f, 0x83, 0xf6, 0xc3, 0x3f, 0x01, 0x00, 0x00, 0xff, 0xff, 0x52, 0xa0, 0xb5, 0xc9,
+ 0x64, 0x01, 0x00, 0x00,
+}
diff --git a/metrics-server/vendor/k8s.io/apimachinery/pkg/util/intstr/generated.proto b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/intstr/generated.proto
new file mode 100644
index 0000000..1c3ec73
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/intstr/generated.proto
@@ -0,0 +1,43 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+
+// This file was autogenerated by go-to-protobuf. Do not edit it manually!
+
+syntax = 'proto2';
+
+package k8s.io.apimachinery.pkg.util.intstr;
+
+// Package-wide variables from generator "generated".
+option go_package = "intstr";
+
+// IntOrString is a type that can hold an int32 or a string. When used in
+// JSON or YAML marshalling and unmarshalling, it produces or consumes the
+// inner type. This allows you to have, for example, a JSON field that can
+// accept a name or number.
+// TODO: Rename to Int32OrString
+//
+// +protobuf=true
+// +protobuf.options.(gogoproto.goproto_stringer)=false
+// +k8s:openapi-gen=true
+message IntOrString {
+ optional int64 type = 1;
+
+ optional int32 intVal = 2;
+
+ optional string strVal = 3;
+}
+
diff --git a/metrics-server/vendor/k8s.io/apimachinery/pkg/util/intstr/intstr.go b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/intstr/intstr.go
new file mode 100644
index 0000000..231498c
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/intstr/intstr.go
@@ -0,0 +1,173 @@
+/*
+Copyright 2014 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package intstr
+
+import (
+ "encoding/json"
+ "fmt"
+ "math"
+ "runtime/debug"
+ "strconv"
+ "strings"
+
+ "github.com/golang/glog"
+ "github.com/google/gofuzz"
+)
+
+// IntOrString is a type that can hold an int32 or a string. When used in
+// JSON or YAML marshalling and unmarshalling, it produces or consumes the
+// inner type. This allows you to have, for example, a JSON field that can
+// accept a name or number.
+// TODO: Rename to Int32OrString
+//
+// +protobuf=true
+// +protobuf.options.(gogoproto.goproto_stringer)=false
+// +k8s:openapi-gen=true
+type IntOrString struct {
+ Type Type `protobuf:"varint,1,opt,name=type,casttype=Type"`
+ IntVal int32 `protobuf:"varint,2,opt,name=intVal"`
+ StrVal string `protobuf:"bytes,3,opt,name=strVal"`
+}
+
+// Type represents the stored type of IntOrString.
+type Type int
+
+const (
+ Int Type = iota // The IntOrString holds an int.
+ String // The IntOrString holds a string.
+)
+
+// FromInt creates an IntOrString object with an int32 value. It is
+// your responsibility not to call this method with a value greater
+// than int32.
+// TODO: convert to (val int32)
+func FromInt(val int) IntOrString {
+ if val > math.MaxInt32 || val < math.MinInt32 {
+ glog.Errorf("value: %d overflows int32\n%s\n", val, debug.Stack())
+ }
+ return IntOrString{Type: Int, IntVal: int32(val)}
+}
+
+// FromString creates an IntOrString object with a string value.
+func FromString(val string) IntOrString {
+ return IntOrString{Type: String, StrVal: val}
+}
+
+// Parse the given string and try to convert it to an integer before
+// setting it as a string value.
+func Parse(val string) IntOrString {
+ i, err := strconv.Atoi(val)
+ if err != nil {
+ return FromString(val)
+ }
+ return FromInt(i)
+}
+
+// UnmarshalJSON implements the json.Unmarshaller interface.
+func (intstr *IntOrString) UnmarshalJSON(value []byte) error {
+ if value[0] == '"' {
+ intstr.Type = String
+ return json.Unmarshal(value, &intstr.StrVal)
+ }
+ intstr.Type = Int
+ return json.Unmarshal(value, &intstr.IntVal)
+}
+
+// String returns the string value, or the Itoa of the int value.
+func (intstr *IntOrString) String() string {
+ if intstr.Type == String {
+ return intstr.StrVal
+ }
+ return strconv.Itoa(intstr.IntValue())
+}
+
+// IntValue returns the IntVal if type Int, or if
+// it is a String, will attempt a conversion to int.
+func (intstr *IntOrString) IntValue() int {
+ if intstr.Type == String {
+ i, _ := strconv.Atoi(intstr.StrVal)
+ return i
+ }
+ return int(intstr.IntVal)
+}
+
+// MarshalJSON implements the json.Marshaller interface.
+func (intstr IntOrString) MarshalJSON() ([]byte, error) {
+ switch intstr.Type {
+ case Int:
+ return json.Marshal(intstr.IntVal)
+ case String:
+ return json.Marshal(intstr.StrVal)
+ default:
+ return []byte{}, fmt.Errorf("impossible IntOrString.Type")
+ }
+}
+
+// OpenAPISchemaType is used by the kube-openapi generator when constructing
+// the OpenAPI spec of this type.
+//
+// See: https://github.com/kubernetes/kube-openapi/tree/master/pkg/generators
+func (_ IntOrString) OpenAPISchemaType() []string { return []string{"string"} }
+
+// OpenAPISchemaFormat is used by the kube-openapi generator when constructing
+// the OpenAPI spec of this type.
+func (_ IntOrString) OpenAPISchemaFormat() string { return "int-or-string" }
+
+func (intstr *IntOrString) Fuzz(c fuzz.Continue) {
+ if intstr == nil {
+ return
+ }
+ if c.RandBool() {
+ intstr.Type = Int
+ c.Fuzz(&intstr.IntVal)
+ intstr.StrVal = ""
+ } else {
+ intstr.Type = String
+ intstr.IntVal = 0
+ c.Fuzz(&intstr.StrVal)
+ }
+}
+
+func GetValueFromIntOrPercent(intOrPercent *IntOrString, total int, roundUp bool) (int, error) {
+ value, isPercent, err := getIntOrPercentValue(intOrPercent)
+ if err != nil {
+ return 0, fmt.Errorf("invalid value for IntOrString: %v", err)
+ }
+ if isPercent {
+ if roundUp {
+ value = int(math.Ceil(float64(value) * (float64(total)) / 100))
+ } else {
+ value = int(math.Floor(float64(value) * (float64(total)) / 100))
+ }
+ }
+ return value, nil
+}
+
+func getIntOrPercentValue(intOrStr *IntOrString) (int, bool, error) {
+ switch intOrStr.Type {
+ case Int:
+ return intOrStr.IntValue(), false, nil
+ case String:
+ s := strings.Replace(intOrStr.StrVal, "%", "", -1)
+ v, err := strconv.Atoi(s)
+ if err != nil {
+ return 0, false, fmt.Errorf("invalid value %q: %v", intOrStr.StrVal, err)
+ }
+ return int(v), true, nil
+ }
+ return 0, false, fmt.Errorf("invalid type: neither int nor percentage")
+}
diff --git a/metrics-server/vendor/k8s.io/apimachinery/pkg/util/json/json.go b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/json/json.go
new file mode 100644
index 0000000..10c8cb8
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/json/json.go
@@ -0,0 +1,119 @@
+/*
+Copyright 2015 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package json
+
+import (
+ "bytes"
+ "encoding/json"
+ "io"
+)
+
+// NewEncoder delegates to json.NewEncoder
+// It is only here so this package can be a drop-in for common encoding/json uses
+func NewEncoder(w io.Writer) *json.Encoder {
+ return json.NewEncoder(w)
+}
+
+// Marshal delegates to json.Marshal
+// It is only here so this package can be a drop-in for common encoding/json uses
+func Marshal(v interface{}) ([]byte, error) {
+ return json.Marshal(v)
+}
+
+// Unmarshal unmarshals the given data
+// If v is a *map[string]interface{}, numbers are converted to int64 or float64
+func Unmarshal(data []byte, v interface{}) error {
+ switch v := v.(type) {
+ case *map[string]interface{}:
+ // Build a decoder from the given data
+ decoder := json.NewDecoder(bytes.NewBuffer(data))
+ // Preserve numbers, rather than casting to float64 automatically
+ decoder.UseNumber()
+ // Run the decode
+ if err := decoder.Decode(v); err != nil {
+ return err
+ }
+ // If the decode succeeds, post-process the map to convert json.Number objects to int64 or float64
+ return convertMapNumbers(*v)
+
+ case *[]interface{}:
+ // Build a decoder from the given data
+ decoder := json.NewDecoder(bytes.NewBuffer(data))
+ // Preserve numbers, rather than casting to float64 automatically
+ decoder.UseNumber()
+ // Run the decode
+ if err := decoder.Decode(v); err != nil {
+ return err
+ }
+ // If the decode succeeds, post-process the map to convert json.Number objects to int64 or float64
+ return convertSliceNumbers(*v)
+
+ default:
+ return json.Unmarshal(data, v)
+ }
+}
+
+// convertMapNumbers traverses the map, converting any json.Number values to int64 or float64.
+// values which are map[string]interface{} or []interface{} are recursively visited
+func convertMapNumbers(m map[string]interface{}) error {
+ var err error
+ for k, v := range m {
+ switch v := v.(type) {
+ case json.Number:
+ m[k], err = convertNumber(v)
+ case map[string]interface{}:
+ err = convertMapNumbers(v)
+ case []interface{}:
+ err = convertSliceNumbers(v)
+ }
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// convertSliceNumbers traverses the slice, converting any json.Number values to int64 or float64.
+// values which are map[string]interface{} or []interface{} are recursively visited
+func convertSliceNumbers(s []interface{}) error {
+ var err error
+ for i, v := range s {
+ switch v := v.(type) {
+ case json.Number:
+ s[i], err = convertNumber(v)
+ case map[string]interface{}:
+ err = convertMapNumbers(v)
+ case []interface{}:
+ err = convertSliceNumbers(v)
+ }
+ if err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// convertNumber converts a json.Number to an int64 or float64, or returns an error
+func convertNumber(n json.Number) (interface{}, error) {
+ // Attempt to convert to an int64 first
+ if i, err := n.Int64(); err == nil {
+ return i, nil
+ }
+ // Return a float64 (default json.Decode() behavior)
+ // An overflow will return an error
+ return n.Float64()
+}
diff --git a/metrics-server/vendor/k8s.io/apimachinery/pkg/util/mergepatch/OWNERS b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/mergepatch/OWNERS
new file mode 100644
index 0000000..8e8d9fc
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/mergepatch/OWNERS
@@ -0,0 +1,5 @@
+approvers:
+- pwittrock
+reviewers:
+- mengqiy
+- apelisse
diff --git a/metrics-server/vendor/k8s.io/apimachinery/pkg/util/mergepatch/errors.go b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/mergepatch/errors.go
new file mode 100644
index 0000000..16501d5
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/mergepatch/errors.go
@@ -0,0 +1,102 @@
+/*
+Copyright 2017 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package mergepatch
+
+import (
+ "errors"
+ "fmt"
+ "reflect"
+)
+
+var (
+ ErrBadJSONDoc = errors.New("invalid JSON document")
+ ErrNoListOfLists = errors.New("lists of lists are not supported")
+ ErrBadPatchFormatForPrimitiveList = errors.New("invalid patch format of primitive list")
+ ErrBadPatchFormatForRetainKeys = errors.New("invalid patch format of retainKeys")
+ ErrBadPatchFormatForSetElementOrderList = errors.New("invalid patch format of setElementOrder list")
+ ErrPatchContentNotMatchRetainKeys = errors.New("patch content doesn't match retainKeys list")
+ ErrUnsupportedStrategicMergePatchFormat = errors.New("strategic merge patch format is not supported")
+)
+
+func ErrNoMergeKey(m map[string]interface{}, k string) error {
+ return fmt.Errorf("map: %v does not contain declared merge key: %s", m, k)
+}
+
+func ErrBadArgType(expected, actual interface{}) error {
+ return fmt.Errorf("expected a %s, but received a %s",
+ reflect.TypeOf(expected),
+ reflect.TypeOf(actual))
+}
+
+func ErrBadArgKind(expected, actual interface{}) error {
+ var expectedKindString, actualKindString string
+ if expected == nil {
+ expectedKindString = "nil"
+ } else {
+ expectedKindString = reflect.TypeOf(expected).Kind().String()
+ }
+ if actual == nil {
+ actualKindString = "nil"
+ } else {
+ actualKindString = reflect.TypeOf(actual).Kind().String()
+ }
+ return fmt.Errorf("expected a %s, but received a %s", expectedKindString, actualKindString)
+}
+
+func ErrBadPatchType(t interface{}, m map[string]interface{}) error {
+ return fmt.Errorf("unknown patch type: %s in map: %v", t, m)
+}
+
+// IsPreconditionFailed returns true if the provided error indicates
+// a precondition failed.
+func IsPreconditionFailed(err error) bool {
+ _, ok := err.(ErrPreconditionFailed)
+ return ok
+}
+
+type ErrPreconditionFailed struct {
+ message string
+}
+
+func NewErrPreconditionFailed(target map[string]interface{}) ErrPreconditionFailed {
+ s := fmt.Sprintf("precondition failed for: %v", target)
+ return ErrPreconditionFailed{s}
+}
+
+func (err ErrPreconditionFailed) Error() string {
+ return err.message
+}
+
+type ErrConflict struct {
+ message string
+}
+
+func NewErrConflict(patch, current string) ErrConflict {
+ s := fmt.Sprintf("patch:\n%s\nconflicts with changes made from original to current:\n%s\n", patch, current)
+ return ErrConflict{s}
+}
+
+func (err ErrConflict) Error() string {
+ return err.message
+}
+
+// IsConflict returns true if the provided error indicates
+// a conflict between the patch and the current configuration.
+func IsConflict(err error) bool {
+ _, ok := err.(ErrConflict)
+ return ok
+}
diff --git a/metrics-server/vendor/k8s.io/apimachinery/pkg/util/mergepatch/util.go b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/mergepatch/util.go
new file mode 100644
index 0000000..9261290
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/mergepatch/util.go
@@ -0,0 +1,133 @@
+/*
+Copyright 2017 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package mergepatch
+
+import (
+ "fmt"
+ "reflect"
+
+ "github.com/davecgh/go-spew/spew"
+ "github.com/ghodss/yaml"
+)
+
+// PreconditionFunc asserts that an incompatible change is not present within a patch.
+type PreconditionFunc func(interface{}) bool
+
+// RequireKeyUnchanged returns a precondition function that fails if the provided key
+// is present in the patch (indicating that its value has changed).
+func RequireKeyUnchanged(key string) PreconditionFunc {
+ return func(patch interface{}) bool {
+ patchMap, ok := patch.(map[string]interface{})
+ if !ok {
+ return true
+ }
+
+ // The presence of key means that its value has been changed, so the test fails.
+ _, ok = patchMap[key]
+ return !ok
+ }
+}
+
+// RequireMetadataKeyUnchanged creates a precondition function that fails
+// if the metadata.key is present in the patch (indicating its value
+// has changed).
+func RequireMetadataKeyUnchanged(key string) PreconditionFunc {
+ return func(patch interface{}) bool {
+ patchMap, ok := patch.(map[string]interface{})
+ if !ok {
+ return true
+ }
+ patchMap1, ok := patchMap["metadata"]
+ if !ok {
+ return true
+ }
+ patchMap2, ok := patchMap1.(map[string]interface{})
+ if !ok {
+ return true
+ }
+ _, ok = patchMap2[key]
+ return !ok
+ }
+}
+
+func ToYAMLOrError(v interface{}) string {
+ y, err := toYAML(v)
+ if err != nil {
+ return err.Error()
+ }
+
+ return y
+}
+
+func toYAML(v interface{}) (string, error) {
+ y, err := yaml.Marshal(v)
+ if err != nil {
+ return "", fmt.Errorf("yaml marshal failed:%v\n%v\n", err, spew.Sdump(v))
+ }
+
+ return string(y), nil
+}
+
+// HasConflicts returns true if the left and right JSON interface objects overlap with
+// different values in any key. All keys are required to be strings. Since patches of the
+// same Type have congruent keys, this is valid for multiple patch types. This method
+// supports JSON merge patch semantics.
+//
+// NOTE: Numbers with different types (e.g. int(0) vs int64(0)) will be detected as conflicts.
+// Make sure the unmarshaling of left and right are consistent (e.g. use the same library).
+func HasConflicts(left, right interface{}) (bool, error) {
+ switch typedLeft := left.(type) {
+ case map[string]interface{}:
+ switch typedRight := right.(type) {
+ case map[string]interface{}:
+ for key, leftValue := range typedLeft {
+ rightValue, ok := typedRight[key]
+ if !ok {
+ continue
+ }
+ if conflict, err := HasConflicts(leftValue, rightValue); err != nil || conflict {
+ return conflict, err
+ }
+ }
+
+ return false, nil
+ default:
+ return true, nil
+ }
+ case []interface{}:
+ switch typedRight := right.(type) {
+ case []interface{}:
+ if len(typedLeft) != len(typedRight) {
+ return true, nil
+ }
+
+ for i := range typedLeft {
+ if conflict, err := HasConflicts(typedLeft[i], typedRight[i]); err != nil || conflict {
+ return conflict, err
+ }
+ }
+
+ return false, nil
+ default:
+ return true, nil
+ }
+ case string, float64, bool, int, int64, nil:
+ return !reflect.DeepEqual(left, right), nil
+ default:
+ return true, fmt.Errorf("unknown type: %v", reflect.TypeOf(left))
+ }
+}
diff --git a/metrics-server/vendor/k8s.io/apimachinery/pkg/util/net/http.go b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/net/http.go
new file mode 100644
index 0000000..7ea2df2
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/net/http.go
@@ -0,0 +1,426 @@
+/*
+Copyright 2016 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package net
+
+import (
+ "bufio"
+ "bytes"
+ "context"
+ "crypto/tls"
+ "fmt"
+ "io"
+ "net"
+ "net/http"
+ "net/url"
+ "os"
+ "path"
+ "strconv"
+ "strings"
+
+ "github.com/golang/glog"
+ "golang.org/x/net/http2"
+)
+
+// JoinPreservingTrailingSlash does a path.Join of the specified elements,
+// preserving any trailing slash on the last non-empty segment
+func JoinPreservingTrailingSlash(elem ...string) string {
+ // do the basic path join
+ result := path.Join(elem...)
+
+ // find the last non-empty segment
+ for i := len(elem) - 1; i >= 0; i-- {
+ if len(elem[i]) > 0 {
+ // if the last segment ended in a slash, ensure our result does as well
+ if strings.HasSuffix(elem[i], "/") && !strings.HasSuffix(result, "/") {
+ result += "/"
+ }
+ break
+ }
+ }
+
+ return result
+}
+
+// IsProbableEOF returns true if the given error resembles a connection termination
+// scenario that would justify assuming that the watch is empty.
+// These errors are what the Go http stack returns back to us which are general
+// connection closure errors (strongly correlated) and callers that need to
+// differentiate probable errors in connection behavior between normal "this is
+// disconnected" should use the method.
+func IsProbableEOF(err error) bool {
+ if err == nil {
+ return false
+ }
+ if uerr, ok := err.(*url.Error); ok {
+ err = uerr.Err
+ }
+ switch {
+ case err == io.EOF:
+ return true
+ case err.Error() == "http: can't write HTTP request on broken connection":
+ return true
+ case strings.Contains(err.Error(), "connection reset by peer"):
+ return true
+ case strings.Contains(strings.ToLower(err.Error()), "use of closed network connection"):
+ return true
+ }
+ return false
+}
+
+var defaultTransport = http.DefaultTransport.(*http.Transport)
+
+// SetOldTransportDefaults applies the defaults from http.DefaultTransport
+// for the Proxy, Dial, and TLSHandshakeTimeout fields if unset
+func SetOldTransportDefaults(t *http.Transport) *http.Transport {
+ if t.Proxy == nil || isDefault(t.Proxy) {
+ // http.ProxyFromEnvironment doesn't respect CIDRs and that makes it impossible to exclude things like pod and service IPs from proxy settings
+ // ProxierWithNoProxyCIDR allows CIDR rules in NO_PROXY
+ t.Proxy = NewProxierWithNoProxyCIDR(http.ProxyFromEnvironment)
+ }
+ if t.DialContext == nil {
+ t.DialContext = defaultTransport.DialContext
+ }
+ if t.TLSHandshakeTimeout == 0 {
+ t.TLSHandshakeTimeout = defaultTransport.TLSHandshakeTimeout
+ }
+ return t
+}
+
+// SetTransportDefaults applies the defaults from http.DefaultTransport
+// for the Proxy, Dial, and TLSHandshakeTimeout fields if unset
+func SetTransportDefaults(t *http.Transport) *http.Transport {
+ t = SetOldTransportDefaults(t)
+ // Allow clients to disable http2 if needed.
+ if s := os.Getenv("DISABLE_HTTP2"); len(s) > 0 {
+ glog.Infof("HTTP2 has been explicitly disabled")
+ } else {
+ if err := http2.ConfigureTransport(t); err != nil {
+ glog.Warningf("Transport failed http2 configuration: %v", err)
+ }
+ }
+ return t
+}
+
+type RoundTripperWrapper interface {
+ http.RoundTripper
+ WrappedRoundTripper() http.RoundTripper
+}
+
+type DialFunc func(ctx context.Context, net, addr string) (net.Conn, error)
+
+func DialerFor(transport http.RoundTripper) (DialFunc, error) {
+ if transport == nil {
+ return nil, nil
+ }
+
+ switch transport := transport.(type) {
+ case *http.Transport:
+ return transport.DialContext, nil
+ case RoundTripperWrapper:
+ return DialerFor(transport.WrappedRoundTripper())
+ default:
+ return nil, fmt.Errorf("unknown transport type: %T", transport)
+ }
+}
+
+type TLSClientConfigHolder interface {
+ TLSClientConfig() *tls.Config
+}
+
+func TLSClientConfig(transport http.RoundTripper) (*tls.Config, error) {
+ if transport == nil {
+ return nil, nil
+ }
+
+ switch transport := transport.(type) {
+ case *http.Transport:
+ return transport.TLSClientConfig, nil
+ case TLSClientConfigHolder:
+ return transport.TLSClientConfig(), nil
+ case RoundTripperWrapper:
+ return TLSClientConfig(transport.WrappedRoundTripper())
+ default:
+ return nil, fmt.Errorf("unknown transport type: %T", transport)
+ }
+}
+
+func FormatURL(scheme string, host string, port int, path string) *url.URL {
+ return &url.URL{
+ Scheme: scheme,
+ Host: net.JoinHostPort(host, strconv.Itoa(port)),
+ Path: path,
+ }
+}
+
+func GetHTTPClient(req *http.Request) string {
+ if userAgent, ok := req.Header["User-Agent"]; ok {
+ if len(userAgent) > 0 {
+ return userAgent[0]
+ }
+ }
+ return "unknown"
+}
+
+// SourceIPs splits the comma separated X-Forwarded-For header or returns the X-Real-Ip header or req.RemoteAddr,
+// in that order, ignoring invalid IPs. It returns nil if all of these are empty or invalid.
+func SourceIPs(req *http.Request) []net.IP {
+ hdr := req.Header
+ // First check the X-Forwarded-For header for requests via proxy.
+ hdrForwardedFor := hdr.Get("X-Forwarded-For")
+ forwardedForIPs := []net.IP{}
+ if hdrForwardedFor != "" {
+ // X-Forwarded-For can be a csv of IPs in case of multiple proxies.
+ // Use the first valid one.
+ parts := strings.Split(hdrForwardedFor, ",")
+ for _, part := range parts {
+ ip := net.ParseIP(strings.TrimSpace(part))
+ if ip != nil {
+ forwardedForIPs = append(forwardedForIPs, ip)
+ }
+ }
+ }
+ if len(forwardedForIPs) > 0 {
+ return forwardedForIPs
+ }
+
+ // Try the X-Real-Ip header.
+ hdrRealIp := hdr.Get("X-Real-Ip")
+ if hdrRealIp != "" {
+ ip := net.ParseIP(hdrRealIp)
+ if ip != nil {
+ return []net.IP{ip}
+ }
+ }
+
+ // Fallback to Remote Address in request, which will give the correct client IP when there is no proxy.
+ // Remote Address in Go's HTTP server is in the form host:port so we need to split that first.
+ host, _, err := net.SplitHostPort(req.RemoteAddr)
+ if err == nil {
+ if remoteIP := net.ParseIP(host); remoteIP != nil {
+ return []net.IP{remoteIP}
+ }
+ }
+
+ // Fallback if Remote Address was just IP.
+ if remoteIP := net.ParseIP(req.RemoteAddr); remoteIP != nil {
+ return []net.IP{remoteIP}
+ }
+
+ return nil
+}
+
+// Extracts and returns the clients IP from the given request.
+// Looks at X-Forwarded-For header, X-Real-Ip header and request.RemoteAddr in that order.
+// Returns nil if none of them are set or is set to an invalid value.
+func GetClientIP(req *http.Request) net.IP {
+ ips := SourceIPs(req)
+ if len(ips) == 0 {
+ return nil
+ }
+ return ips[0]
+}
+
+// Prepares the X-Forwarded-For header for another forwarding hop by appending the previous sender's
+// IP address to the X-Forwarded-For chain.
+func AppendForwardedForHeader(req *http.Request) {
+ // Copied from net/http/httputil/reverseproxy.go:
+ if clientIP, _, err := net.SplitHostPort(req.RemoteAddr); err == nil {
+ // If we aren't the first proxy retain prior
+ // X-Forwarded-For information as a comma+space
+ // separated list and fold multiple headers into one.
+ if prior, ok := req.Header["X-Forwarded-For"]; ok {
+ clientIP = strings.Join(prior, ", ") + ", " + clientIP
+ }
+ req.Header.Set("X-Forwarded-For", clientIP)
+ }
+}
+
+var defaultProxyFuncPointer = fmt.Sprintf("%p", http.ProxyFromEnvironment)
+
+// isDefault checks to see if the transportProxierFunc is pointing to the default one
+func isDefault(transportProxier func(*http.Request) (*url.URL, error)) bool {
+ transportProxierPointer := fmt.Sprintf("%p", transportProxier)
+ return transportProxierPointer == defaultProxyFuncPointer
+}
+
+// NewProxierWithNoProxyCIDR constructs a Proxier function that respects CIDRs in NO_PROXY and delegates if
+// no matching CIDRs are found
+func NewProxierWithNoProxyCIDR(delegate func(req *http.Request) (*url.URL, error)) func(req *http.Request) (*url.URL, error) {
+ // we wrap the default method, so we only need to perform our check if the NO_PROXY (or no_proxy) envvar has a CIDR in it
+ noProxyEnv := os.Getenv("NO_PROXY")
+ if noProxyEnv == "" {
+ noProxyEnv = os.Getenv("no_proxy")
+ }
+ noProxyRules := strings.Split(noProxyEnv, ",")
+
+ cidrs := []*net.IPNet{}
+ for _, noProxyRule := range noProxyRules {
+ _, cidr, _ := net.ParseCIDR(noProxyRule)
+ if cidr != nil {
+ cidrs = append(cidrs, cidr)
+ }
+ }
+
+ if len(cidrs) == 0 {
+ return delegate
+ }
+
+ return func(req *http.Request) (*url.URL, error) {
+ ip := net.ParseIP(req.URL.Hostname())
+ if ip == nil {
+ return delegate(req)
+ }
+
+ for _, cidr := range cidrs {
+ if cidr.Contains(ip) {
+ return nil, nil
+ }
+ }
+
+ return delegate(req)
+ }
+}
+
+// DialerFunc implements Dialer for the provided function.
+type DialerFunc func(req *http.Request) (net.Conn, error)
+
+func (fn DialerFunc) Dial(req *http.Request) (net.Conn, error) {
+ return fn(req)
+}
+
+// Dialer dials a host and writes a request to it.
+type Dialer interface {
+ // Dial connects to the host specified by req's URL, writes the request to the connection, and
+ // returns the opened net.Conn.
+ Dial(req *http.Request) (net.Conn, error)
+}
+
+// ConnectWithRedirects uses dialer to send req, following up to 10 redirects (relative to
+// originalLocation). It returns the opened net.Conn and the raw response bytes.
+func ConnectWithRedirects(originalMethod string, originalLocation *url.URL, header http.Header, originalBody io.Reader, dialer Dialer) (net.Conn, []byte, error) {
+ const (
+ maxRedirects = 10
+ maxResponseSize = 16384 // play it safe to allow the potential for lots of / large headers
+ )
+
+ var (
+ location = originalLocation
+ method = originalMethod
+ intermediateConn net.Conn
+ rawResponse = bytes.NewBuffer(make([]byte, 0, 256))
+ body = originalBody
+ )
+
+ defer func() {
+ if intermediateConn != nil {
+ intermediateConn.Close()
+ }
+ }()
+
+redirectLoop:
+ for redirects := 0; ; redirects++ {
+ if redirects > maxRedirects {
+ return nil, nil, fmt.Errorf("too many redirects (%d)", redirects)
+ }
+
+ req, err := http.NewRequest(method, location.String(), body)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ req.Header = header
+
+ intermediateConn, err = dialer.Dial(req)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ // Peek at the backend response.
+ rawResponse.Reset()
+ respReader := bufio.NewReader(io.TeeReader(
+ io.LimitReader(intermediateConn, maxResponseSize), // Don't read more than maxResponseSize bytes.
+ rawResponse)) // Save the raw response.
+ resp, err := http.ReadResponse(respReader, nil)
+ if err != nil {
+ // Unable to read the backend response; let the client handle it.
+ glog.Warningf("Error reading backend response: %v", err)
+ break redirectLoop
+ }
+
+ switch resp.StatusCode {
+ case http.StatusFound:
+ // Redirect, continue.
+ default:
+ // Don't redirect.
+ break redirectLoop
+ }
+
+ // Redirected requests switch to "GET" according to the HTTP spec:
+ // https://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3
+ method = "GET"
+ // don't send a body when following redirects
+ body = nil
+
+ resp.Body.Close() // not used
+
+ // Reset the connection.
+ intermediateConn.Close()
+ intermediateConn = nil
+
+ // Prepare to follow the redirect.
+ redirectStr := resp.Header.Get("Location")
+ if redirectStr == "" {
+ return nil, nil, fmt.Errorf("%d response missing Location header", resp.StatusCode)
+ }
+ // We have to parse relative to the current location, NOT originalLocation. For example,
+ // if we request http://foo.com/a and get back "http://bar.com/b", the result should be
+ // http://bar.com/b. If we then make that request and get back a redirect to "/c", the result
+ // should be http://bar.com/c, not http://foo.com/c.
+ location, err = location.Parse(redirectStr)
+ if err != nil {
+ return nil, nil, fmt.Errorf("malformed Location header: %v", err)
+ }
+ }
+
+ connToReturn := intermediateConn
+ intermediateConn = nil // Don't close the connection when we return it.
+ return connToReturn, rawResponse.Bytes(), nil
+}
+
+// CloneRequest creates a shallow copy of the request along with a deep copy of the Headers.
+func CloneRequest(req *http.Request) *http.Request {
+ r := new(http.Request)
+
+ // shallow clone
+ *r = *req
+
+ // deep copy headers
+ r.Header = CloneHeader(req.Header)
+
+ return r
+}
+
+// CloneHeader creates a deep copy of an http.Header.
+func CloneHeader(in http.Header) http.Header {
+ out := make(http.Header, len(in))
+ for key, values := range in {
+ newValues := make([]string, len(values))
+ copy(newValues, values)
+ out[key] = newValues
+ }
+ return out
+}
diff --git a/metrics-server/vendor/k8s.io/apimachinery/pkg/util/net/interface.go b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/net/interface.go
new file mode 100644
index 0000000..42816bd
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/net/interface.go
@@ -0,0 +1,392 @@
+/*
+Copyright 2016 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package net
+
+import (
+ "bufio"
+ "encoding/hex"
+ "fmt"
+ "io"
+ "net"
+ "os"
+
+ "strings"
+
+ "github.com/golang/glog"
+)
+
+type AddressFamily uint
+
+const (
+ familyIPv4 AddressFamily = 4
+ familyIPv6 AddressFamily = 6
+)
+
+const (
+ ipv4RouteFile = "/proc/net/route"
+ ipv6RouteFile = "/proc/net/ipv6_route"
+)
+
+type Route struct {
+ Interface string
+ Destination net.IP
+ Gateway net.IP
+ Family AddressFamily
+}
+
+type RouteFile struct {
+ name string
+ parse func(input io.Reader) ([]Route, error)
+}
+
+var (
+ v4File = RouteFile{name: ipv4RouteFile, parse: getIPv4DefaultRoutes}
+ v6File = RouteFile{name: ipv6RouteFile, parse: getIPv6DefaultRoutes}
+)
+
+func (rf RouteFile) extract() ([]Route, error) {
+ file, err := os.Open(rf.name)
+ if err != nil {
+ return nil, err
+ }
+ defer file.Close()
+ return rf.parse(file)
+}
+
+// getIPv4DefaultRoutes obtains the IPv4 routes, and filters out non-default routes.
+func getIPv4DefaultRoutes(input io.Reader) ([]Route, error) {
+ routes := []Route{}
+ scanner := bufio.NewReader(input)
+ for {
+ line, err := scanner.ReadString('\n')
+ if err == io.EOF {
+ break
+ }
+ //ignore the headers in the route info
+ if strings.HasPrefix(line, "Iface") {
+ continue
+ }
+ fields := strings.Fields(line)
+ // Interested in fields:
+ // 0 - interface name
+ // 1 - destination address
+ // 2 - gateway
+ dest, err := parseIP(fields[1], familyIPv4)
+ if err != nil {
+ return nil, err
+ }
+ gw, err := parseIP(fields[2], familyIPv4)
+ if err != nil {
+ return nil, err
+ }
+ if !dest.Equal(net.IPv4zero) {
+ continue
+ }
+ routes = append(routes, Route{
+ Interface: fields[0],
+ Destination: dest,
+ Gateway: gw,
+ Family: familyIPv4,
+ })
+ }
+ return routes, nil
+}
+
+func getIPv6DefaultRoutes(input io.Reader) ([]Route, error) {
+ routes := []Route{}
+ scanner := bufio.NewReader(input)
+ for {
+ line, err := scanner.ReadString('\n')
+ if err == io.EOF {
+ break
+ }
+ fields := strings.Fields(line)
+ // Interested in fields:
+ // 0 - destination address
+ // 4 - gateway
+ // 9 - interface name
+ dest, err := parseIP(fields[0], familyIPv6)
+ if err != nil {
+ return nil, err
+ }
+ gw, err := parseIP(fields[4], familyIPv6)
+ if err != nil {
+ return nil, err
+ }
+ if !dest.Equal(net.IPv6zero) {
+ continue
+ }
+ if gw.Equal(net.IPv6zero) {
+ continue // loopback
+ }
+ routes = append(routes, Route{
+ Interface: fields[9],
+ Destination: dest,
+ Gateway: gw,
+ Family: familyIPv6,
+ })
+ }
+ return routes, nil
+}
+
+// parseIP takes the hex IP address string from route file and converts it
+// to a net.IP address. For IPv4, the value must be converted to big endian.
+func parseIP(str string, family AddressFamily) (net.IP, error) {
+ if str == "" {
+ return nil, fmt.Errorf("input is nil")
+ }
+ bytes, err := hex.DecodeString(str)
+ if err != nil {
+ return nil, err
+ }
+ if family == familyIPv4 {
+ if len(bytes) != net.IPv4len {
+ return nil, fmt.Errorf("invalid IPv4 address in route")
+ }
+ return net.IP([]byte{bytes[3], bytes[2], bytes[1], bytes[0]}), nil
+ }
+ // Must be IPv6
+ if len(bytes) != net.IPv6len {
+ return nil, fmt.Errorf("invalid IPv6 address in route")
+ }
+ return net.IP(bytes), nil
+}
+
+func isInterfaceUp(intf *net.Interface) bool {
+ if intf == nil {
+ return false
+ }
+ if intf.Flags&net.FlagUp != 0 {
+ glog.V(4).Infof("Interface %v is up", intf.Name)
+ return true
+ }
+ return false
+}
+
+func isLoopbackOrPointToPoint(intf *net.Interface) bool {
+ return intf.Flags&(net.FlagLoopback|net.FlagPointToPoint) != 0
+}
+
+// getMatchingGlobalIP returns the first valid global unicast address of the given
+// 'family' from the list of 'addrs'.
+func getMatchingGlobalIP(addrs []net.Addr, family AddressFamily) (net.IP, error) {
+ if len(addrs) > 0 {
+ for i := range addrs {
+ glog.V(4).Infof("Checking addr %s.", addrs[i].String())
+ ip, _, err := net.ParseCIDR(addrs[i].String())
+ if err != nil {
+ return nil, err
+ }
+ if memberOf(ip, family) {
+ if ip.IsGlobalUnicast() {
+ glog.V(4).Infof("IP found %v", ip)
+ return ip, nil
+ } else {
+ glog.V(4).Infof("Non-global unicast address found %v", ip)
+ }
+ } else {
+ glog.V(4).Infof("%v is not an IPv%d address", ip, int(family))
+ }
+
+ }
+ }
+ return nil, nil
+}
+
+// getIPFromInterface gets the IPs on an interface and returns a global unicast address, if any. The
+// interface must be up, the IP must in the family requested, and the IP must be a global unicast address.
+func getIPFromInterface(intfName string, forFamily AddressFamily, nw networkInterfacer) (net.IP, error) {
+ intf, err := nw.InterfaceByName(intfName)
+ if err != nil {
+ return nil, err
+ }
+ if isInterfaceUp(intf) {
+ addrs, err := nw.Addrs(intf)
+ if err != nil {
+ return nil, err
+ }
+ glog.V(4).Infof("Interface %q has %d addresses :%v.", intfName, len(addrs), addrs)
+ matchingIP, err := getMatchingGlobalIP(addrs, forFamily)
+ if err != nil {
+ return nil, err
+ }
+ if matchingIP != nil {
+ glog.V(4).Infof("Found valid IPv%d address %v for interface %q.", int(forFamily), matchingIP, intfName)
+ return matchingIP, nil
+ }
+ }
+ return nil, nil
+}
+
+// memberOF tells if the IP is of the desired family. Used for checking interface addresses.
+func memberOf(ip net.IP, family AddressFamily) bool {
+ if ip.To4() != nil {
+ return family == familyIPv4
+ } else {
+ return family == familyIPv6
+ }
+}
+
+// chooseIPFromHostInterfaces looks at all system interfaces, trying to find one that is up that
+// has a global unicast address (non-loopback, non-link local, non-point2point), and returns the IP.
+// Searches for IPv4 addresses, and then IPv6 addresses.
+func chooseIPFromHostInterfaces(nw networkInterfacer) (net.IP, error) {
+ intfs, err := nw.Interfaces()
+ if err != nil {
+ return nil, err
+ }
+ if len(intfs) == 0 {
+ return nil, fmt.Errorf("no interfaces found on host.")
+ }
+ for _, family := range []AddressFamily{familyIPv4, familyIPv6} {
+ glog.V(4).Infof("Looking for system interface with a global IPv%d address", uint(family))
+ for _, intf := range intfs {
+ if !isInterfaceUp(&intf) {
+ glog.V(4).Infof("Skipping: down interface %q", intf.Name)
+ continue
+ }
+ if isLoopbackOrPointToPoint(&intf) {
+ glog.V(4).Infof("Skipping: LB or P2P interface %q", intf.Name)
+ continue
+ }
+ addrs, err := nw.Addrs(&intf)
+ if err != nil {
+ return nil, err
+ }
+ if len(addrs) == 0 {
+ glog.V(4).Infof("Skipping: no addresses on interface %q", intf.Name)
+ continue
+ }
+ for _, addr := range addrs {
+ ip, _, err := net.ParseCIDR(addr.String())
+ if err != nil {
+ return nil, fmt.Errorf("Unable to parse CIDR for interface %q: %s", intf.Name, err)
+ }
+ if !memberOf(ip, family) {
+ glog.V(4).Infof("Skipping: no address family match for %q on interface %q.", ip, intf.Name)
+ continue
+ }
+ // TODO: Decide if should open up to allow IPv6 LLAs in future.
+ if !ip.IsGlobalUnicast() {
+ glog.V(4).Infof("Skipping: non-global address %q on interface %q.", ip, intf.Name)
+ continue
+ }
+ glog.V(4).Infof("Found global unicast address %q on interface %q.", ip, intf.Name)
+ return ip, nil
+ }
+ }
+ }
+ return nil, fmt.Errorf("no acceptable interface with global unicast address found on host")
+}
+
+// ChooseHostInterface is a method used fetch an IP for a daemon.
+// If there is no routing info file, it will choose a global IP from the system
+// interfaces. Otherwise, it will use IPv4 and IPv6 route information to return the
+// IP of the interface with a gateway on it (with priority given to IPv4). For a node
+// with no internet connection, it returns error.
+func ChooseHostInterface() (net.IP, error) {
+ var nw networkInterfacer = networkInterface{}
+ if _, err := os.Stat(ipv4RouteFile); os.IsNotExist(err) {
+ return chooseIPFromHostInterfaces(nw)
+ }
+ routes, err := getAllDefaultRoutes()
+ if err != nil {
+ return nil, err
+ }
+ return chooseHostInterfaceFromRoute(routes, nw)
+}
+
+// networkInterfacer defines an interface for several net library functions. Production
+// code will forward to net library functions, and unit tests will override the methods
+// for testing purposes.
+type networkInterfacer interface {
+ InterfaceByName(intfName string) (*net.Interface, error)
+ Addrs(intf *net.Interface) ([]net.Addr, error)
+ Interfaces() ([]net.Interface, error)
+}
+
+// networkInterface implements the networkInterfacer interface for production code, just
+// wrapping the underlying net library function calls.
+type networkInterface struct{}
+
+func (_ networkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
+ return net.InterfaceByName(intfName)
+}
+
+func (_ networkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
+ return intf.Addrs()
+}
+
+func (_ networkInterface) Interfaces() ([]net.Interface, error) {
+ return net.Interfaces()
+}
+
+// getAllDefaultRoutes obtains IPv4 and IPv6 default routes on the node. If unable
+// to read the IPv4 routing info file, we return an error. If unable to read the IPv6
+// routing info file (which is optional), we'll just use the IPv4 route information.
+// Using all the routing info, if no default routes are found, an error is returned.
+func getAllDefaultRoutes() ([]Route, error) {
+ routes, err := v4File.extract()
+ if err != nil {
+ return nil, err
+ }
+ v6Routes, _ := v6File.extract()
+ routes = append(routes, v6Routes...)
+ if len(routes) == 0 {
+ return nil, fmt.Errorf("No default routes.")
+ }
+ return routes, nil
+}
+
+// chooseHostInterfaceFromRoute cycles through each default route provided, looking for a
+// global IP address from the interface for the route. Will first look all each IPv4 route for
+// an IPv4 IP, and then will look at each IPv6 route for an IPv6 IP.
+func chooseHostInterfaceFromRoute(routes []Route, nw networkInterfacer) (net.IP, error) {
+ for _, family := range []AddressFamily{familyIPv4, familyIPv6} {
+ glog.V(4).Infof("Looking for default routes with IPv%d addresses", uint(family))
+ for _, route := range routes {
+ if route.Family != family {
+ continue
+ }
+ glog.V(4).Infof("Default route transits interface %q", route.Interface)
+ finalIP, err := getIPFromInterface(route.Interface, family, nw)
+ if err != nil {
+ return nil, err
+ }
+ if finalIP != nil {
+ glog.V(4).Infof("Found active IP %v ", finalIP)
+ return finalIP, nil
+ }
+ }
+ }
+ glog.V(4).Infof("No active IP found by looking at default routes")
+ return nil, fmt.Errorf("unable to select an IP from default routes.")
+}
+
+// If bind-address is usable, return it directly
+// If bind-address is not usable (unset, 0.0.0.0, or loopback), we will use the host's default
+// interface.
+func ChooseBindAddress(bindAddress net.IP) (net.IP, error) {
+ if bindAddress == nil || bindAddress.IsUnspecified() || bindAddress.IsLoopback() {
+ hostIP, err := ChooseHostInterface()
+ if err != nil {
+ return nil, err
+ }
+ bindAddress = hostIP
+ }
+ return bindAddress, nil
+}
diff --git a/metrics-server/vendor/k8s.io/apimachinery/pkg/util/net/port_range.go b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/net/port_range.go
new file mode 100644
index 0000000..7b6eca8
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/net/port_range.go
@@ -0,0 +1,149 @@
+/*
+Copyright 2015 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package net
+
+import (
+ "fmt"
+ "strconv"
+ "strings"
+)
+
+// PortRange represents a range of TCP/UDP ports. To represent a single port,
+// set Size to 1.
+type PortRange struct {
+ Base int
+ Size int
+}
+
+// Contains tests whether a given port falls within the PortRange.
+func (pr *PortRange) Contains(p int) bool {
+ return (p >= pr.Base) && ((p - pr.Base) < pr.Size)
+}
+
+// String converts the PortRange to a string representation, which can be
+// parsed by PortRange.Set or ParsePortRange.
+func (pr PortRange) String() string {
+ if pr.Size == 0 {
+ return ""
+ }
+ return fmt.Sprintf("%d-%d", pr.Base, pr.Base+pr.Size-1)
+}
+
+// Set parses a string of the form "value", "min-max", or "min+offset", inclusive at both ends, and
+// sets the PortRange from it. This is part of the flag.Value and pflag.Value
+// interfaces.
+func (pr *PortRange) Set(value string) error {
+ const (
+ SinglePortNotation = 1 << iota
+ HyphenNotation
+ PlusNotation
+ )
+
+ value = strings.TrimSpace(value)
+ hyphenIndex := strings.Index(value, "-")
+ plusIndex := strings.Index(value, "+")
+
+ if value == "" {
+ pr.Base = 0
+ pr.Size = 0
+ return nil
+ }
+
+ var err error
+ var low, high int
+ var notation int
+
+ if plusIndex == -1 && hyphenIndex == -1 {
+ notation |= SinglePortNotation
+ }
+ if hyphenIndex != -1 {
+ notation |= HyphenNotation
+ }
+ if plusIndex != -1 {
+ notation |= PlusNotation
+ }
+
+ switch notation {
+ case SinglePortNotation:
+ var port int
+ port, err = strconv.Atoi(value)
+ if err != nil {
+ return err
+ }
+ low = port
+ high = port
+ case HyphenNotation:
+ low, err = strconv.Atoi(value[:hyphenIndex])
+ if err != nil {
+ return err
+ }
+ high, err = strconv.Atoi(value[hyphenIndex+1:])
+ if err != nil {
+ return err
+ }
+ case PlusNotation:
+ var offset int
+ low, err = strconv.Atoi(value[:plusIndex])
+ if err != nil {
+ return err
+ }
+ offset, err = strconv.Atoi(value[plusIndex+1:])
+ if err != nil {
+ return err
+ }
+ high = low + offset
+ default:
+ return fmt.Errorf("unable to parse port range: %s", value)
+ }
+
+ if low > 65535 || high > 65535 {
+ return fmt.Errorf("the port range cannot be greater than 65535: %s", value)
+ }
+
+ if high < low {
+ return fmt.Errorf("end port cannot be less than start port: %s", value)
+ }
+
+ pr.Base = low
+ pr.Size = 1 + high - low
+ return nil
+}
+
+// Type returns a descriptive string about this type. This is part of the
+// pflag.Value interface.
+func (*PortRange) Type() string {
+ return "portRange"
+}
+
+// ParsePortRange parses a string of the form "min-max", inclusive at both
+// ends, and initializs a new PortRange from it.
+func ParsePortRange(value string) (*PortRange, error) {
+ pr := &PortRange{}
+ err := pr.Set(value)
+ if err != nil {
+ return nil, err
+ }
+ return pr, nil
+}
+
+func ParsePortRangeOrDie(value string) *PortRange {
+ pr, err := ParsePortRange(value)
+ if err != nil {
+ panic(fmt.Sprintf("couldn't parse port range %q: %v", value, err))
+ }
+ return pr
+}
diff --git a/metrics-server/vendor/k8s.io/apimachinery/pkg/util/net/port_split.go b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/net/port_split.go
new file mode 100644
index 0000000..c0fd4e2
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/net/port_split.go
@@ -0,0 +1,77 @@
+/*
+Copyright 2015 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package net
+
+import (
+ "strings"
+
+ "k8s.io/apimachinery/pkg/util/sets"
+)
+
+var validSchemes = sets.NewString("http", "https", "")
+
+// SplitSchemeNamePort takes a string of the following forms:
+// * "<name>", returns "", "<name>","", true
+// * "<name>:<port>", returns "", "<name>","<port>",true
+// * "<scheme>:<name>:<port>", returns "<scheme>","<name>","<port>",true
+//
+// Name must be non-empty or valid will be returned false.
+// Scheme must be "http" or "https" if specified
+// Port is returned as a string, and it is not required to be numeric (could be
+// used for a named port, for example).
+func SplitSchemeNamePort(id string) (scheme, name, port string, valid bool) {
+ parts := strings.Split(id, ":")
+ switch len(parts) {
+ case 1:
+ name = parts[0]
+ case 2:
+ name = parts[0]
+ port = parts[1]
+ case 3:
+ scheme = parts[0]
+ name = parts[1]
+ port = parts[2]
+ default:
+ return "", "", "", false
+ }
+
+ if len(name) > 0 && validSchemes.Has(scheme) {
+ return scheme, name, port, true
+ } else {
+ return "", "", "", false
+ }
+}
+
+// JoinSchemeNamePort returns a string that specifies the scheme, name, and port:
+// * "<name>"
+// * "<name>:<port>"
+// * "<scheme>:<name>:<port>"
+// None of the parameters may contain a ':' character
+// Name is required
+// Scheme must be "", "http", or "https"
+func JoinSchemeNamePort(scheme, name, port string) string {
+ if len(scheme) > 0 {
+ // Must include three segments to specify scheme
+ return scheme + ":" + name + ":" + port
+ }
+ if len(port) > 0 {
+ // Must include two segments to specify port
+ return name + ":" + port
+ }
+ // Return name alone
+ return name
+}
diff --git a/metrics-server/vendor/k8s.io/apimachinery/pkg/util/net/util.go b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/net/util.go
new file mode 100644
index 0000000..8344d10
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/net/util.go
@@ -0,0 +1,56 @@
+/*
+Copyright 2016 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package net
+
+import (
+ "net"
+ "net/url"
+ "os"
+ "reflect"
+ "syscall"
+)
+
+// IPNetEqual checks if the two input IPNets are representing the same subnet.
+// For example,
+// 10.0.0.1/24 and 10.0.0.0/24 are the same subnet.
+// 10.0.0.1/24 and 10.0.0.0/25 are not the same subnet.
+func IPNetEqual(ipnet1, ipnet2 *net.IPNet) bool {
+ if ipnet1 == nil || ipnet2 == nil {
+ return false
+ }
+ if reflect.DeepEqual(ipnet1.Mask, ipnet2.Mask) && ipnet1.Contains(ipnet2.IP) && ipnet2.Contains(ipnet1.IP) {
+ return true
+ }
+ return false
+}
+
+// Returns if the given err is "connection reset by peer" error.
+func IsConnectionReset(err error) bool {
+ if urlErr, ok := err.(*url.Error); ok {
+ err = urlErr.Err
+ }
+ if opErr, ok := err.(*net.OpError); ok {
+ err = opErr.Err
+ }
+ if osErr, ok := err.(*os.SyscallError); ok {
+ err = osErr.Err
+ }
+ if errno, ok := err.(syscall.Errno); ok && errno == syscall.ECONNRESET {
+ return true
+ }
+ return false
+}
diff --git a/metrics-server/vendor/k8s.io/apimachinery/pkg/util/rand/rand.go b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/rand/rand.go
new file mode 100644
index 0000000..9421eda
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/rand/rand.go
@@ -0,0 +1,120 @@
+/*
+Copyright 2015 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Package rand provides utilities related to randomization.
+package rand
+
+import (
+ "math/rand"
+ "sync"
+ "time"
+)
+
+var rng = struct {
+ sync.Mutex
+ rand *rand.Rand
+}{
+ rand: rand.New(rand.NewSource(time.Now().UTC().UnixNano())),
+}
+
+// Intn generates an integer in range [0,max).
+// By design this should panic if input is invalid, <= 0.
+func Intn(max int) int {
+ rng.Lock()
+ defer rng.Unlock()
+ return rng.rand.Intn(max)
+}
+
+// IntnRange generates an integer in range [min,max).
+// By design this should panic if input is invalid, <= 0.
+func IntnRange(min, max int) int {
+ rng.Lock()
+ defer rng.Unlock()
+ return rng.rand.Intn(max-min) + min
+}
+
+// IntnRange generates an int64 integer in range [min,max).
+// By design this should panic if input is invalid, <= 0.
+func Int63nRange(min, max int64) int64 {
+ rng.Lock()
+ defer rng.Unlock()
+ return rng.rand.Int63n(max-min) + min
+}
+
+// Seed seeds the rng with the provided seed.
+func Seed(seed int64) {
+ rng.Lock()
+ defer rng.Unlock()
+
+ rng.rand = rand.New(rand.NewSource(seed))
+}
+
+// Perm returns, as a slice of n ints, a pseudo-random permutation of the integers [0,n)
+// from the default Source.
+func Perm(n int) []int {
+ rng.Lock()
+ defer rng.Unlock()
+ return rng.rand.Perm(n)
+}
+
+const (
+ // We omit vowels from the set of available characters to reduce the chances
+ // of "bad words" being formed.
+ alphanums = "bcdfghjklmnpqrstvwxz2456789"
+ // No. of bits required to index into alphanums string.
+ alphanumsIdxBits = 5
+ // Mask used to extract last alphanumsIdxBits of an int.
+ alphanumsIdxMask = 1<<alphanumsIdxBits - 1
+ // No. of random letters we can extract from a single int63.
+ maxAlphanumsPerInt = 63 / alphanumsIdxBits
+)
+
+// String generates a random alphanumeric string, without vowels, which is n
+// characters long. This will panic if n is less than zero.
+// How the random string is created:
+// - we generate random int63's
+// - from each int63, we are extracting multiple random letters by bit-shifting and masking
+// - if some index is out of range of alphanums we neglect it (unlikely to happen multiple times in a row)
+func String(n int) string {
+ b := make([]byte, n)
+ rng.Lock()
+ defer rng.Unlock()
+
+ randomInt63 := rng.rand.Int63()
+ remaining := maxAlphanumsPerInt
+ for i := 0; i < n; {
+ if remaining == 0 {
+ randomInt63, remaining = rng.rand.Int63(), maxAlphanumsPerInt
+ }
+ if idx := int(randomInt63 & alphanumsIdxMask); idx < len(alphanums) {
+ b[i] = alphanums[idx]
+ i++
+ }
+ randomInt63 >>= alphanumsIdxBits
+ remaining--
+ }
+ return string(b)
+}
+
+// SafeEncodeString encodes s using the same characters as rand.String. This reduces the chances of bad words and
+// ensures that strings generated from hash functions appear consistent throughout the API.
+func SafeEncodeString(s string) string {
+ r := make([]byte, len(s))
+ for i, b := range []rune(s) {
+ r[i] = alphanums[(int(b) % len(alphanums))]
+ }
+ return string(r)
+}
diff --git a/metrics-server/vendor/k8s.io/apimachinery/pkg/util/runtime/runtime.go b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/runtime/runtime.go
new file mode 100644
index 0000000..da32fe1
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/runtime/runtime.go
@@ -0,0 +1,169 @@
+/*
+Copyright 2014 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package runtime
+
+import (
+ "fmt"
+ "runtime"
+ "sync"
+ "time"
+
+ "github.com/golang/glog"
+)
+
+var (
+ // ReallyCrash controls the behavior of HandleCrash and now defaults
+ // true. It's still exposed so components can optionally set to false
+ // to restore prior behavior.
+ ReallyCrash = true
+)
+
+// PanicHandlers is a list of functions which will be invoked when a panic happens.
+var PanicHandlers = []func(interface{}){logPanic}
+
+// HandleCrash simply catches a crash and logs an error. Meant to be called via
+// defer. Additional context-specific handlers can be provided, and will be
+// called in case of panic. HandleCrash actually crashes, after calling the
+// handlers and logging the panic message.
+//
+// TODO: remove this function. We are switching to a world where it's safe for
+// apiserver to panic, since it will be restarted by kubelet. At the beginning
+// of the Kubernetes project, nothing was going to restart apiserver and so
+// catching panics was important. But it's actually much simpler for monitoring
+// software if we just exit when an unexpected panic happens.
+func HandleCrash(additionalHandlers ...func(interface{})) {
+ if r := recover(); r != nil {
+ for _, fn := range PanicHandlers {
+ fn(r)
+ }
+ for _, fn := range additionalHandlers {
+ fn(r)
+ }
+ if ReallyCrash {
+ // Actually proceed to panic.
+ panic(r)
+ }
+ }
+}
+
+// logPanic logs the caller tree when a panic occurs.
+func logPanic(r interface{}) {
+ callers := getCallers(r)
+ glog.Errorf("Observed a panic: %#v (%v)\n%v", r, r, callers)
+}
+
+func getCallers(r interface{}) string {
+ callers := ""
+ for i := 0; true; i++ {
+ _, file, line, ok := runtime.Caller(i)
+ if !ok {
+ break
+ }
+ callers = callers + fmt.Sprintf("%v:%v\n", file, line)
+ }
+
+ return callers
+}
+
+// ErrorHandlers is a list of functions which will be invoked when an unreturnable
+// error occurs.
+// TODO(lavalamp): for testability, this and the below HandleError function
+// should be packaged up into a testable and reusable object.
+var ErrorHandlers = []func(error){
+ logError,
+ (&rudimentaryErrorBackoff{
+ lastErrorTime: time.Now(),
+ // 1ms was the number folks were able to stomach as a global rate limit.
+ // If you need to log errors more than 1000 times a second you
+ // should probably consider fixing your code instead. :)
+ minPeriod: time.Millisecond,
+ }).OnError,
+}
+
+// HandlerError is a method to invoke when a non-user facing piece of code cannot
+// return an error and needs to indicate it has been ignored. Invoking this method
+// is preferable to logging the error - the default behavior is to log but the
+// errors may be sent to a remote server for analysis.
+func HandleError(err error) {
+ // this is sometimes called with a nil error. We probably shouldn't fail and should do nothing instead
+ if err == nil {
+ return
+ }
+
+ for _, fn := range ErrorHandlers {
+ fn(err)
+ }
+}
+
+// logError prints an error with the call stack of the location it was reported
+func logError(err error) {
+ glog.ErrorDepth(2, err)
+}
+
+type rudimentaryErrorBackoff struct {
+ minPeriod time.Duration // immutable
+ // TODO(lavalamp): use the clock for testability. Need to move that
+ // package for that to be accessible here.
+ lastErrorTimeLock sync.Mutex
+ lastErrorTime time.Time
+}
+
+// OnError will block if it is called more often than the embedded period time.
+// This will prevent overly tight hot error loops.
+func (r *rudimentaryErrorBackoff) OnError(error) {
+ r.lastErrorTimeLock.Lock()
+ defer r.lastErrorTimeLock.Unlock()
+ d := time.Since(r.lastErrorTime)
+ if d < r.minPeriod {
+ // If the time moves backwards for any reason, do nothing
+ time.Sleep(r.minPeriod - d)
+ }
+ r.lastErrorTime = time.Now()
+}
+
+// GetCaller returns the caller of the function that calls it.
+func GetCaller() string {
+ var pc [1]uintptr
+ runtime.Callers(3, pc[:])
+ f := runtime.FuncForPC(pc[0])
+ if f == nil {
+ return fmt.Sprintf("Unable to find caller")
+ }
+ return f.Name()
+}
+
+// RecoverFromPanic replaces the specified error with an error containing the
+// original error, and the call tree when a panic occurs. This enables error
+// handlers to handle errors and panics the same way.
+func RecoverFromPanic(err *error) {
+ if r := recover(); r != nil {
+ callers := getCallers(r)
+
+ *err = fmt.Errorf(
+ "recovered from panic %q. (err=%v) Call stack:\n%v",
+ r,
+ *err,
+ callers)
+ }
+}
+
+// Must panics on non-nil errors. Useful to handling programmer level errors.
+func Must(err error) {
+ if err != nil {
+ panic(err)
+ }
+}
diff --git a/metrics-server/vendor/k8s.io/apimachinery/pkg/util/sets/byte.go b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/sets/byte.go
new file mode 100644
index 0000000..766f450
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/sets/byte.go
@@ -0,0 +1,203 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by set-gen. DO NOT EDIT.
+
+package sets
+
+import (
+ "reflect"
+ "sort"
+)
+
+// sets.Byte is a set of bytes, implemented via map[byte]struct{} for minimal memory consumption.
+type Byte map[byte]Empty
+
+// NewByte creates a Byte from a list of values.
+func NewByte(items ...byte) Byte {
+ ss := Byte{}
+ ss.Insert(items...)
+ return ss
+}
+
+// ByteKeySet creates a Byte from a keys of a map[byte](? extends interface{}).
+// If the value passed in is not actually a map, this will panic.
+func ByteKeySet(theMap interface{}) Byte {
+ v := reflect.ValueOf(theMap)
+ ret := Byte{}
+
+ for _, keyValue := range v.MapKeys() {
+ ret.Insert(keyValue.Interface().(byte))
+ }
+ return ret
+}
+
+// Insert adds items to the set.
+func (s Byte) Insert(items ...byte) {
+ for _, item := range items {
+ s[item] = Empty{}
+ }
+}
+
+// Delete removes all items from the set.
+func (s Byte) Delete(items ...byte) {
+ for _, item := range items {
+ delete(s, item)
+ }
+}
+
+// Has returns true if and only if item is contained in the set.
+func (s Byte) Has(item byte) bool {
+ _, contained := s[item]
+ return contained
+}
+
+// HasAll returns true if and only if all items are contained in the set.
+func (s Byte) HasAll(items ...byte) bool {
+ for _, item := range items {
+ if !s.Has(item) {
+ return false
+ }
+ }
+ return true
+}
+
+// HasAny returns true if any items are contained in the set.
+func (s Byte) HasAny(items ...byte) bool {
+ for _, item := range items {
+ if s.Has(item) {
+ return true
+ }
+ }
+ return false
+}
+
+// Difference returns a set of objects that are not in s2
+// For example:
+// s1 = {a1, a2, a3}
+// s2 = {a1, a2, a4, a5}
+// s1.Difference(s2) = {a3}
+// s2.Difference(s1) = {a4, a5}
+func (s Byte) Difference(s2 Byte) Byte {
+ result := NewByte()
+ for key := range s {
+ if !s2.Has(key) {
+ result.Insert(key)
+ }
+ }
+ return result
+}
+
+// Union returns a new set which includes items in either s1 or s2.
+// For example:
+// s1 = {a1, a2}
+// s2 = {a3, a4}
+// s1.Union(s2) = {a1, a2, a3, a4}
+// s2.Union(s1) = {a1, a2, a3, a4}
+func (s1 Byte) Union(s2 Byte) Byte {
+ result := NewByte()
+ for key := range s1 {
+ result.Insert(key)
+ }
+ for key := range s2 {
+ result.Insert(key)
+ }
+ return result
+}
+
+// Intersection returns a new set which includes the item in BOTH s1 and s2
+// For example:
+// s1 = {a1, a2}
+// s2 = {a2, a3}
+// s1.Intersection(s2) = {a2}
+func (s1 Byte) Intersection(s2 Byte) Byte {
+ var walk, other Byte
+ result := NewByte()
+ if s1.Len() < s2.Len() {
+ walk = s1
+ other = s2
+ } else {
+ walk = s2
+ other = s1
+ }
+ for key := range walk {
+ if other.Has(key) {
+ result.Insert(key)
+ }
+ }
+ return result
+}
+
+// IsSuperset returns true if and only if s1 is a superset of s2.
+func (s1 Byte) IsSuperset(s2 Byte) bool {
+ for item := range s2 {
+ if !s1.Has(item) {
+ return false
+ }
+ }
+ return true
+}
+
+// Equal returns true if and only if s1 is equal (as a set) to s2.
+// Two sets are equal if their membership is identical.
+// (In practice, this means same elements, order doesn't matter)
+func (s1 Byte) Equal(s2 Byte) bool {
+ return len(s1) == len(s2) && s1.IsSuperset(s2)
+}
+
+type sortableSliceOfByte []byte
+
+func (s sortableSliceOfByte) Len() int { return len(s) }
+func (s sortableSliceOfByte) Less(i, j int) bool { return lessByte(s[i], s[j]) }
+func (s sortableSliceOfByte) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+
+// List returns the contents as a sorted byte slice.
+func (s Byte) List() []byte {
+ res := make(sortableSliceOfByte, 0, len(s))
+ for key := range s {
+ res = append(res, key)
+ }
+ sort.Sort(res)
+ return []byte(res)
+}
+
+// UnsortedList returns the slice with contents in random order.
+func (s Byte) UnsortedList() []byte {
+ res := make([]byte, 0, len(s))
+ for key := range s {
+ res = append(res, key)
+ }
+ return res
+}
+
+// Returns a single element from the set.
+func (s Byte) PopAny() (byte, bool) {
+ for key := range s {
+ s.Delete(key)
+ return key, true
+ }
+ var zeroValue byte
+ return zeroValue, false
+}
+
+// Len returns the size of the set.
+func (s Byte) Len() int {
+ return len(s)
+}
+
+func lessByte(lhs, rhs byte) bool {
+ return lhs < rhs
+}
diff --git a/metrics-server/vendor/k8s.io/apimachinery/pkg/util/sets/doc.go b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/sets/doc.go
new file mode 100644
index 0000000..b152a0b
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/sets/doc.go
@@ -0,0 +1,20 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by set-gen. DO NOT EDIT.
+
+// Package sets has auto-generated set types.
+package sets
diff --git a/metrics-server/vendor/k8s.io/apimachinery/pkg/util/sets/empty.go b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/sets/empty.go
new file mode 100644
index 0000000..e11e622
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/sets/empty.go
@@ -0,0 +1,23 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by set-gen. DO NOT EDIT.
+
+package sets
+
+// Empty is public since it is used by some internal API objects for conversions between external
+// string arrays and internal sets, and conversion logic requires public types today.
+type Empty struct{}
diff --git a/metrics-server/vendor/k8s.io/apimachinery/pkg/util/sets/int.go b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/sets/int.go
new file mode 100644
index 0000000..a0a513c
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/sets/int.go
@@ -0,0 +1,203 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by set-gen. DO NOT EDIT.
+
+package sets
+
+import (
+ "reflect"
+ "sort"
+)
+
+// sets.Int is a set of ints, implemented via map[int]struct{} for minimal memory consumption.
+type Int map[int]Empty
+
+// NewInt creates a Int from a list of values.
+func NewInt(items ...int) Int {
+ ss := Int{}
+ ss.Insert(items...)
+ return ss
+}
+
+// IntKeySet creates a Int from a keys of a map[int](? extends interface{}).
+// If the value passed in is not actually a map, this will panic.
+func IntKeySet(theMap interface{}) Int {
+ v := reflect.ValueOf(theMap)
+ ret := Int{}
+
+ for _, keyValue := range v.MapKeys() {
+ ret.Insert(keyValue.Interface().(int))
+ }
+ return ret
+}
+
+// Insert adds items to the set.
+func (s Int) Insert(items ...int) {
+ for _, item := range items {
+ s[item] = Empty{}
+ }
+}
+
+// Delete removes all items from the set.
+func (s Int) Delete(items ...int) {
+ for _, item := range items {
+ delete(s, item)
+ }
+}
+
+// Has returns true if and only if item is contained in the set.
+func (s Int) Has(item int) bool {
+ _, contained := s[item]
+ return contained
+}
+
+// HasAll returns true if and only if all items are contained in the set.
+func (s Int) HasAll(items ...int) bool {
+ for _, item := range items {
+ if !s.Has(item) {
+ return false
+ }
+ }
+ return true
+}
+
+// HasAny returns true if any items are contained in the set.
+func (s Int) HasAny(items ...int) bool {
+ for _, item := range items {
+ if s.Has(item) {
+ return true
+ }
+ }
+ return false
+}
+
+// Difference returns a set of objects that are not in s2
+// For example:
+// s1 = {a1, a2, a3}
+// s2 = {a1, a2, a4, a5}
+// s1.Difference(s2) = {a3}
+// s2.Difference(s1) = {a4, a5}
+func (s Int) Difference(s2 Int) Int {
+ result := NewInt()
+ for key := range s {
+ if !s2.Has(key) {
+ result.Insert(key)
+ }
+ }
+ return result
+}
+
+// Union returns a new set which includes items in either s1 or s2.
+// For example:
+// s1 = {a1, a2}
+// s2 = {a3, a4}
+// s1.Union(s2) = {a1, a2, a3, a4}
+// s2.Union(s1) = {a1, a2, a3, a4}
+func (s1 Int) Union(s2 Int) Int {
+ result := NewInt()
+ for key := range s1 {
+ result.Insert(key)
+ }
+ for key := range s2 {
+ result.Insert(key)
+ }
+ return result
+}
+
+// Intersection returns a new set which includes the item in BOTH s1 and s2
+// For example:
+// s1 = {a1, a2}
+// s2 = {a2, a3}
+// s1.Intersection(s2) = {a2}
+func (s1 Int) Intersection(s2 Int) Int {
+ var walk, other Int
+ result := NewInt()
+ if s1.Len() < s2.Len() {
+ walk = s1
+ other = s2
+ } else {
+ walk = s2
+ other = s1
+ }
+ for key := range walk {
+ if other.Has(key) {
+ result.Insert(key)
+ }
+ }
+ return result
+}
+
+// IsSuperset returns true if and only if s1 is a superset of s2.
+func (s1 Int) IsSuperset(s2 Int) bool {
+ for item := range s2 {
+ if !s1.Has(item) {
+ return false
+ }
+ }
+ return true
+}
+
+// Equal returns true if and only if s1 is equal (as a set) to s2.
+// Two sets are equal if their membership is identical.
+// (In practice, this means same elements, order doesn't matter)
+func (s1 Int) Equal(s2 Int) bool {
+ return len(s1) == len(s2) && s1.IsSuperset(s2)
+}
+
+type sortableSliceOfInt []int
+
+func (s sortableSliceOfInt) Len() int { return len(s) }
+func (s sortableSliceOfInt) Less(i, j int) bool { return lessInt(s[i], s[j]) }
+func (s sortableSliceOfInt) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+
+// List returns the contents as a sorted int slice.
+func (s Int) List() []int {
+ res := make(sortableSliceOfInt, 0, len(s))
+ for key := range s {
+ res = append(res, key)
+ }
+ sort.Sort(res)
+ return []int(res)
+}
+
+// UnsortedList returns the slice with contents in random order.
+func (s Int) UnsortedList() []int {
+ res := make([]int, 0, len(s))
+ for key := range s {
+ res = append(res, key)
+ }
+ return res
+}
+
+// Returns a single element from the set.
+func (s Int) PopAny() (int, bool) {
+ for key := range s {
+ s.Delete(key)
+ return key, true
+ }
+ var zeroValue int
+ return zeroValue, false
+}
+
+// Len returns the size of the set.
+func (s Int) Len() int {
+ return len(s)
+}
+
+func lessInt(lhs, rhs int) bool {
+ return lhs < rhs
+}
diff --git a/metrics-server/vendor/k8s.io/apimachinery/pkg/util/sets/int64.go b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/sets/int64.go
new file mode 100644
index 0000000..9ca9af0
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/sets/int64.go
@@ -0,0 +1,203 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by set-gen. DO NOT EDIT.
+
+package sets
+
+import (
+ "reflect"
+ "sort"
+)
+
+// sets.Int64 is a set of int64s, implemented via map[int64]struct{} for minimal memory consumption.
+type Int64 map[int64]Empty
+
+// NewInt64 creates a Int64 from a list of values.
+func NewInt64(items ...int64) Int64 {
+ ss := Int64{}
+ ss.Insert(items...)
+ return ss
+}
+
+// Int64KeySet creates a Int64 from a keys of a map[int64](? extends interface{}).
+// If the value passed in is not actually a map, this will panic.
+func Int64KeySet(theMap interface{}) Int64 {
+ v := reflect.ValueOf(theMap)
+ ret := Int64{}
+
+ for _, keyValue := range v.MapKeys() {
+ ret.Insert(keyValue.Interface().(int64))
+ }
+ return ret
+}
+
+// Insert adds items to the set.
+func (s Int64) Insert(items ...int64) {
+ for _, item := range items {
+ s[item] = Empty{}
+ }
+}
+
+// Delete removes all items from the set.
+func (s Int64) Delete(items ...int64) {
+ for _, item := range items {
+ delete(s, item)
+ }
+}
+
+// Has returns true if and only if item is contained in the set.
+func (s Int64) Has(item int64) bool {
+ _, contained := s[item]
+ return contained
+}
+
+// HasAll returns true if and only if all items are contained in the set.
+func (s Int64) HasAll(items ...int64) bool {
+ for _, item := range items {
+ if !s.Has(item) {
+ return false
+ }
+ }
+ return true
+}
+
+// HasAny returns true if any items are contained in the set.
+func (s Int64) HasAny(items ...int64) bool {
+ for _, item := range items {
+ if s.Has(item) {
+ return true
+ }
+ }
+ return false
+}
+
+// Difference returns a set of objects that are not in s2
+// For example:
+// s1 = {a1, a2, a3}
+// s2 = {a1, a2, a4, a5}
+// s1.Difference(s2) = {a3}
+// s2.Difference(s1) = {a4, a5}
+func (s Int64) Difference(s2 Int64) Int64 {
+ result := NewInt64()
+ for key := range s {
+ if !s2.Has(key) {
+ result.Insert(key)
+ }
+ }
+ return result
+}
+
+// Union returns a new set which includes items in either s1 or s2.
+// For example:
+// s1 = {a1, a2}
+// s2 = {a3, a4}
+// s1.Union(s2) = {a1, a2, a3, a4}
+// s2.Union(s1) = {a1, a2, a3, a4}
+func (s1 Int64) Union(s2 Int64) Int64 {
+ result := NewInt64()
+ for key := range s1 {
+ result.Insert(key)
+ }
+ for key := range s2 {
+ result.Insert(key)
+ }
+ return result
+}
+
+// Intersection returns a new set which includes the item in BOTH s1 and s2
+// For example:
+// s1 = {a1, a2}
+// s2 = {a2, a3}
+// s1.Intersection(s2) = {a2}
+func (s1 Int64) Intersection(s2 Int64) Int64 {
+ var walk, other Int64
+ result := NewInt64()
+ if s1.Len() < s2.Len() {
+ walk = s1
+ other = s2
+ } else {
+ walk = s2
+ other = s1
+ }
+ for key := range walk {
+ if other.Has(key) {
+ result.Insert(key)
+ }
+ }
+ return result
+}
+
+// IsSuperset returns true if and only if s1 is a superset of s2.
+func (s1 Int64) IsSuperset(s2 Int64) bool {
+ for item := range s2 {
+ if !s1.Has(item) {
+ return false
+ }
+ }
+ return true
+}
+
+// Equal returns true if and only if s1 is equal (as a set) to s2.
+// Two sets are equal if their membership is identical.
+// (In practice, this means same elements, order doesn't matter)
+func (s1 Int64) Equal(s2 Int64) bool {
+ return len(s1) == len(s2) && s1.IsSuperset(s2)
+}
+
+type sortableSliceOfInt64 []int64
+
+func (s sortableSliceOfInt64) Len() int { return len(s) }
+func (s sortableSliceOfInt64) Less(i, j int) bool { return lessInt64(s[i], s[j]) }
+func (s sortableSliceOfInt64) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+
+// List returns the contents as a sorted int64 slice.
+func (s Int64) List() []int64 {
+ res := make(sortableSliceOfInt64, 0, len(s))
+ for key := range s {
+ res = append(res, key)
+ }
+ sort.Sort(res)
+ return []int64(res)
+}
+
+// UnsortedList returns the slice with contents in random order.
+func (s Int64) UnsortedList() []int64 {
+ res := make([]int64, 0, len(s))
+ for key := range s {
+ res = append(res, key)
+ }
+ return res
+}
+
+// Returns a single element from the set.
+func (s Int64) PopAny() (int64, bool) {
+ for key := range s {
+ s.Delete(key)
+ return key, true
+ }
+ var zeroValue int64
+ return zeroValue, false
+}
+
+// Len returns the size of the set.
+func (s Int64) Len() int {
+ return len(s)
+}
+
+func lessInt64(lhs, rhs int64) bool {
+ return lhs < rhs
+}
diff --git a/metrics-server/vendor/k8s.io/apimachinery/pkg/util/sets/string.go b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/sets/string.go
new file mode 100644
index 0000000..ba00ad7
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/sets/string.go
@@ -0,0 +1,203 @@
+/*
+Copyright The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Code generated by set-gen. DO NOT EDIT.
+
+package sets
+
+import (
+ "reflect"
+ "sort"
+)
+
+// sets.String is a set of strings, implemented via map[string]struct{} for minimal memory consumption.
+type String map[string]Empty
+
+// NewString creates a String from a list of values.
+func NewString(items ...string) String {
+ ss := String{}
+ ss.Insert(items...)
+ return ss
+}
+
+// StringKeySet creates a String from a keys of a map[string](? extends interface{}).
+// If the value passed in is not actually a map, this will panic.
+func StringKeySet(theMap interface{}) String {
+ v := reflect.ValueOf(theMap)
+ ret := String{}
+
+ for _, keyValue := range v.MapKeys() {
+ ret.Insert(keyValue.Interface().(string))
+ }
+ return ret
+}
+
+// Insert adds items to the set.
+func (s String) Insert(items ...string) {
+ for _, item := range items {
+ s[item] = Empty{}
+ }
+}
+
+// Delete removes all items from the set.
+func (s String) Delete(items ...string) {
+ for _, item := range items {
+ delete(s, item)
+ }
+}
+
+// Has returns true if and only if item is contained in the set.
+func (s String) Has(item string) bool {
+ _, contained := s[item]
+ return contained
+}
+
+// HasAll returns true if and only if all items are contained in the set.
+func (s String) HasAll(items ...string) bool {
+ for _, item := range items {
+ if !s.Has(item) {
+ return false
+ }
+ }
+ return true
+}
+
+// HasAny returns true if any items are contained in the set.
+func (s String) HasAny(items ...string) bool {
+ for _, item := range items {
+ if s.Has(item) {
+ return true
+ }
+ }
+ return false
+}
+
+// Difference returns a set of objects that are not in s2
+// For example:
+// s1 = {a1, a2, a3}
+// s2 = {a1, a2, a4, a5}
+// s1.Difference(s2) = {a3}
+// s2.Difference(s1) = {a4, a5}
+func (s String) Difference(s2 String) String {
+ result := NewString()
+ for key := range s {
+ if !s2.Has(key) {
+ result.Insert(key)
+ }
+ }
+ return result
+}
+
+// Union returns a new set which includes items in either s1 or s2.
+// For example:
+// s1 = {a1, a2}
+// s2 = {a3, a4}
+// s1.Union(s2) = {a1, a2, a3, a4}
+// s2.Union(s1) = {a1, a2, a3, a4}
+func (s1 String) Union(s2 String) String {
+ result := NewString()
+ for key := range s1 {
+ result.Insert(key)
+ }
+ for key := range s2 {
+ result.Insert(key)
+ }
+ return result
+}
+
+// Intersection returns a new set which includes the item in BOTH s1 and s2
+// For example:
+// s1 = {a1, a2}
+// s2 = {a2, a3}
+// s1.Intersection(s2) = {a2}
+func (s1 String) Intersection(s2 String) String {
+ var walk, other String
+ result := NewString()
+ if s1.Len() < s2.Len() {
+ walk = s1
+ other = s2
+ } else {
+ walk = s2
+ other = s1
+ }
+ for key := range walk {
+ if other.Has(key) {
+ result.Insert(key)
+ }
+ }
+ return result
+}
+
+// IsSuperset returns true if and only if s1 is a superset of s2.
+func (s1 String) IsSuperset(s2 String) bool {
+ for item := range s2 {
+ if !s1.Has(item) {
+ return false
+ }
+ }
+ return true
+}
+
+// Equal returns true if and only if s1 is equal (as a set) to s2.
+// Two sets are equal if their membership is identical.
+// (In practice, this means same elements, order doesn't matter)
+func (s1 String) Equal(s2 String) bool {
+ return len(s1) == len(s2) && s1.IsSuperset(s2)
+}
+
+type sortableSliceOfString []string
+
+func (s sortableSliceOfString) Len() int { return len(s) }
+func (s sortableSliceOfString) Less(i, j int) bool { return lessString(s[i], s[j]) }
+func (s sortableSliceOfString) Swap(i, j int) { s[i], s[j] = s[j], s[i] }
+
+// List returns the contents as a sorted string slice.
+func (s String) List() []string {
+ res := make(sortableSliceOfString, 0, len(s))
+ for key := range s {
+ res = append(res, key)
+ }
+ sort.Sort(res)
+ return []string(res)
+}
+
+// UnsortedList returns the slice with contents in random order.
+func (s String) UnsortedList() []string {
+ res := make([]string, 0, len(s))
+ for key := range s {
+ res = append(res, key)
+ }
+ return res
+}
+
+// Returns a single element from the set.
+func (s String) PopAny() (string, bool) {
+ for key := range s {
+ s.Delete(key)
+ return key, true
+ }
+ var zeroValue string
+ return zeroValue, false
+}
+
+// Len returns the size of the set.
+func (s String) Len() int {
+ return len(s)
+}
+
+func lessString(lhs, rhs string) bool {
+ return lhs < rhs
+}
diff --git a/metrics-server/vendor/k8s.io/apimachinery/pkg/util/strategicpatch/OWNERS b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/strategicpatch/OWNERS
new file mode 100644
index 0000000..8e8d9fc
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/strategicpatch/OWNERS
@@ -0,0 +1,5 @@
+approvers:
+- pwittrock
+reviewers:
+- mengqiy
+- apelisse
diff --git a/metrics-server/vendor/k8s.io/apimachinery/pkg/util/strategicpatch/errors.go b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/strategicpatch/errors.go
new file mode 100644
index 0000000..ab66d04
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/strategicpatch/errors.go
@@ -0,0 +1,49 @@
+/*
+Copyright 2017 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package strategicpatch
+
+import (
+ "fmt"
+)
+
+type LookupPatchMetaError struct {
+ Path string
+ Err error
+}
+
+func (e LookupPatchMetaError) Error() string {
+ return fmt.Sprintf("LookupPatchMetaError(%s): %v", e.Path, e.Err)
+}
+
+type FieldNotFoundError struct {
+ Path string
+ Field string
+}
+
+func (e FieldNotFoundError) Error() string {
+ return fmt.Sprintf("unable to find api field %q in %s", e.Field, e.Path)
+}
+
+type InvalidTypeError struct {
+ Path string
+ Expected string
+ Actual string
+}
+
+func (e InvalidTypeError) Error() string {
+ return fmt.Sprintf("invalid type for %s: got %q, expected %q", e.Path, e.Actual, e.Expected)
+}
diff --git a/metrics-server/vendor/k8s.io/apimachinery/pkg/util/strategicpatch/meta.go b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/strategicpatch/meta.go
new file mode 100644
index 0000000..c31de15
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/strategicpatch/meta.go
@@ -0,0 +1,194 @@
+/*
+Copyright 2017 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package strategicpatch
+
+import (
+ "errors"
+ "fmt"
+ "reflect"
+
+ "k8s.io/apimachinery/pkg/util/mergepatch"
+ forkedjson "k8s.io/apimachinery/third_party/forked/golang/json"
+ openapi "k8s.io/kube-openapi/pkg/util/proto"
+)
+
+type PatchMeta struct {
+ patchStrategies []string
+ patchMergeKey string
+}
+
+func (pm PatchMeta) GetPatchStrategies() []string {
+ if pm.patchStrategies == nil {
+ return []string{}
+ }
+ return pm.patchStrategies
+}
+
+func (pm PatchMeta) SetPatchStrategies(ps []string) {
+ pm.patchStrategies = ps
+}
+
+func (pm PatchMeta) GetPatchMergeKey() string {
+ return pm.patchMergeKey
+}
+
+func (pm PatchMeta) SetPatchMergeKey(pmk string) {
+ pm.patchMergeKey = pmk
+}
+
+type LookupPatchMeta interface {
+ // LookupPatchMetadataForStruct gets subschema and the patch metadata (e.g. patch strategy and merge key) for map.
+ LookupPatchMetadataForStruct(key string) (LookupPatchMeta, PatchMeta, error)
+ // LookupPatchMetadataForSlice get subschema and the patch metadata for slice.
+ LookupPatchMetadataForSlice(key string) (LookupPatchMeta, PatchMeta, error)
+ // Get the type name of the field
+ Name() string
+}
+
+type PatchMetaFromStruct struct {
+ T reflect.Type
+}
+
+func NewPatchMetaFromStruct(dataStruct interface{}) (PatchMetaFromStruct, error) {
+ t, err := getTagStructType(dataStruct)
+ return PatchMetaFromStruct{T: t}, err
+}
+
+var _ LookupPatchMeta = PatchMetaFromStruct{}
+
+func (s PatchMetaFromStruct) LookupPatchMetadataForStruct(key string) (LookupPatchMeta, PatchMeta, error) {
+ fieldType, fieldPatchStrategies, fieldPatchMergeKey, err := forkedjson.LookupPatchMetadataForStruct(s.T, key)
+ if err != nil {
+ return nil, PatchMeta{}, err
+ }
+
+ return PatchMetaFromStruct{T: fieldType},
+ PatchMeta{
+ patchStrategies: fieldPatchStrategies,
+ patchMergeKey: fieldPatchMergeKey,
+ }, nil
+}
+
+func (s PatchMetaFromStruct) LookupPatchMetadataForSlice(key string) (LookupPatchMeta, PatchMeta, error) {
+ subschema, patchMeta, err := s.LookupPatchMetadataForStruct(key)
+ if err != nil {
+ return nil, PatchMeta{}, err
+ }
+ elemPatchMetaFromStruct := subschema.(PatchMetaFromStruct)
+ t := elemPatchMetaFromStruct.T
+
+ var elemType reflect.Type
+ switch t.Kind() {
+ // If t is an array or a slice, get the element type.
+ // If element is still an array or a slice, return an error.
+ // Otherwise, return element type.
+ case reflect.Array, reflect.Slice:
+ elemType = t.Elem()
+ if elemType.Kind() == reflect.Array || elemType.Kind() == reflect.Slice {
+ return nil, PatchMeta{}, errors.New("unexpected slice of slice")
+ }
+ // If t is an pointer, get the underlying element.
+ // If the underlying element is neither an array nor a slice, the pointer is pointing to a slice,
+ // e.g. https://github.com/kubernetes/kubernetes/blob/bc22e206c79282487ea0bf5696d5ccec7e839a76/staging/src/k8s.io/apimachinery/pkg/util/strategicpatch/patch_test.go#L2782-L2822
+ // If the underlying element is either an array or a slice, return its element type.
+ case reflect.Ptr:
+ t = t.Elem()
+ if t.Kind() == reflect.Array || t.Kind() == reflect.Slice {
+ t = t.Elem()
+ }
+ elemType = t
+ default:
+ return nil, PatchMeta{}, fmt.Errorf("expected slice or array type, but got: %s", s.T.Kind().String())
+ }
+
+ return PatchMetaFromStruct{T: elemType}, patchMeta, nil
+}
+
+func (s PatchMetaFromStruct) Name() string {
+ return s.T.Kind().String()
+}
+
+func getTagStructType(dataStruct interface{}) (reflect.Type, error) {
+ if dataStruct == nil {
+ return nil, mergepatch.ErrBadArgKind(struct{}{}, nil)
+ }
+
+ t := reflect.TypeOf(dataStruct)
+ // Get the underlying type for pointers
+ if t.Kind() == reflect.Ptr {
+ t = t.Elem()
+ }
+
+ if t.Kind() != reflect.Struct {
+ return nil, mergepatch.ErrBadArgKind(struct{}{}, dataStruct)
+ }
+
+ return t, nil
+}
+
+func GetTagStructTypeOrDie(dataStruct interface{}) reflect.Type {
+ t, err := getTagStructType(dataStruct)
+ if err != nil {
+ panic(err)
+ }
+ return t
+}
+
+type PatchMetaFromOpenAPI struct {
+ Schema openapi.Schema
+}
+
+func NewPatchMetaFromOpenAPI(s openapi.Schema) PatchMetaFromOpenAPI {
+ return PatchMetaFromOpenAPI{Schema: s}
+}
+
+var _ LookupPatchMeta = PatchMetaFromOpenAPI{}
+
+func (s PatchMetaFromOpenAPI) LookupPatchMetadataForStruct(key string) (LookupPatchMeta, PatchMeta, error) {
+ if s.Schema == nil {
+ return nil, PatchMeta{}, nil
+ }
+ kindItem := NewKindItem(key, s.Schema.GetPath())
+ s.Schema.Accept(kindItem)
+
+ err := kindItem.Error()
+ if err != nil {
+ return nil, PatchMeta{}, err
+ }
+ return PatchMetaFromOpenAPI{Schema: kindItem.subschema},
+ kindItem.patchmeta, nil
+}
+
+func (s PatchMetaFromOpenAPI) LookupPatchMetadataForSlice(key string) (LookupPatchMeta, PatchMeta, error) {
+ if s.Schema == nil {
+ return nil, PatchMeta{}, nil
+ }
+ sliceItem := NewSliceItem(key, s.Schema.GetPath())
+ s.Schema.Accept(sliceItem)
+
+ err := sliceItem.Error()
+ if err != nil {
+ return nil, PatchMeta{}, err
+ }
+ return PatchMetaFromOpenAPI{Schema: sliceItem.subschema},
+ sliceItem.patchmeta, nil
+}
+
+func (s PatchMetaFromOpenAPI) Name() string {
+ schema := s.Schema
+ return schema.GetName()
+}
diff --git a/metrics-server/vendor/k8s.io/apimachinery/pkg/util/strategicpatch/patch.go b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/strategicpatch/patch.go
new file mode 100644
index 0000000..6be328f
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/strategicpatch/patch.go
@@ -0,0 +1,2174 @@
+/*
+Copyright 2014 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package strategicpatch
+
+import (
+ "fmt"
+ "reflect"
+ "sort"
+ "strings"
+
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+ "k8s.io/apimachinery/pkg/util/json"
+ "k8s.io/apimachinery/pkg/util/mergepatch"
+)
+
+// An alternate implementation of JSON Merge Patch
+// (https://tools.ietf.org/html/rfc7386) which supports the ability to annotate
+// certain fields with metadata that indicates whether the elements of JSON
+// lists should be merged or replaced.
+//
+// For more information, see the PATCH section of docs/devel/api-conventions.md.
+//
+// Some of the content of this package was borrowed with minor adaptations from
+// evanphx/json-patch and openshift/origin.
+
+const (
+ directiveMarker = "$patch"
+ deleteDirective = "delete"
+ replaceDirective = "replace"
+ mergeDirective = "merge"
+
+ retainKeysStrategy = "retainKeys"
+
+ deleteFromPrimitiveListDirectivePrefix = "$deleteFromPrimitiveList"
+ retainKeysDirective = "$" + retainKeysStrategy
+ setElementOrderDirectivePrefix = "$setElementOrder"
+)
+
+// JSONMap is a representations of JSON object encoded as map[string]interface{}
+// where the children can be either map[string]interface{}, []interface{} or
+// primitive type).
+// Operating on JSONMap representation is much faster as it doesn't require any
+// json marshaling and/or unmarshaling operations.
+type JSONMap map[string]interface{}
+
+type DiffOptions struct {
+ // SetElementOrder determines whether we generate the $setElementOrder parallel list.
+ SetElementOrder bool
+ // IgnoreChangesAndAdditions indicates if we keep the changes and additions in the patch.
+ IgnoreChangesAndAdditions bool
+ // IgnoreDeletions indicates if we keep the deletions in the patch.
+ IgnoreDeletions bool
+ // We introduce a new value retainKeys for patchStrategy.
+ // It indicates that all fields needing to be preserved must be
+ // present in the `retainKeys` list.
+ // And the fields that are present will be merged with live object.
+ // All the missing fields will be cleared when patching.
+ BuildRetainKeysDirective bool
+}
+
+type MergeOptions struct {
+ // MergeParallelList indicates if we are merging the parallel list.
+ // We don't merge parallel list when calling mergeMap() in CreateThreeWayMergePatch()
+ // which is called client-side.
+ // We merge parallel list iff when calling mergeMap() in StrategicMergeMapPatch()
+ // which is called server-side
+ MergeParallelList bool
+ // IgnoreUnmatchedNulls indicates if we should process the unmatched nulls.
+ IgnoreUnmatchedNulls bool
+}
+
+// The following code is adapted from github.com/openshift/origin/pkg/util/jsonmerge.
+// Instead of defining a Delta that holds an original, a patch and a set of preconditions,
+// the reconcile method accepts a set of preconditions as an argument.
+
+// CreateTwoWayMergePatch creates a patch that can be passed to StrategicMergePatch from an original
+// document and a modified document, which are passed to the method as json encoded content. It will
+// return a patch that yields the modified document when applied to the original document, or an error
+// if either of the two documents is invalid.
+func CreateTwoWayMergePatch(original, modified []byte, dataStruct interface{}, fns ...mergepatch.PreconditionFunc) ([]byte, error) {
+ schema, err := NewPatchMetaFromStruct(dataStruct)
+ if err != nil {
+ return nil, err
+ }
+
+ return CreateTwoWayMergePatchUsingLookupPatchMeta(original, modified, schema, fns...)
+}
+
+func CreateTwoWayMergePatchUsingLookupPatchMeta(
+ original, modified []byte, schema LookupPatchMeta, fns ...mergepatch.PreconditionFunc) ([]byte, error) {
+ originalMap := map[string]interface{}{}
+ if len(original) > 0 {
+ if err := json.Unmarshal(original, &originalMap); err != nil {
+ return nil, mergepatch.ErrBadJSONDoc
+ }
+ }
+
+ modifiedMap := map[string]interface{}{}
+ if len(modified) > 0 {
+ if err := json.Unmarshal(modified, &modifiedMap); err != nil {
+ return nil, mergepatch.ErrBadJSONDoc
+ }
+ }
+
+ patchMap, err := CreateTwoWayMergeMapPatchUsingLookupPatchMeta(originalMap, modifiedMap, schema, fns...)
+ if err != nil {
+ return nil, err
+ }
+
+ return json.Marshal(patchMap)
+}
+
+// CreateTwoWayMergeMapPatch creates a patch from an original and modified JSON objects,
+// encoded JSONMap.
+// The serialized version of the map can then be passed to StrategicMergeMapPatch.
+func CreateTwoWayMergeMapPatch(original, modified JSONMap, dataStruct interface{}, fns ...mergepatch.PreconditionFunc) (JSONMap, error) {
+ schema, err := NewPatchMetaFromStruct(dataStruct)
+ if err != nil {
+ return nil, err
+ }
+
+ return CreateTwoWayMergeMapPatchUsingLookupPatchMeta(original, modified, schema, fns...)
+}
+
+func CreateTwoWayMergeMapPatchUsingLookupPatchMeta(original, modified JSONMap, schema LookupPatchMeta, fns ...mergepatch.PreconditionFunc) (JSONMap, error) {
+ diffOptions := DiffOptions{
+ SetElementOrder: true,
+ }
+ patchMap, err := diffMaps(original, modified, schema, diffOptions)
+ if err != nil {
+ return nil, err
+ }
+
+ // Apply the preconditions to the patch, and return an error if any of them fail.
+ for _, fn := range fns {
+ if !fn(patchMap) {
+ return nil, mergepatch.NewErrPreconditionFailed(patchMap)
+ }
+ }
+
+ return patchMap, nil
+}
+
+// Returns a (recursive) strategic merge patch that yields modified when applied to original.
+// Including:
+// - Adding fields to the patch present in modified, missing from original
+// - Setting fields to the patch present in modified and original with different values
+// - Delete fields present in original, missing from modified through
+// - IFF map field - set to nil in patch
+// - IFF list of maps && merge strategy - use deleteDirective for the elements
+// - IFF list of primitives && merge strategy - use parallel deletion list
+// - IFF list of maps or primitives with replace strategy (default) - set patch value to the value in modified
+// - Build $retainKeys directive for fields with retainKeys patch strategy
+func diffMaps(original, modified map[string]interface{}, schema LookupPatchMeta, diffOptions DiffOptions) (map[string]interface{}, error) {
+ patch := map[string]interface{}{}
+
+ // This will be used to build the $retainKeys directive sent in the patch
+ retainKeysList := make([]interface{}, 0, len(modified))
+
+ // Compare each value in the modified map against the value in the original map
+ for key, modifiedValue := range modified {
+ // Get the underlying type for pointers
+ if diffOptions.BuildRetainKeysDirective && modifiedValue != nil {
+ retainKeysList = append(retainKeysList, key)
+ }
+
+ originalValue, ok := original[key]
+ if !ok {
+ // Key was added, so add to patch
+ if !diffOptions.IgnoreChangesAndAdditions {
+ patch[key] = modifiedValue
+ }
+ continue
+ }
+
+ // The patch may have a patch directive
+ // TODO: figure out if we need this. This shouldn't be needed by apply. When would the original map have patch directives in it?
+ foundDirectiveMarker, err := handleDirectiveMarker(key, originalValue, modifiedValue, patch)
+ if err != nil {
+ return nil, err
+ }
+ if foundDirectiveMarker {
+ continue
+ }
+
+ if reflect.TypeOf(originalValue) != reflect.TypeOf(modifiedValue) {
+ // Types have changed, so add to patch
+ if !diffOptions.IgnoreChangesAndAdditions {
+ patch[key] = modifiedValue
+ }
+ continue
+ }
+
+ // Types are the same, so compare values
+ switch originalValueTyped := originalValue.(type) {
+ case map[string]interface{}:
+ modifiedValueTyped := modifiedValue.(map[string]interface{})
+ err = handleMapDiff(key, originalValueTyped, modifiedValueTyped, patch, schema, diffOptions)
+ case []interface{}:
+ modifiedValueTyped := modifiedValue.([]interface{})
+ err = handleSliceDiff(key, originalValueTyped, modifiedValueTyped, patch, schema, diffOptions)
+ default:
+ replacePatchFieldIfNotEqual(key, originalValue, modifiedValue, patch, diffOptions)
+ }
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ updatePatchIfMissing(original, modified, patch, diffOptions)
+ // Insert the retainKeysList iff there are values present in the retainKeysList and
+ // either of the following is true:
+ // - the patch is not empty
+ // - there are additional field in original that need to be cleared
+ if len(retainKeysList) > 0 &&
+ (len(patch) > 0 || hasAdditionalNewField(original, modified)) {
+ patch[retainKeysDirective] = sortScalars(retainKeysList)
+ }
+ return patch, nil
+}
+
+// handleDirectiveMarker handles how to diff directive marker between 2 objects
+func handleDirectiveMarker(key string, originalValue, modifiedValue interface{}, patch map[string]interface{}) (bool, error) {
+ if key == directiveMarker {
+ originalString, ok := originalValue.(string)
+ if !ok {
+ return false, fmt.Errorf("invalid value for special key: %s", directiveMarker)
+ }
+ modifiedString, ok := modifiedValue.(string)
+ if !ok {
+ return false, fmt.Errorf("invalid value for special key: %s", directiveMarker)
+ }
+ if modifiedString != originalString {
+ patch[directiveMarker] = modifiedValue
+ }
+ return true, nil
+ }
+ return false, nil
+}
+
+// handleMapDiff diff between 2 maps `originalValueTyped` and `modifiedValue`,
+// puts the diff in the `patch` associated with `key`
+// key is the key associated with originalValue and modifiedValue.
+// originalValue, modifiedValue are the old and new value respectively.They are both maps
+// patch is the patch map that contains key and the updated value, and it is the parent of originalValue, modifiedValue
+// diffOptions contains multiple options to control how we do the diff.
+func handleMapDiff(key string, originalValue, modifiedValue, patch map[string]interface{},
+ schema LookupPatchMeta, diffOptions DiffOptions) error {
+ subschema, patchMeta, err := schema.LookupPatchMetadataForStruct(key)
+
+ if err != nil {
+ // We couldn't look up metadata for the field
+ // If the values are identical, this doesn't matter, no patch is needed
+ if reflect.DeepEqual(originalValue, modifiedValue) {
+ return nil
+ }
+ // Otherwise, return the error
+ return err
+ }
+ retainKeys, patchStrategy, err := extractRetainKeysPatchStrategy(patchMeta.GetPatchStrategies())
+ if err != nil {
+ return err
+ }
+ diffOptions.BuildRetainKeysDirective = retainKeys
+ switch patchStrategy {
+ // The patch strategic from metadata tells us to replace the entire object instead of diffing it
+ case replaceDirective:
+ if !diffOptions.IgnoreChangesAndAdditions {
+ patch[key] = modifiedValue
+ }
+ default:
+ patchValue, err := diffMaps(originalValue, modifiedValue, subschema, diffOptions)
+ if err != nil {
+ return err
+ }
+ // Maps were not identical, use provided patch value
+ if len(patchValue) > 0 {
+ patch[key] = patchValue
+ }
+ }
+ return nil
+}
+
+// handleSliceDiff diff between 2 slices `originalValueTyped` and `modifiedValue`,
+// puts the diff in the `patch` associated with `key`
+// key is the key associated with originalValue and modifiedValue.
+// originalValue, modifiedValue are the old and new value respectively.They are both slices
+// patch is the patch map that contains key and the updated value, and it is the parent of originalValue, modifiedValue
+// diffOptions contains multiple options to control how we do the diff.
+func handleSliceDiff(key string, originalValue, modifiedValue []interface{}, patch map[string]interface{},
+ schema LookupPatchMeta, diffOptions DiffOptions) error {
+ subschema, patchMeta, err := schema.LookupPatchMetadataForSlice(key)
+ if err != nil {
+ // We couldn't look up metadata for the field
+ // If the values are identical, this doesn't matter, no patch is needed
+ if reflect.DeepEqual(originalValue, modifiedValue) {
+ return nil
+ }
+ // Otherwise, return the error
+ return err
+ }
+ retainKeys, patchStrategy, err := extractRetainKeysPatchStrategy(patchMeta.GetPatchStrategies())
+ if err != nil {
+ return err
+ }
+ switch patchStrategy {
+ // Merge the 2 slices using mergePatchKey
+ case mergeDirective:
+ diffOptions.BuildRetainKeysDirective = retainKeys
+ addList, deletionList, setOrderList, err := diffLists(originalValue, modifiedValue, subschema, patchMeta.GetPatchMergeKey(), diffOptions)
+ if err != nil {
+ return err
+ }
+ if len(addList) > 0 {
+ patch[key] = addList
+ }
+ // generate a parallel list for deletion
+ if len(deletionList) > 0 {
+ parallelDeletionListKey := fmt.Sprintf("%s/%s", deleteFromPrimitiveListDirectivePrefix, key)
+ patch[parallelDeletionListKey] = deletionList
+ }
+ if len(setOrderList) > 0 {
+ parallelSetOrderListKey := fmt.Sprintf("%s/%s", setElementOrderDirectivePrefix, key)
+ patch[parallelSetOrderListKey] = setOrderList
+ }
+ default:
+ replacePatchFieldIfNotEqual(key, originalValue, modifiedValue, patch, diffOptions)
+ }
+ return nil
+}
+
+// replacePatchFieldIfNotEqual updates the patch if original and modified are not deep equal
+// if diffOptions.IgnoreChangesAndAdditions is false.
+// original is the old value, maybe either the live cluster object or the last applied configuration
+// modified is the new value, is always the users new config
+func replacePatchFieldIfNotEqual(key string, original, modified interface{},
+ patch map[string]interface{}, diffOptions DiffOptions) {
+ if diffOptions.IgnoreChangesAndAdditions {
+ // Ignoring changes - do nothing
+ return
+ }
+ if reflect.DeepEqual(original, modified) {
+ // Contents are identical - do nothing
+ return
+ }
+ // Create a patch to replace the old value with the new one
+ patch[key] = modified
+}
+
+// updatePatchIfMissing iterates over `original` when ignoreDeletions is false.
+// Clear the field whose key is not present in `modified`.
+// original is the old value, maybe either the live cluster object or the last applied configuration
+// modified is the new value, is always the users new config
+func updatePatchIfMissing(original, modified, patch map[string]interface{}, diffOptions DiffOptions) {
+ if diffOptions.IgnoreDeletions {
+ // Ignoring deletion - do nothing
+ return
+ }
+ // Add nils for deleted values
+ for key := range original {
+ if _, found := modified[key]; !found {
+ patch[key] = nil
+ }
+ }
+}
+
+// validateMergeKeyInLists checks if each map in the list has the mentryerge key.
+func validateMergeKeyInLists(mergeKey string, lists ...[]interface{}) error {
+ for _, list := range lists {
+ for _, item := range list {
+ m, ok := item.(map[string]interface{})
+ if !ok {
+ return mergepatch.ErrBadArgType(m, item)
+ }
+ if _, ok = m[mergeKey]; !ok {
+ return mergepatch.ErrNoMergeKey(m, mergeKey)
+ }
+ }
+ }
+ return nil
+}
+
+// normalizeElementOrder sort `patch` list by `patchOrder` and sort `serverOnly` list by `serverOrder`.
+// Then it merges the 2 sorted lists.
+// It guarantee the relative order in the patch list and in the serverOnly list is kept.
+// `patch` is a list of items in the patch, and `serverOnly` is a list of items in the live object.
+// `patchOrder` is the order we want `patch` list to have and
+// `serverOrder` is the order we want `serverOnly` list to have.
+// kind is the kind of each item in the lists `patch` and `serverOnly`.
+func normalizeElementOrder(patch, serverOnly, patchOrder, serverOrder []interface{}, mergeKey string, kind reflect.Kind) ([]interface{}, error) {
+ patch, err := normalizeSliceOrder(patch, patchOrder, mergeKey, kind)
+ if err != nil {
+ return nil, err
+ }
+ serverOnly, err = normalizeSliceOrder(serverOnly, serverOrder, mergeKey, kind)
+ if err != nil {
+ return nil, err
+ }
+ all := mergeSortedSlice(serverOnly, patch, serverOrder, mergeKey, kind)
+
+ return all, nil
+}
+
+// mergeSortedSlice merges the 2 sorted lists by serverOrder with best effort.
+// It will insert each item in `left` list to `right` list. In most cases, the 2 lists will be interleaved.
+// The relative order of left and right are guaranteed to be kept.
+// They have higher precedence than the order in the live list.
+// The place for a item in `left` is found by:
+// scan from the place of last insertion in `right` to the end of `right`,
+// the place is before the first item that is greater than the item we want to insert.
+// example usage: using server-only items as left and patch items as right. We insert server-only items
+// to patch list. We use the order of live object as record for comparison.
+func mergeSortedSlice(left, right, serverOrder []interface{}, mergeKey string, kind reflect.Kind) []interface{} {
+ // Returns if l is less than r, and if both have been found.
+ // If l and r both present and l is in front of r, l is less than r.
+ less := func(l, r interface{}) (bool, bool) {
+ li := index(serverOrder, l, mergeKey, kind)
+ ri := index(serverOrder, r, mergeKey, kind)
+ if li >= 0 && ri >= 0 {
+ return li < ri, true
+ } else {
+ return false, false
+ }
+ }
+
+ // left and right should be non-overlapping.
+ size := len(left) + len(right)
+ i, j := 0, 0
+ s := make([]interface{}, size, size)
+
+ for k := 0; k < size; k++ {
+ if i >= len(left) && j < len(right) {
+ // have items left in `right` list
+ s[k] = right[j]
+ j++
+ } else if j >= len(right) && i < len(left) {
+ // have items left in `left` list
+ s[k] = left[i]
+ i++
+ } else {
+ // compare them if i and j are both in bound
+ less, foundBoth := less(left[i], right[j])
+ if foundBoth && less {
+ s[k] = left[i]
+ i++
+ } else {
+ s[k] = right[j]
+ j++
+ }
+ }
+ }
+ return s
+}
+
+// index returns the index of the item in the given items, or -1 if it doesn't exist
+// l must NOT be a slice of slices, this should be checked before calling.
+func index(l []interface{}, valToLookUp interface{}, mergeKey string, kind reflect.Kind) int {
+ var getValFn func(interface{}) interface{}
+ // Get the correct `getValFn` based on item `kind`.
+ // It should return the value of merge key for maps and
+ // return the item for other kinds.
+ switch kind {
+ case reflect.Map:
+ getValFn = func(item interface{}) interface{} {
+ typedItem, ok := item.(map[string]interface{})
+ if !ok {
+ return nil
+ }
+ val := typedItem[mergeKey]
+ return val
+ }
+ default:
+ getValFn = func(item interface{}) interface{} {
+ return item
+ }
+ }
+
+ for i, v := range l {
+ if getValFn(valToLookUp) == getValFn(v) {
+ return i
+ }
+ }
+ return -1
+}
+
+// extractToDeleteItems takes a list and
+// returns 2 lists: one contains items that should be kept and the other contains items to be deleted.
+func extractToDeleteItems(l []interface{}) ([]interface{}, []interface{}, error) {
+ var nonDelete, toDelete []interface{}
+ for _, v := range l {
+ m, ok := v.(map[string]interface{})
+ if !ok {
+ return nil, nil, mergepatch.ErrBadArgType(m, v)
+ }
+
+ directive, foundDirective := m[directiveMarker]
+ if foundDirective && directive == deleteDirective {
+ toDelete = append(toDelete, v)
+ } else {
+ nonDelete = append(nonDelete, v)
+ }
+ }
+ return nonDelete, toDelete, nil
+}
+
+// normalizeSliceOrder sort `toSort` list by `order`
+func normalizeSliceOrder(toSort, order []interface{}, mergeKey string, kind reflect.Kind) ([]interface{}, error) {
+ var toDelete []interface{}
+ if kind == reflect.Map {
+ // make sure each item in toSort, order has merge key
+ err := validateMergeKeyInLists(mergeKey, toSort, order)
+ if err != nil {
+ return nil, err
+ }
+ toSort, toDelete, err = extractToDeleteItems(toSort)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ sort.SliceStable(toSort, func(i, j int) bool {
+ if ii := index(order, toSort[i], mergeKey, kind); ii >= 0 {
+ if ij := index(order, toSort[j], mergeKey, kind); ij >= 0 {
+ return ii < ij
+ }
+ }
+ return true
+ })
+ toSort = append(toSort, toDelete...)
+ return toSort, nil
+}
+
+// Returns a (recursive) strategic merge patch, a parallel deletion list if necessary and
+// another list to set the order of the list
+// Only list of primitives with merge strategy will generate a parallel deletion list.
+// These two lists should yield modified when applied to original, for lists with merge semantics.
+func diffLists(original, modified []interface{}, schema LookupPatchMeta, mergeKey string, diffOptions DiffOptions) ([]interface{}, []interface{}, []interface{}, error) {
+ if len(original) == 0 {
+ // Both slices are empty - do nothing
+ if len(modified) == 0 || diffOptions.IgnoreChangesAndAdditions {
+ return nil, nil, nil, nil
+ }
+
+ // Old slice was empty - add all elements from the new slice
+ return modified, nil, nil, nil
+ }
+
+ elementType, err := sliceElementType(original, modified)
+ if err != nil {
+ return nil, nil, nil, err
+ }
+
+ var patchList, deleteList, setOrderList []interface{}
+ kind := elementType.Kind()
+ switch kind {
+ case reflect.Map:
+ patchList, deleteList, err = diffListsOfMaps(original, modified, schema, mergeKey, diffOptions)
+ if err != nil {
+ return nil, nil, nil, err
+ }
+ patchList, err = normalizeSliceOrder(patchList, modified, mergeKey, kind)
+ if err != nil {
+ return nil, nil, nil, err
+ }
+ orderSame, err := isOrderSame(original, modified, mergeKey)
+ if err != nil {
+ return nil, nil, nil, err
+ }
+ // append the deletions to the end of the patch list.
+ patchList = append(patchList, deleteList...)
+ deleteList = nil
+ // generate the setElementOrder list when there are content changes or order changes
+ if diffOptions.SetElementOrder &&
+ ((!diffOptions.IgnoreChangesAndAdditions && (len(patchList) > 0 || !orderSame)) ||
+ (!diffOptions.IgnoreDeletions && len(patchList) > 0)) {
+ // Generate a list of maps that each item contains only the merge key.
+ setOrderList = make([]interface{}, len(modified))
+ for i, v := range modified {
+ typedV := v.(map[string]interface{})
+ setOrderList[i] = map[string]interface{}{
+ mergeKey: typedV[mergeKey],
+ }
+ }
+ }
+ case reflect.Slice:
+ // Lists of Lists are not permitted by the api
+ return nil, nil, nil, mergepatch.ErrNoListOfLists
+ default:
+ patchList, deleteList, err = diffListsOfScalars(original, modified, diffOptions)
+ if err != nil {
+ return nil, nil, nil, err
+ }
+ patchList, err = normalizeSliceOrder(patchList, modified, mergeKey, kind)
+ // generate the setElementOrder list when there are content changes or order changes
+ if diffOptions.SetElementOrder && ((!diffOptions.IgnoreDeletions && len(deleteList) > 0) ||
+ (!diffOptions.IgnoreChangesAndAdditions && !reflect.DeepEqual(original, modified))) {
+ setOrderList = modified
+ }
+ }
+ return patchList, deleteList, setOrderList, err
+}
+
+// isOrderSame checks if the order in a list has changed
+func isOrderSame(original, modified []interface{}, mergeKey string) (bool, error) {
+ if len(original) != len(modified) {
+ return false, nil
+ }
+ for i, modifiedItem := range modified {
+ equal, err := mergeKeyValueEqual(original[i], modifiedItem, mergeKey)
+ if err != nil || !equal {
+ return equal, err
+ }
+ }
+ return true, nil
+}
+
+// diffListsOfScalars returns 2 lists, the first one is addList and the second one is deletionList.
+// Argument diffOptions.IgnoreChangesAndAdditions controls if calculate addList. true means not calculate.
+// Argument diffOptions.IgnoreDeletions controls if calculate deletionList. true means not calculate.
+// original may be changed, but modified is guaranteed to not be changed
+func diffListsOfScalars(original, modified []interface{}, diffOptions DiffOptions) ([]interface{}, []interface{}, error) {
+ modifiedCopy := make([]interface{}, len(modified))
+ copy(modifiedCopy, modified)
+ // Sort the scalars for easier calculating the diff
+ originalScalars := sortScalars(original)
+ modifiedScalars := sortScalars(modifiedCopy)
+
+ originalIndex, modifiedIndex := 0, 0
+ addList := []interface{}{}
+ deletionList := []interface{}{}
+
+ for {
+ originalInBounds := originalIndex < len(originalScalars)
+ modifiedInBounds := modifiedIndex < len(modifiedScalars)
+ if !originalInBounds && !modifiedInBounds {
+ break
+ }
+ // we need to compare the string representation of the scalar,
+ // because the scalar is an interface which doesn't support either < or >
+ // And that's how func sortScalars compare scalars.
+ var originalString, modifiedString string
+ var originalValue, modifiedValue interface{}
+ if originalInBounds {
+ originalValue = originalScalars[originalIndex]
+ originalString = fmt.Sprintf("%v", originalValue)
+ }
+ if modifiedInBounds {
+ modifiedValue = modifiedScalars[modifiedIndex]
+ modifiedString = fmt.Sprintf("%v", modifiedValue)
+ }
+
+ originalV, modifiedV := compareListValuesAtIndex(originalInBounds, modifiedInBounds, originalString, modifiedString)
+ switch {
+ case originalV == nil && modifiedV == nil:
+ originalIndex++
+ modifiedIndex++
+ case originalV != nil && modifiedV == nil:
+ if !diffOptions.IgnoreDeletions {
+ deletionList = append(deletionList, originalValue)
+ }
+ originalIndex++
+ case originalV == nil && modifiedV != nil:
+ if !diffOptions.IgnoreChangesAndAdditions {
+ addList = append(addList, modifiedValue)
+ }
+ modifiedIndex++
+ default:
+ return nil, nil, fmt.Errorf("Unexpected returned value from compareListValuesAtIndex: %v and %v", originalV, modifiedV)
+ }
+ }
+
+ return addList, deduplicateScalars(deletionList), nil
+}
+
+// If first return value is non-nil, list1 contains an element not present in list2
+// If second return value is non-nil, list2 contains an element not present in list1
+func compareListValuesAtIndex(list1Inbounds, list2Inbounds bool, list1Value, list2Value string) (interface{}, interface{}) {
+ bothInBounds := list1Inbounds && list2Inbounds
+ switch {
+ // scalars are identical
+ case bothInBounds && list1Value == list2Value:
+ return nil, nil
+ // only list2 is in bound
+ case !list1Inbounds:
+ fallthrough
+ // list2 has additional scalar
+ case bothInBounds && list1Value > list2Value:
+ return nil, list2Value
+ // only original is in bound
+ case !list2Inbounds:
+ fallthrough
+ // original has additional scalar
+ case bothInBounds && list1Value < list2Value:
+ return list1Value, nil
+ default:
+ return nil, nil
+ }
+}
+
+// diffListsOfMaps takes a pair of lists and
+// returns a (recursive) strategic merge patch list contains additions and changes and
+// a deletion list contains deletions
+func diffListsOfMaps(original, modified []interface{}, schema LookupPatchMeta, mergeKey string, diffOptions DiffOptions) ([]interface{}, []interface{}, error) {
+ patch := make([]interface{}, 0, len(modified))
+ deletionList := make([]interface{}, 0, len(original))
+
+ originalSorted, err := sortMergeListsByNameArray(original, schema, mergeKey, false)
+ if err != nil {
+ return nil, nil, err
+ }
+ modifiedSorted, err := sortMergeListsByNameArray(modified, schema, mergeKey, false)
+ if err != nil {
+ return nil, nil, err
+ }
+
+ originalIndex, modifiedIndex := 0, 0
+ for {
+ originalInBounds := originalIndex < len(originalSorted)
+ modifiedInBounds := modifiedIndex < len(modifiedSorted)
+ bothInBounds := originalInBounds && modifiedInBounds
+ if !originalInBounds && !modifiedInBounds {
+ break
+ }
+
+ var originalElementMergeKeyValueString, modifiedElementMergeKeyValueString string
+ var originalElementMergeKeyValue, modifiedElementMergeKeyValue interface{}
+ var originalElement, modifiedElement map[string]interface{}
+ if originalInBounds {
+ originalElement, originalElementMergeKeyValue, err = getMapAndMergeKeyValueByIndex(originalIndex, mergeKey, originalSorted)
+ if err != nil {
+ return nil, nil, err
+ }
+ originalElementMergeKeyValueString = fmt.Sprintf("%v", originalElementMergeKeyValue)
+ }
+ if modifiedInBounds {
+ modifiedElement, modifiedElementMergeKeyValue, err = getMapAndMergeKeyValueByIndex(modifiedIndex, mergeKey, modifiedSorted)
+ if err != nil {
+ return nil, nil, err
+ }
+ modifiedElementMergeKeyValueString = fmt.Sprintf("%v", modifiedElementMergeKeyValue)
+ }
+
+ switch {
+ case bothInBounds && ItemMatchesOriginalAndModifiedSlice(originalElementMergeKeyValueString, modifiedElementMergeKeyValueString):
+ // Merge key values are equal, so recurse
+ patchValue, err := diffMaps(originalElement, modifiedElement, schema, diffOptions)
+ if err != nil {
+ return nil, nil, err
+ }
+ if len(patchValue) > 0 {
+ patchValue[mergeKey] = modifiedElementMergeKeyValue
+ patch = append(patch, patchValue)
+ }
+ originalIndex++
+ modifiedIndex++
+ // only modified is in bound
+ case !originalInBounds:
+ fallthrough
+ // modified has additional map
+ case bothInBounds && ItemAddedToModifiedSlice(originalElementMergeKeyValueString, modifiedElementMergeKeyValueString):
+ if !diffOptions.IgnoreChangesAndAdditions {
+ patch = append(patch, modifiedElement)
+ }
+ modifiedIndex++
+ // only original is in bound
+ case !modifiedInBounds:
+ fallthrough
+ // original has additional map
+ case bothInBounds && ItemRemovedFromModifiedSlice(originalElementMergeKeyValueString, modifiedElementMergeKeyValueString):
+ if !diffOptions.IgnoreDeletions {
+ // Item was deleted, so add delete directive
+ deletionList = append(deletionList, CreateDeleteDirective(mergeKey, originalElementMergeKeyValue))
+ }
+ originalIndex++
+ }
+ }
+
+ return patch, deletionList, nil
+}
+
+// getMapAndMergeKeyValueByIndex return a map in the list and its merge key value given the index of the map.
+func getMapAndMergeKeyValueByIndex(index int, mergeKey string, listOfMaps []interface{}) (map[string]interface{}, interface{}, error) {
+ m, ok := listOfMaps[index].(map[string]interface{})
+ if !ok {
+ return nil, nil, mergepatch.ErrBadArgType(m, listOfMaps[index])
+ }
+
+ val, ok := m[mergeKey]
+ if !ok {
+ return nil, nil, mergepatch.ErrNoMergeKey(m, mergeKey)
+ }
+ return m, val, nil
+}
+
+// StrategicMergePatch applies a strategic merge patch. The patch and the original document
+// must be json encoded content. A patch can be created from an original and a modified document
+// by calling CreateStrategicMergePatch.
+func StrategicMergePatch(original, patch []byte, dataStruct interface{}) ([]byte, error) {
+ schema, err := NewPatchMetaFromStruct(dataStruct)
+ if err != nil {
+ return nil, err
+ }
+
+ return StrategicMergePatchUsingLookupPatchMeta(original, patch, schema)
+}
+
+func StrategicMergePatchUsingLookupPatchMeta(original, patch []byte, schema LookupPatchMeta) ([]byte, error) {
+ originalMap, err := handleUnmarshal(original)
+ if err != nil {
+ return nil, err
+ }
+ patchMap, err := handleUnmarshal(patch)
+ if err != nil {
+ return nil, err
+ }
+
+ result, err := StrategicMergeMapPatchUsingLookupPatchMeta(originalMap, patchMap, schema)
+ if err != nil {
+ return nil, err
+ }
+
+ return json.Marshal(result)
+}
+
+func handleUnmarshal(j []byte) (map[string]interface{}, error) {
+ if j == nil {
+ j = []byte("{}")
+ }
+
+ m := map[string]interface{}{}
+ err := json.Unmarshal(j, &m)
+ if err != nil {
+ return nil, mergepatch.ErrBadJSONDoc
+ }
+ return m, nil
+}
+
+// StrategicMergeMapPatch applies a strategic merge patch. The original and patch documents
+// must be JSONMap. A patch can be created from an original and modified document by
+// calling CreateTwoWayMergeMapPatch.
+// Warning: the original and patch JSONMap objects are mutated by this function and should not be reused.
+func StrategicMergeMapPatch(original, patch JSONMap, dataStruct interface{}) (JSONMap, error) {
+ schema, err := NewPatchMetaFromStruct(dataStruct)
+ if err != nil {
+ return nil, err
+ }
+
+ // We need the go struct tags `patchMergeKey` and `patchStrategy` for fields that support a strategic merge patch.
+ // For native resources, we can easily figure out these tags since we know the fields.
+
+ // Because custom resources are decoded as Unstructured and because we're missing the metadata about how to handle
+ // each field in a strategic merge patch, we can't find the go struct tags. Hence, we can't easily do a strategic merge
+ // for custom resources. So we should fail fast and return an error.
+ if _, ok := dataStruct.(*unstructured.Unstructured); ok {
+ return nil, mergepatch.ErrUnsupportedStrategicMergePatchFormat
+ }
+
+ return StrategicMergeMapPatchUsingLookupPatchMeta(original, patch, schema)
+}
+
+func StrategicMergeMapPatchUsingLookupPatchMeta(original, patch JSONMap, schema LookupPatchMeta) (JSONMap, error) {
+ mergeOptions := MergeOptions{
+ MergeParallelList: true,
+ IgnoreUnmatchedNulls: true,
+ }
+ return mergeMap(original, patch, schema, mergeOptions)
+}
+
+// MergeStrategicMergeMapPatchUsingLookupPatchMeta merges strategic merge
+// patches retaining `null` fields and parallel lists. If 2 patches change the
+// same fields and the latter one will override the former one. If you don't
+// want that happen, you need to run func MergingMapsHaveConflicts before
+// merging these patches. Applying the resulting merged merge patch to a JSONMap
+// yields the same as merging each strategic merge patch to the JSONMap in
+// succession.
+func MergeStrategicMergeMapPatchUsingLookupPatchMeta(schema LookupPatchMeta, patches ...JSONMap) (JSONMap, error) {
+ mergeOptions := MergeOptions{
+ MergeParallelList: false,
+ IgnoreUnmatchedNulls: false,
+ }
+ merged := JSONMap{}
+ var err error
+ for _, patch := range patches {
+ merged, err = mergeMap(merged, patch, schema, mergeOptions)
+ if err != nil {
+ return nil, err
+ }
+ }
+ return merged, nil
+}
+
+// handleDirectiveInMergeMap handles the patch directive when merging 2 maps.
+func handleDirectiveInMergeMap(directive interface{}, patch map[string]interface{}) (map[string]interface{}, error) {
+ if directive == replaceDirective {
+ // If the patch contains "$patch: replace", don't merge it, just use the
+ // patch directly. Later on, we can add a single level replace that only
+ // affects the map that the $patch is in.
+ delete(patch, directiveMarker)
+ return patch, nil
+ }
+
+ if directive == deleteDirective {
+ // If the patch contains "$patch: delete", don't merge it, just return
+ // an empty map.
+ return map[string]interface{}{}, nil
+ }
+
+ return nil, mergepatch.ErrBadPatchType(directive, patch)
+}
+
+func containsDirectiveMarker(item interface{}) bool {
+ m, ok := item.(map[string]interface{})
+ if ok {
+ if _, foundDirectiveMarker := m[directiveMarker]; foundDirectiveMarker {
+ return true
+ }
+ }
+ return false
+}
+
+func mergeKeyValueEqual(left, right interface{}, mergeKey string) (bool, error) {
+ if len(mergeKey) == 0 {
+ return left == right, nil
+ }
+ typedLeft, ok := left.(map[string]interface{})
+ if !ok {
+ return false, mergepatch.ErrBadArgType(typedLeft, left)
+ }
+ typedRight, ok := right.(map[string]interface{})
+ if !ok {
+ return false, mergepatch.ErrBadArgType(typedRight, right)
+ }
+ mergeKeyLeft, ok := typedLeft[mergeKey]
+ if !ok {
+ return false, mergepatch.ErrNoMergeKey(typedLeft, mergeKey)
+ }
+ mergeKeyRight, ok := typedRight[mergeKey]
+ if !ok {
+ return false, mergepatch.ErrNoMergeKey(typedRight, mergeKey)
+ }
+ return mergeKeyLeft == mergeKeyRight, nil
+}
+
+// extractKey trims the prefix and return the original key
+func extractKey(s, prefix string) (string, error) {
+ substrings := strings.SplitN(s, "/", 2)
+ if len(substrings) <= 1 || substrings[0] != prefix {
+ switch prefix {
+ case deleteFromPrimitiveListDirectivePrefix:
+ return "", mergepatch.ErrBadPatchFormatForPrimitiveList
+ case setElementOrderDirectivePrefix:
+ return "", mergepatch.ErrBadPatchFormatForSetElementOrderList
+ default:
+ return "", fmt.Errorf("fail to find unknown prefix %q in %s\n", prefix, s)
+ }
+ }
+ return substrings[1], nil
+}
+
+// validatePatchUsingSetOrderList verifies:
+// the relative order of any two items in the setOrderList list matches that in the patch list.
+// the items in the patch list must be a subset or the same as the $setElementOrder list (deletions are ignored).
+func validatePatchWithSetOrderList(patchList, setOrderList interface{}, mergeKey string) error {
+ typedSetOrderList, ok := setOrderList.([]interface{})
+ if !ok {
+ return mergepatch.ErrBadPatchFormatForSetElementOrderList
+ }
+ typedPatchList, ok := patchList.([]interface{})
+ if !ok {
+ return mergepatch.ErrBadPatchFormatForSetElementOrderList
+ }
+ if len(typedSetOrderList) == 0 || len(typedPatchList) == 0 {
+ return nil
+ }
+
+ var nonDeleteList, toDeleteList []interface{}
+ var err error
+ if len(mergeKey) > 0 {
+ nonDeleteList, toDeleteList, err = extractToDeleteItems(typedPatchList)
+ if err != nil {
+ return err
+ }
+ } else {
+ nonDeleteList = typedPatchList
+ }
+
+ patchIndex, setOrderIndex := 0, 0
+ for patchIndex < len(nonDeleteList) && setOrderIndex < len(typedSetOrderList) {
+ if containsDirectiveMarker(nonDeleteList[patchIndex]) {
+ patchIndex++
+ continue
+ }
+ mergeKeyEqual, err := mergeKeyValueEqual(nonDeleteList[patchIndex], typedSetOrderList[setOrderIndex], mergeKey)
+ if err != nil {
+ return err
+ }
+ if mergeKeyEqual {
+ patchIndex++
+ }
+ setOrderIndex++
+ }
+ // If patchIndex is inbound but setOrderIndex if out of bound mean there are items mismatching between the patch list and setElementOrder list.
+ // the second check is is a sanity check, and should always be true if the first is true.
+ if patchIndex < len(nonDeleteList) && setOrderIndex >= len(typedSetOrderList) {
+ return fmt.Errorf("The order in patch list:\n%v\n doesn't match %s list:\n%v\n", typedPatchList, setElementOrderDirectivePrefix, setOrderList)
+ }
+ typedPatchList = append(nonDeleteList, toDeleteList...)
+ return nil
+}
+
+// preprocessDeletionListForMerging preprocesses the deletion list.
+// it returns shouldContinue, isDeletionList, noPrefixKey
+func preprocessDeletionListForMerging(key string, original map[string]interface{},
+ patchVal interface{}, mergeDeletionList bool) (bool, bool, string, error) {
+ // If found a parallel list for deletion and we are going to merge the list,
+ // overwrite the key to the original key and set flag isDeleteList
+ foundParallelListPrefix := strings.HasPrefix(key, deleteFromPrimitiveListDirectivePrefix)
+ if foundParallelListPrefix {
+ if !mergeDeletionList {
+ original[key] = patchVal
+ return true, false, "", nil
+ }
+ originalKey, err := extractKey(key, deleteFromPrimitiveListDirectivePrefix)
+ return false, true, originalKey, err
+ }
+ return false, false, "", nil
+}
+
+// applyRetainKeysDirective looks for a retainKeys directive and applies to original
+// - if no directive exists do nothing
+// - if directive is found, clear keys in original missing from the directive list
+// - validate that all keys present in the patch are present in the retainKeys directive
+// note: original may be another patch request, e.g. applying the add+modified patch to the deletions patch. In this case it may have directives
+func applyRetainKeysDirective(original, patch map[string]interface{}, options MergeOptions) error {
+ retainKeysInPatch, foundInPatch := patch[retainKeysDirective]
+ if !foundInPatch {
+ return nil
+ }
+ // cleanup the directive
+ delete(patch, retainKeysDirective)
+
+ if !options.MergeParallelList {
+ // If original is actually a patch, make sure the retainKeys directives are the same in both patches if present in both.
+ // If not present in the original patch, copy from the modified patch.
+ retainKeysInOriginal, foundInOriginal := original[retainKeysDirective]
+ if foundInOriginal {
+ if !reflect.DeepEqual(retainKeysInOriginal, retainKeysInPatch) {
+ // This error actually should never happen.
+ return fmt.Errorf("%v and %v are not deep equal: this may happen when calculating the 3-way diff patch", retainKeysInOriginal, retainKeysInPatch)
+ }
+ } else {
+ original[retainKeysDirective] = retainKeysInPatch
+ }
+ return nil
+ }
+
+ retainKeysList, ok := retainKeysInPatch.([]interface{})
+ if !ok {
+ return mergepatch.ErrBadPatchFormatForRetainKeys
+ }
+
+ // validate patch to make sure all fields in the patch are present in the retainKeysList.
+ // The map is used only as a set, the value is never referenced
+ m := map[interface{}]struct{}{}
+ for _, v := range retainKeysList {
+ m[v] = struct{}{}
+ }
+ for k, v := range patch {
+ if v == nil || strings.HasPrefix(k, deleteFromPrimitiveListDirectivePrefix) ||
+ strings.HasPrefix(k, setElementOrderDirectivePrefix) {
+ continue
+ }
+ // If there is an item present in the patch but not in the retainKeys list,
+ // the patch is invalid.
+ if _, found := m[k]; !found {
+ return mergepatch.ErrBadPatchFormatForRetainKeys
+ }
+ }
+
+ // clear not present fields
+ for k := range original {
+ if _, found := m[k]; !found {
+ delete(original, k)
+ }
+ }
+ return nil
+}
+
+// mergePatchIntoOriginal processes $setElementOrder list.
+// When not merging the directive, it will make sure $setElementOrder list exist only in original.
+// When merging the directive, it will try to find the $setElementOrder list and
+// its corresponding patch list, validate it and merge it.
+// Then, sort them by the relative order in setElementOrder, patch list and live list.
+// The precedence is $setElementOrder > order in patch list > order in live list.
+// This function will delete the item after merging it to prevent process it again in the future.
+// Ref: https://git.k8s.io/community/contributors/design-proposals/cli/preserve-order-in-strategic-merge-patch.md
+func mergePatchIntoOriginal(original, patch map[string]interface{}, schema LookupPatchMeta, mergeOptions MergeOptions) error {
+ for key, patchV := range patch {
+ // Do nothing if there is no ordering directive
+ if !strings.HasPrefix(key, setElementOrderDirectivePrefix) {
+ continue
+ }
+
+ setElementOrderInPatch := patchV
+ // Copies directive from the second patch (`patch`) to the first patch (`original`)
+ // and checks they are equal and delete the directive in the second patch
+ if !mergeOptions.MergeParallelList {
+ setElementOrderListInOriginal, ok := original[key]
+ if ok {
+ // check if the setElementOrder list in original and the one in patch matches
+ if !reflect.DeepEqual(setElementOrderListInOriginal, setElementOrderInPatch) {
+ return mergepatch.ErrBadPatchFormatForSetElementOrderList
+ }
+ } else {
+ // move the setElementOrder list from patch to original
+ original[key] = setElementOrderInPatch
+ }
+ }
+ delete(patch, key)
+
+ var (
+ ok bool
+ originalFieldValue, patchFieldValue, merged []interface{}
+ patchStrategy string
+ patchMeta PatchMeta
+ subschema LookupPatchMeta
+ )
+ typedSetElementOrderList, ok := setElementOrderInPatch.([]interface{})
+ if !ok {
+ return mergepatch.ErrBadArgType(typedSetElementOrderList, setElementOrderInPatch)
+ }
+ // Trim the setElementOrderDirectivePrefix to get the key of the list field in original.
+ originalKey, err := extractKey(key, setElementOrderDirectivePrefix)
+ if err != nil {
+ return err
+ }
+ // try to find the list with `originalKey` in `original` and `modified` and merge them.
+ originalList, foundOriginal := original[originalKey]
+ patchList, foundPatch := patch[originalKey]
+ if foundOriginal {
+ originalFieldValue, ok = originalList.([]interface{})
+ if !ok {
+ return mergepatch.ErrBadArgType(originalFieldValue, originalList)
+ }
+ }
+ if foundPatch {
+ patchFieldValue, ok = patchList.([]interface{})
+ if !ok {
+ return mergepatch.ErrBadArgType(patchFieldValue, patchList)
+ }
+ }
+ subschema, patchMeta, err = schema.LookupPatchMetadataForSlice(originalKey)
+ if err != nil {
+ return err
+ }
+ _, patchStrategy, err = extractRetainKeysPatchStrategy(patchMeta.GetPatchStrategies())
+ if err != nil {
+ return err
+ }
+ // Check for consistency between the element order list and the field it applies to
+ err = validatePatchWithSetOrderList(patchFieldValue, typedSetElementOrderList, patchMeta.GetPatchMergeKey())
+ if err != nil {
+ return err
+ }
+
+ switch {
+ case foundOriginal && !foundPatch:
+ // no change to list contents
+ merged = originalFieldValue
+ case !foundOriginal && foundPatch:
+ // list was added
+ merged = patchFieldValue
+ case foundOriginal && foundPatch:
+ merged, err = mergeSliceHandler(originalList, patchList, subschema,
+ patchStrategy, patchMeta.GetPatchMergeKey(), false, mergeOptions)
+ if err != nil {
+ return err
+ }
+ case !foundOriginal && !foundPatch:
+ continue
+ }
+
+ // Split all items into patch items and server-only items and then enforce the order.
+ var patchItems, serverOnlyItems []interface{}
+ if len(patchMeta.GetPatchMergeKey()) == 0 {
+ // Primitives doesn't need merge key to do partitioning.
+ patchItems, serverOnlyItems = partitionPrimitivesByPresentInList(merged, typedSetElementOrderList)
+
+ } else {
+ // Maps need merge key to do partitioning.
+ patchItems, serverOnlyItems, err = partitionMapsByPresentInList(merged, typedSetElementOrderList, patchMeta.GetPatchMergeKey())
+ if err != nil {
+ return err
+ }
+ }
+
+ elementType, err := sliceElementType(originalFieldValue, patchFieldValue)
+ if err != nil {
+ return err
+ }
+ kind := elementType.Kind()
+ // normalize merged list
+ // typedSetElementOrderList contains all the relative order in typedPatchList,
+ // so don't need to use typedPatchList
+ both, err := normalizeElementOrder(patchItems, serverOnlyItems, typedSetElementOrderList, originalFieldValue, patchMeta.GetPatchMergeKey(), kind)
+ if err != nil {
+ return err
+ }
+ original[originalKey] = both
+ // delete patch list from patch to prevent process again in the future
+ delete(patch, originalKey)
+ }
+ return nil
+}
+
+// partitionPrimitivesByPresentInList partitions elements into 2 slices, the first containing items present in partitionBy, the other not.
+func partitionPrimitivesByPresentInList(original, partitionBy []interface{}) ([]interface{}, []interface{}) {
+ patch := make([]interface{}, 0, len(original))
+ serverOnly := make([]interface{}, 0, len(original))
+ inPatch := map[interface{}]bool{}
+ for _, v := range partitionBy {
+ inPatch[v] = true
+ }
+ for _, v := range original {
+ if !inPatch[v] {
+ serverOnly = append(serverOnly, v)
+ } else {
+ patch = append(patch, v)
+ }
+ }
+ return patch, serverOnly
+}
+
+// partitionMapsByPresentInList partitions elements into 2 slices, the first containing items present in partitionBy, the other not.
+func partitionMapsByPresentInList(original, partitionBy []interface{}, mergeKey string) ([]interface{}, []interface{}, error) {
+ patch := make([]interface{}, 0, len(original))
+ serverOnly := make([]interface{}, 0, len(original))
+ for _, v := range original {
+ typedV, ok := v.(map[string]interface{})
+ if !ok {
+ return nil, nil, mergepatch.ErrBadArgType(typedV, v)
+ }
+ mergeKeyValue, foundMergeKey := typedV[mergeKey]
+ if !foundMergeKey {
+ return nil, nil, mergepatch.ErrNoMergeKey(typedV, mergeKey)
+ }
+ _, _, found, err := findMapInSliceBasedOnKeyValue(partitionBy, mergeKey, mergeKeyValue)
+ if err != nil {
+ return nil, nil, err
+ }
+ if !found {
+ serverOnly = append(serverOnly, v)
+ } else {
+ patch = append(patch, v)
+ }
+ }
+ return patch, serverOnly, nil
+}
+
+// Merge fields from a patch map into the original map. Note: This may modify
+// both the original map and the patch because getting a deep copy of a map in
+// golang is highly non-trivial.
+// flag mergeOptions.MergeParallelList controls if using the parallel list to delete or keeping the list.
+// If patch contains any null field (e.g. field_1: null) that is not
+// present in original, then to propagate it to the end result use
+// mergeOptions.IgnoreUnmatchedNulls == false.
+func mergeMap(original, patch map[string]interface{}, schema LookupPatchMeta, mergeOptions MergeOptions) (map[string]interface{}, error) {
+ if v, ok := patch[directiveMarker]; ok {
+ return handleDirectiveInMergeMap(v, patch)
+ }
+
+ // nil is an accepted value for original to simplify logic in other places.
+ // If original is nil, replace it with an empty map and then apply the patch.
+ if original == nil {
+ original = map[string]interface{}{}
+ }
+
+ err := applyRetainKeysDirective(original, patch, mergeOptions)
+ if err != nil {
+ return nil, err
+ }
+
+ // Process $setElementOrder list and other lists sharing the same key.
+ // When not merging the directive, it will make sure $setElementOrder list exist only in original.
+ // When merging the directive, it will process $setElementOrder and its patch list together.
+ // This function will delete the merged elements from patch so they will not be reprocessed
+ err = mergePatchIntoOriginal(original, patch, schema, mergeOptions)
+ if err != nil {
+ return nil, err
+ }
+
+ // Start merging the patch into the original.
+ for k, patchV := range patch {
+ skipProcessing, isDeleteList, noPrefixKey, err := preprocessDeletionListForMerging(k, original, patchV, mergeOptions.MergeParallelList)
+ if err != nil {
+ return nil, err
+ }
+ if skipProcessing {
+ continue
+ }
+ if len(noPrefixKey) > 0 {
+ k = noPrefixKey
+ }
+
+ // If the value of this key is null, delete the key if it exists in the
+ // original. Otherwise, check if we want to preserve it or skip it.
+ // Preserving the null value is useful when we want to send an explicit
+ // delete to the API server.
+ if patchV == nil {
+ if _, ok := original[k]; ok {
+ delete(original, k)
+ }
+ if mergeOptions.IgnoreUnmatchedNulls {
+ continue
+ }
+ }
+
+ _, ok := original[k]
+ if !ok {
+ // If it's not in the original document, just take the patch value.
+ original[k] = patchV
+ continue
+ }
+
+ originalType := reflect.TypeOf(original[k])
+ patchType := reflect.TypeOf(patchV)
+ if originalType != patchType {
+ original[k] = patchV
+ continue
+ }
+ // If they're both maps or lists, recurse into the value.
+ switch originalType.Kind() {
+ case reflect.Map:
+ subschema, patchMeta, err2 := schema.LookupPatchMetadataForStruct(k)
+ if err2 != nil {
+ return nil, err2
+ }
+ _, patchStrategy, err2 := extractRetainKeysPatchStrategy(patchMeta.GetPatchStrategies())
+ if err2 != nil {
+ return nil, err2
+ }
+ original[k], err = mergeMapHandler(original[k], patchV, subschema, patchStrategy, mergeOptions)
+ case reflect.Slice:
+ subschema, patchMeta, err2 := schema.LookupPatchMetadataForSlice(k)
+ if err2 != nil {
+ return nil, err2
+ }
+ _, patchStrategy, err2 := extractRetainKeysPatchStrategy(patchMeta.GetPatchStrategies())
+ if err2 != nil {
+ return nil, err2
+ }
+ original[k], err = mergeSliceHandler(original[k], patchV, subschema, patchStrategy, patchMeta.GetPatchMergeKey(), isDeleteList, mergeOptions)
+ default:
+ original[k] = patchV
+ }
+ if err != nil {
+ return nil, err
+ }
+ }
+ return original, nil
+}
+
+// mergeMapHandler handles how to merge `patchV` whose key is `key` with `original` respecting
+// fieldPatchStrategy and mergeOptions.
+func mergeMapHandler(original, patch interface{}, schema LookupPatchMeta,
+ fieldPatchStrategy string, mergeOptions MergeOptions) (map[string]interface{}, error) {
+ typedOriginal, typedPatch, err := mapTypeAssertion(original, patch)
+ if err != nil {
+ return nil, err
+ }
+
+ if fieldPatchStrategy != replaceDirective {
+ return mergeMap(typedOriginal, typedPatch, schema, mergeOptions)
+ } else {
+ return typedPatch, nil
+ }
+}
+
+// mergeSliceHandler handles how to merge `patchV` whose key is `key` with `original` respecting
+// fieldPatchStrategy, fieldPatchMergeKey, isDeleteList and mergeOptions.
+func mergeSliceHandler(original, patch interface{}, schema LookupPatchMeta,
+ fieldPatchStrategy, fieldPatchMergeKey string, isDeleteList bool, mergeOptions MergeOptions) ([]interface{}, error) {
+ typedOriginal, typedPatch, err := sliceTypeAssertion(original, patch)
+ if err != nil {
+ return nil, err
+ }
+
+ if fieldPatchStrategy == mergeDirective {
+ return mergeSlice(typedOriginal, typedPatch, schema, fieldPatchMergeKey, mergeOptions, isDeleteList)
+ } else {
+ return typedPatch, nil
+ }
+}
+
+// Merge two slices together. Note: This may modify both the original slice and
+// the patch because getting a deep copy of a slice in golang is highly
+// non-trivial.
+func mergeSlice(original, patch []interface{}, schema LookupPatchMeta, mergeKey string, mergeOptions MergeOptions, isDeleteList bool) ([]interface{}, error) {
+ if len(original) == 0 && len(patch) == 0 {
+ return original, nil
+ }
+
+ // All the values must be of the same type, but not a list.
+ t, err := sliceElementType(original, patch)
+ if err != nil {
+ return nil, err
+ }
+
+ var merged []interface{}
+ kind := t.Kind()
+ // If the elements are not maps, merge the slices of scalars.
+ if kind != reflect.Map {
+ if mergeOptions.MergeParallelList && isDeleteList {
+ return deleteFromSlice(original, patch), nil
+ }
+ // Maybe in the future add a "concat" mode that doesn't
+ // deduplicate.
+ both := append(original, patch...)
+ merged = deduplicateScalars(both)
+
+ } else {
+ if mergeKey == "" {
+ return nil, fmt.Errorf("cannot merge lists without merge key for %s", schema.Name())
+ }
+
+ original, patch, err = mergeSliceWithSpecialElements(original, patch, mergeKey)
+ if err != nil {
+ return nil, err
+ }
+
+ merged, err = mergeSliceWithoutSpecialElements(original, patch, mergeKey, schema, mergeOptions)
+ if err != nil {
+ return nil, err
+ }
+ }
+
+ // enforce the order
+ var patchItems, serverOnlyItems []interface{}
+ if len(mergeKey) == 0 {
+ patchItems, serverOnlyItems = partitionPrimitivesByPresentInList(merged, patch)
+ } else {
+ patchItems, serverOnlyItems, err = partitionMapsByPresentInList(merged, patch, mergeKey)
+ if err != nil {
+ return nil, err
+ }
+ }
+ return normalizeElementOrder(patchItems, serverOnlyItems, patch, original, mergeKey, kind)
+}
+
+// mergeSliceWithSpecialElements handles special elements with directiveMarker
+// before merging the slices. It returns a updated `original` and a patch without special elements.
+// original and patch must be slices of maps, they should be checked before calling this function.
+func mergeSliceWithSpecialElements(original, patch []interface{}, mergeKey string) ([]interface{}, []interface{}, error) {
+ patchWithoutSpecialElements := []interface{}{}
+ replace := false
+ for _, v := range patch {
+ typedV := v.(map[string]interface{})
+ patchType, ok := typedV[directiveMarker]
+ if !ok {
+ patchWithoutSpecialElements = append(patchWithoutSpecialElements, v)
+ } else {
+ switch patchType {
+ case deleteDirective:
+ mergeValue, ok := typedV[mergeKey]
+ if ok {
+ var err error
+ original, err = deleteMatchingEntries(original, mergeKey, mergeValue)
+ if err != nil {
+ return nil, nil, err
+ }
+ } else {
+ return nil, nil, mergepatch.ErrNoMergeKey(typedV, mergeKey)
+ }
+ case replaceDirective:
+ replace = true
+ // Continue iterating through the array to prune any other $patch elements.
+ case mergeDirective:
+ return nil, nil, fmt.Errorf("merging lists cannot yet be specified in the patch")
+ default:
+ return nil, nil, mergepatch.ErrBadPatchType(patchType, typedV)
+ }
+ }
+ }
+ if replace {
+ return patchWithoutSpecialElements, nil, nil
+ }
+ return original, patchWithoutSpecialElements, nil
+}
+
+// delete all matching entries (based on merge key) from a merging list
+func deleteMatchingEntries(original []interface{}, mergeKey string, mergeValue interface{}) ([]interface{}, error) {
+ for {
+ _, originalKey, found, err := findMapInSliceBasedOnKeyValue(original, mergeKey, mergeValue)
+ if err != nil {
+ return nil, err
+ }
+
+ if !found {
+ break
+ }
+ // Delete the element at originalKey.
+ original = append(original[:originalKey], original[originalKey+1:]...)
+ }
+ return original, nil
+}
+
+// mergeSliceWithoutSpecialElements merges slices with non-special elements.
+// original and patch must be slices of maps, they should be checked before calling this function.
+func mergeSliceWithoutSpecialElements(original, patch []interface{}, mergeKey string, schema LookupPatchMeta, mergeOptions MergeOptions) ([]interface{}, error) {
+ for _, v := range patch {
+ typedV := v.(map[string]interface{})
+ mergeValue, ok := typedV[mergeKey]
+ if !ok {
+ return nil, mergepatch.ErrNoMergeKey(typedV, mergeKey)
+ }
+
+ // If we find a value with this merge key value in original, merge the
+ // maps. Otherwise append onto original.
+ originalMap, originalKey, found, err := findMapInSliceBasedOnKeyValue(original, mergeKey, mergeValue)
+ if err != nil {
+ return nil, err
+ }
+
+ if found {
+ var mergedMaps interface{}
+ var err error
+ // Merge into original.
+ mergedMaps, err = mergeMap(originalMap, typedV, schema, mergeOptions)
+ if err != nil {
+ return nil, err
+ }
+
+ original[originalKey] = mergedMaps
+ } else {
+ original = append(original, v)
+ }
+ }
+ return original, nil
+}
+
+// deleteFromSlice uses the parallel list to delete the items in a list of scalars
+func deleteFromSlice(current, toDelete []interface{}) []interface{} {
+ toDeleteMap := map[interface{}]interface{}{}
+ processed := make([]interface{}, 0, len(current))
+ for _, v := range toDelete {
+ toDeleteMap[v] = true
+ }
+ for _, v := range current {
+ if _, found := toDeleteMap[v]; !found {
+ processed = append(processed, v)
+ }
+ }
+ return processed
+}
+
+// This method no longer panics if any element of the slice is not a map.
+func findMapInSliceBasedOnKeyValue(m []interface{}, key string, value interface{}) (map[string]interface{}, int, bool, error) {
+ for k, v := range m {
+ typedV, ok := v.(map[string]interface{})
+ if !ok {
+ return nil, 0, false, fmt.Errorf("value for key %v is not a map", k)
+ }
+
+ valueToMatch, ok := typedV[key]
+ if ok && valueToMatch == value {
+ return typedV, k, true, nil
+ }
+ }
+
+ return nil, 0, false, nil
+}
+
+// This function takes a JSON map and sorts all the lists that should be merged
+// by key. This is needed by tests because in JSON, list order is significant,
+// but in Strategic Merge Patch, merge lists do not have significant order.
+// Sorting the lists allows for order-insensitive comparison of patched maps.
+func sortMergeListsByName(mapJSON []byte, schema LookupPatchMeta) ([]byte, error) {
+ var m map[string]interface{}
+ err := json.Unmarshal(mapJSON, &m)
+ if err != nil {
+ return nil, mergepatch.ErrBadJSONDoc
+ }
+
+ newM, err := sortMergeListsByNameMap(m, schema)
+ if err != nil {
+ return nil, err
+ }
+
+ return json.Marshal(newM)
+}
+
+// Function sortMergeListsByNameMap recursively sorts the merge lists by its mergeKey in a map.
+func sortMergeListsByNameMap(s map[string]interface{}, schema LookupPatchMeta) (map[string]interface{}, error) {
+ newS := map[string]interface{}{}
+ for k, v := range s {
+ if k == retainKeysDirective {
+ typedV, ok := v.([]interface{})
+ if !ok {
+ return nil, mergepatch.ErrBadPatchFormatForRetainKeys
+ }
+ v = sortScalars(typedV)
+ } else if strings.HasPrefix(k, deleteFromPrimitiveListDirectivePrefix) {
+ typedV, ok := v.([]interface{})
+ if !ok {
+ return nil, mergepatch.ErrBadPatchFormatForPrimitiveList
+ }
+ v = sortScalars(typedV)
+ } else if strings.HasPrefix(k, setElementOrderDirectivePrefix) {
+ _, ok := v.([]interface{})
+ if !ok {
+ return nil, mergepatch.ErrBadPatchFormatForSetElementOrderList
+ }
+ } else if k != directiveMarker {
+ // recurse for map and slice.
+ switch typedV := v.(type) {
+ case map[string]interface{}:
+ subschema, _, err := schema.LookupPatchMetadataForStruct(k)
+ if err != nil {
+ return nil, err
+ }
+ v, err = sortMergeListsByNameMap(typedV, subschema)
+ if err != nil {
+ return nil, err
+ }
+ case []interface{}:
+ subschema, patchMeta, err := schema.LookupPatchMetadataForSlice(k)
+ if err != nil {
+ return nil, err
+ }
+ _, patchStrategy, err := extractRetainKeysPatchStrategy(patchMeta.GetPatchStrategies())
+ if err != nil {
+ return nil, err
+ }
+ if patchStrategy == mergeDirective {
+ var err error
+ v, err = sortMergeListsByNameArray(typedV, subschema, patchMeta.GetPatchMergeKey(), true)
+ if err != nil {
+ return nil, err
+ }
+ }
+ }
+ }
+
+ newS[k] = v
+ }
+
+ return newS, nil
+}
+
+// Function sortMergeListsByNameMap recursively sorts the merge lists by its mergeKey in an array.
+func sortMergeListsByNameArray(s []interface{}, schema LookupPatchMeta, mergeKey string, recurse bool) ([]interface{}, error) {
+ if len(s) == 0 {
+ return s, nil
+ }
+
+ // We don't support lists of lists yet.
+ t, err := sliceElementType(s)
+ if err != nil {
+ return nil, err
+ }
+
+ // If the elements are not maps...
+ if t.Kind() != reflect.Map {
+ // Sort the elements, because they may have been merged out of order.
+ return deduplicateAndSortScalars(s), nil
+ }
+
+ // Elements are maps - if one of the keys of the map is a map or a
+ // list, we may need to recurse into it.
+ newS := []interface{}{}
+ for _, elem := range s {
+ if recurse {
+ typedElem := elem.(map[string]interface{})
+ newElem, err := sortMergeListsByNameMap(typedElem, schema)
+ if err != nil {
+ return nil, err
+ }
+
+ newS = append(newS, newElem)
+ } else {
+ newS = append(newS, elem)
+ }
+ }
+
+ // Sort the maps.
+ newS = sortMapsBasedOnField(newS, mergeKey)
+ return newS, nil
+}
+
+func sortMapsBasedOnField(m []interface{}, fieldName string) []interface{} {
+ mapM := mapSliceFromSlice(m)
+ ss := SortableSliceOfMaps{mapM, fieldName}
+ sort.Sort(ss)
+ newS := sliceFromMapSlice(ss.s)
+ return newS
+}
+
+func mapSliceFromSlice(m []interface{}) []map[string]interface{} {
+ newM := []map[string]interface{}{}
+ for _, v := range m {
+ vt := v.(map[string]interface{})
+ newM = append(newM, vt)
+ }
+
+ return newM
+}
+
+func sliceFromMapSlice(s []map[string]interface{}) []interface{} {
+ newS := []interface{}{}
+ for _, v := range s {
+ newS = append(newS, v)
+ }
+
+ return newS
+}
+
+type SortableSliceOfMaps struct {
+ s []map[string]interface{}
+ k string // key to sort on
+}
+
+func (ss SortableSliceOfMaps) Len() int {
+ return len(ss.s)
+}
+
+func (ss SortableSliceOfMaps) Less(i, j int) bool {
+ iStr := fmt.Sprintf("%v", ss.s[i][ss.k])
+ jStr := fmt.Sprintf("%v", ss.s[j][ss.k])
+ return sort.StringsAreSorted([]string{iStr, jStr})
+}
+
+func (ss SortableSliceOfMaps) Swap(i, j int) {
+ tmp := ss.s[i]
+ ss.s[i] = ss.s[j]
+ ss.s[j] = tmp
+}
+
+func deduplicateAndSortScalars(s []interface{}) []interface{} {
+ s = deduplicateScalars(s)
+ return sortScalars(s)
+}
+
+func sortScalars(s []interface{}) []interface{} {
+ ss := SortableSliceOfScalars{s}
+ sort.Sort(ss)
+ return ss.s
+}
+
+func deduplicateScalars(s []interface{}) []interface{} {
+ // Clever algorithm to deduplicate.
+ length := len(s) - 1
+ for i := 0; i < length; i++ {
+ for j := i + 1; j <= length; j++ {
+ if s[i] == s[j] {
+ s[j] = s[length]
+ s = s[0:length]
+ length--
+ j--
+ }
+ }
+ }
+
+ return s
+}
+
+type SortableSliceOfScalars struct {
+ s []interface{}
+}
+
+func (ss SortableSliceOfScalars) Len() int {
+ return len(ss.s)
+}
+
+func (ss SortableSliceOfScalars) Less(i, j int) bool {
+ iStr := fmt.Sprintf("%v", ss.s[i])
+ jStr := fmt.Sprintf("%v", ss.s[j])
+ return sort.StringsAreSorted([]string{iStr, jStr})
+}
+
+func (ss SortableSliceOfScalars) Swap(i, j int) {
+ tmp := ss.s[i]
+ ss.s[i] = ss.s[j]
+ ss.s[j] = tmp
+}
+
+// Returns the type of the elements of N slice(s). If the type is different,
+// another slice or undefined, returns an error.
+func sliceElementType(slices ...[]interface{}) (reflect.Type, error) {
+ var prevType reflect.Type
+ for _, s := range slices {
+ // Go through elements of all given slices and make sure they are all the same type.
+ for _, v := range s {
+ currentType := reflect.TypeOf(v)
+ if prevType == nil {
+ prevType = currentType
+ // We don't support lists of lists yet.
+ if prevType.Kind() == reflect.Slice {
+ return nil, mergepatch.ErrNoListOfLists
+ }
+ } else {
+ if prevType != currentType {
+ return nil, fmt.Errorf("list element types are not identical: %v", fmt.Sprint(slices))
+ }
+ prevType = currentType
+ }
+ }
+ }
+
+ if prevType == nil {
+ return nil, fmt.Errorf("no elements in any of the given slices")
+ }
+
+ return prevType, nil
+}
+
+// MergingMapsHaveConflicts returns true if the left and right JSON interface
+// objects overlap with different values in any key. All keys are required to be
+// strings. Since patches of the same Type have congruent keys, this is valid
+// for multiple patch types. This method supports strategic merge patch semantics.
+func MergingMapsHaveConflicts(left, right map[string]interface{}, schema LookupPatchMeta) (bool, error) {
+ return mergingMapFieldsHaveConflicts(left, right, schema, "", "")
+}
+
+func mergingMapFieldsHaveConflicts(
+ left, right interface{},
+ schema LookupPatchMeta,
+ fieldPatchStrategy, fieldPatchMergeKey string,
+) (bool, error) {
+ switch leftType := left.(type) {
+ case map[string]interface{}:
+ rightType, ok := right.(map[string]interface{})
+ if !ok {
+ return true, nil
+ }
+ leftMarker, okLeft := leftType[directiveMarker]
+ rightMarker, okRight := rightType[directiveMarker]
+ // if one or the other has a directive marker,
+ // then we need to consider that before looking at the individual keys,
+ // since a directive operates on the whole map.
+ if okLeft || okRight {
+ // if one has a directive marker and the other doesn't,
+ // then we have a conflict, since one is deleting or replacing the whole map,
+ // and the other is doing things to individual keys.
+ if okLeft != okRight {
+ return true, nil
+ }
+ // if they both have markers, but they are not the same directive,
+ // then we have a conflict because they're doing different things to the map.
+ if leftMarker != rightMarker {
+ return true, nil
+ }
+ }
+ if fieldPatchStrategy == replaceDirective {
+ return false, nil
+ }
+ // Check the individual keys.
+ return mapsHaveConflicts(leftType, rightType, schema)
+
+ case []interface{}:
+ rightType, ok := right.([]interface{})
+ if !ok {
+ return true, nil
+ }
+ return slicesHaveConflicts(leftType, rightType, schema, fieldPatchStrategy, fieldPatchMergeKey)
+ case string, float64, bool, int, int64, nil:
+ return !reflect.DeepEqual(left, right), nil
+ default:
+ return true, fmt.Errorf("unknown type: %v", reflect.TypeOf(left))
+ }
+}
+
+func mapsHaveConflicts(typedLeft, typedRight map[string]interface{}, schema LookupPatchMeta) (bool, error) {
+ for key, leftValue := range typedLeft {
+ if key != directiveMarker && key != retainKeysDirective {
+ if rightValue, ok := typedRight[key]; ok {
+ var subschema LookupPatchMeta
+ var patchMeta PatchMeta
+ var patchStrategy string
+ var err error
+ switch leftValue.(type) {
+ case []interface{}:
+ subschema, patchMeta, err = schema.LookupPatchMetadataForSlice(key)
+ if err != nil {
+ return true, err
+ }
+ _, patchStrategy, err = extractRetainKeysPatchStrategy(patchMeta.patchStrategies)
+ if err != nil {
+ return true, err
+ }
+ case map[string]interface{}:
+ subschema, patchMeta, err = schema.LookupPatchMetadataForStruct(key)
+ if err != nil {
+ return true, err
+ }
+ _, patchStrategy, err = extractRetainKeysPatchStrategy(patchMeta.patchStrategies)
+ if err != nil {
+ return true, err
+ }
+ }
+
+ if hasConflicts, err := mergingMapFieldsHaveConflicts(leftValue, rightValue,
+ subschema, patchStrategy, patchMeta.GetPatchMergeKey()); hasConflicts {
+ return true, err
+ }
+ }
+ }
+ }
+
+ return false, nil
+}
+
+func slicesHaveConflicts(
+ typedLeft, typedRight []interface{},
+ schema LookupPatchMeta,
+ fieldPatchStrategy, fieldPatchMergeKey string,
+) (bool, error) {
+ elementType, err := sliceElementType(typedLeft, typedRight)
+ if err != nil {
+ return true, err
+ }
+
+ if fieldPatchStrategy == mergeDirective {
+ // Merging lists of scalars have no conflicts by definition
+ // So we only need to check further if the elements are maps
+ if elementType.Kind() != reflect.Map {
+ return false, nil
+ }
+
+ // Build a map for each slice and then compare the two maps
+ leftMap, err := sliceOfMapsToMapOfMaps(typedLeft, fieldPatchMergeKey)
+ if err != nil {
+ return true, err
+ }
+
+ rightMap, err := sliceOfMapsToMapOfMaps(typedRight, fieldPatchMergeKey)
+ if err != nil {
+ return true, err
+ }
+
+ return mapsOfMapsHaveConflicts(leftMap, rightMap, schema)
+ }
+
+ // Either we don't have type information, or these are non-merging lists
+ if len(typedLeft) != len(typedRight) {
+ return true, nil
+ }
+
+ // Sort scalar slices to prevent ordering issues
+ // We have no way to sort non-merging lists of maps
+ if elementType.Kind() != reflect.Map {
+ typedLeft = deduplicateAndSortScalars(typedLeft)
+ typedRight = deduplicateAndSortScalars(typedRight)
+ }
+
+ // Compare the slices element by element in order
+ // This test will fail if the slices are not sorted
+ for i := range typedLeft {
+ if hasConflicts, err := mergingMapFieldsHaveConflicts(typedLeft[i], typedRight[i], schema, "", ""); hasConflicts {
+ return true, err
+ }
+ }
+
+ return false, nil
+}
+
+func sliceOfMapsToMapOfMaps(slice []interface{}, mergeKey string) (map[string]interface{}, error) {
+ result := make(map[string]interface{}, len(slice))
+ for _, value := range slice {
+ typedValue, ok := value.(map[string]interface{})
+ if !ok {
+ return nil, fmt.Errorf("invalid element type in merging list:%v", slice)
+ }
+
+ mergeValue, ok := typedValue[mergeKey]
+ if !ok {
+ return nil, fmt.Errorf("cannot find merge key `%s` in merging list element:%v", mergeKey, typedValue)
+ }
+
+ result[fmt.Sprintf("%s", mergeValue)] = typedValue
+ }
+
+ return result, nil
+}
+
+func mapsOfMapsHaveConflicts(typedLeft, typedRight map[string]interface{}, schema LookupPatchMeta) (bool, error) {
+ for key, leftValue := range typedLeft {
+ if rightValue, ok := typedRight[key]; ok {
+ if hasConflicts, err := mergingMapFieldsHaveConflicts(leftValue, rightValue, schema, "", ""); hasConflicts {
+ return true, err
+ }
+ }
+ }
+
+ return false, nil
+}
+
+// CreateThreeWayMergePatch reconciles a modified configuration with an original configuration,
+// while preserving any changes or deletions made to the original configuration in the interim,
+// and not overridden by the current configuration. All three documents must be passed to the
+// method as json encoded content. It will return a strategic merge patch, or an error if any
+// of the documents is invalid, or if there are any preconditions that fail against the modified
+// configuration, or, if overwrite is false and there are conflicts between the modified and current
+// configurations. Conflicts are defined as keys changed differently from original to modified
+// than from original to current. In other words, a conflict occurs if modified changes any key
+// in a way that is different from how it is changed in current (e.g., deleting it, changing its
+// value). We also propagate values fields that do not exist in original but are explicitly
+// defined in modified.
+func CreateThreeWayMergePatch(original, modified, current []byte, schema LookupPatchMeta, overwrite bool, fns ...mergepatch.PreconditionFunc) ([]byte, error) {
+ originalMap := map[string]interface{}{}
+ if len(original) > 0 {
+ if err := json.Unmarshal(original, &originalMap); err != nil {
+ return nil, mergepatch.ErrBadJSONDoc
+ }
+ }
+
+ modifiedMap := map[string]interface{}{}
+ if len(modified) > 0 {
+ if err := json.Unmarshal(modified, &modifiedMap); err != nil {
+ return nil, mergepatch.ErrBadJSONDoc
+ }
+ }
+
+ currentMap := map[string]interface{}{}
+ if len(current) > 0 {
+ if err := json.Unmarshal(current, ¤tMap); err != nil {
+ return nil, mergepatch.ErrBadJSONDoc
+ }
+ }
+
+ // The patch is the difference from current to modified without deletions, plus deletions
+ // from original to modified. To find it, we compute deletions, which are the deletions from
+ // original to modified, and delta, which is the difference from current to modified without
+ // deletions, and then apply delta to deletions as a patch, which should be strictly additive.
+ deltaMapDiffOptions := DiffOptions{
+ IgnoreDeletions: true,
+ SetElementOrder: true,
+ }
+ deltaMap, err := diffMaps(currentMap, modifiedMap, schema, deltaMapDiffOptions)
+ if err != nil {
+ return nil, err
+ }
+ deletionsMapDiffOptions := DiffOptions{
+ SetElementOrder: true,
+ IgnoreChangesAndAdditions: true,
+ }
+ deletionsMap, err := diffMaps(originalMap, modifiedMap, schema, deletionsMapDiffOptions)
+ if err != nil {
+ return nil, err
+ }
+
+ mergeOptions := MergeOptions{}
+ patchMap, err := mergeMap(deletionsMap, deltaMap, schema, mergeOptions)
+ if err != nil {
+ return nil, err
+ }
+
+ // Apply the preconditions to the patch, and return an error if any of them fail.
+ for _, fn := range fns {
+ if !fn(patchMap) {
+ return nil, mergepatch.NewErrPreconditionFailed(patchMap)
+ }
+ }
+
+ // If overwrite is false, and the patch contains any keys that were changed differently,
+ // then return a conflict error.
+ if !overwrite {
+ changeMapDiffOptions := DiffOptions{}
+ changedMap, err := diffMaps(originalMap, currentMap, schema, changeMapDiffOptions)
+ if err != nil {
+ return nil, err
+ }
+
+ hasConflicts, err := MergingMapsHaveConflicts(patchMap, changedMap, schema)
+ if err != nil {
+ return nil, err
+ }
+
+ if hasConflicts {
+ return nil, mergepatch.NewErrConflict(mergepatch.ToYAMLOrError(patchMap), mergepatch.ToYAMLOrError(changedMap))
+ }
+ }
+
+ return json.Marshal(patchMap)
+}
+
+func ItemAddedToModifiedSlice(original, modified string) bool { return original > modified }
+
+func ItemRemovedFromModifiedSlice(original, modified string) bool { return original < modified }
+
+func ItemMatchesOriginalAndModifiedSlice(original, modified string) bool { return original == modified }
+
+func CreateDeleteDirective(mergeKey string, mergeKeyValue interface{}) map[string]interface{} {
+ return map[string]interface{}{mergeKey: mergeKeyValue, directiveMarker: deleteDirective}
+}
+
+func mapTypeAssertion(original, patch interface{}) (map[string]interface{}, map[string]interface{}, error) {
+ typedOriginal, ok := original.(map[string]interface{})
+ if !ok {
+ return nil, nil, mergepatch.ErrBadArgType(typedOriginal, original)
+ }
+ typedPatch, ok := patch.(map[string]interface{})
+ if !ok {
+ return nil, nil, mergepatch.ErrBadArgType(typedPatch, patch)
+ }
+ return typedOriginal, typedPatch, nil
+}
+
+func sliceTypeAssertion(original, patch interface{}) ([]interface{}, []interface{}, error) {
+ typedOriginal, ok := original.([]interface{})
+ if !ok {
+ return nil, nil, mergepatch.ErrBadArgType(typedOriginal, original)
+ }
+ typedPatch, ok := patch.([]interface{})
+ if !ok {
+ return nil, nil, mergepatch.ErrBadArgType(typedPatch, patch)
+ }
+ return typedOriginal, typedPatch, nil
+}
+
+// extractRetainKeysPatchStrategy process patch strategy, which is a string may contains multiple
+// patch strategies separated by ",". It returns a boolean var indicating if it has
+// retainKeys strategies and a string for the other strategy.
+func extractRetainKeysPatchStrategy(strategies []string) (bool, string, error) {
+ switch len(strategies) {
+ case 0:
+ return false, "", nil
+ case 1:
+ singleStrategy := strategies[0]
+ switch singleStrategy {
+ case retainKeysStrategy:
+ return true, "", nil
+ default:
+ return false, singleStrategy, nil
+ }
+ case 2:
+ switch {
+ case strategies[0] == retainKeysStrategy:
+ return true, strategies[1], nil
+ case strategies[1] == retainKeysStrategy:
+ return true, strategies[0], nil
+ default:
+ return false, "", fmt.Errorf("unexpected patch strategy: %v", strategies)
+ }
+ default:
+ return false, "", fmt.Errorf("unexpected patch strategy: %v", strategies)
+ }
+}
+
+// hasAdditionalNewField returns if original map has additional key with non-nil value than modified.
+func hasAdditionalNewField(original, modified map[string]interface{}) bool {
+ for k, v := range original {
+ if v == nil {
+ continue
+ }
+ if _, found := modified[k]; !found {
+ return true
+ }
+ }
+ return false
+}
diff --git a/metrics-server/vendor/k8s.io/apimachinery/pkg/util/strategicpatch/types.go b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/strategicpatch/types.go
new file mode 100644
index 0000000..f84d65a
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/strategicpatch/types.go
@@ -0,0 +1,193 @@
+/*
+Copyright 2017 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package strategicpatch
+
+import (
+ "errors"
+ "strings"
+
+ "k8s.io/apimachinery/pkg/util/mergepatch"
+ openapi "k8s.io/kube-openapi/pkg/util/proto"
+)
+
+const (
+ patchStrategyOpenapiextensionKey = "x-kubernetes-patch-strategy"
+ patchMergeKeyOpenapiextensionKey = "x-kubernetes-patch-merge-key"
+)
+
+type LookupPatchItem interface {
+ openapi.SchemaVisitor
+
+ Error() error
+ Path() *openapi.Path
+}
+
+type kindItem struct {
+ key string
+ path *openapi.Path
+ err error
+ patchmeta PatchMeta
+ subschema openapi.Schema
+ hasVisitKind bool
+}
+
+func NewKindItem(key string, path *openapi.Path) *kindItem {
+ return &kindItem{
+ key: key,
+ path: path,
+ }
+}
+
+var _ LookupPatchItem = &kindItem{}
+
+func (item *kindItem) Error() error {
+ return item.err
+}
+
+func (item *kindItem) Path() *openapi.Path {
+ return item.path
+}
+
+func (item *kindItem) VisitPrimitive(schema *openapi.Primitive) {
+ item.err = errors.New("expected kind, but got primitive")
+}
+
+func (item *kindItem) VisitArray(schema *openapi.Array) {
+ item.err = errors.New("expected kind, but got slice")
+}
+
+func (item *kindItem) VisitMap(schema *openapi.Map) {
+ item.err = errors.New("expected kind, but got map")
+}
+
+func (item *kindItem) VisitReference(schema openapi.Reference) {
+ if !item.hasVisitKind {
+ schema.SubSchema().Accept(item)
+ }
+}
+
+func (item *kindItem) VisitKind(schema *openapi.Kind) {
+ subschema, ok := schema.Fields[item.key]
+ if !ok {
+ item.err = FieldNotFoundError{Path: schema.GetPath().String(), Field: item.key}
+ return
+ }
+
+ mergeKey, patchStrategies, err := parsePatchMetadata(subschema.GetExtensions())
+ if err != nil {
+ item.err = err
+ return
+ }
+ item.patchmeta = PatchMeta{
+ patchStrategies: patchStrategies,
+ patchMergeKey: mergeKey,
+ }
+ item.subschema = subschema
+}
+
+type sliceItem struct {
+ key string
+ path *openapi.Path
+ err error
+ patchmeta PatchMeta
+ subschema openapi.Schema
+ hasVisitKind bool
+}
+
+func NewSliceItem(key string, path *openapi.Path) *sliceItem {
+ return &sliceItem{
+ key: key,
+ path: path,
+ }
+}
+
+var _ LookupPatchItem = &sliceItem{}
+
+func (item *sliceItem) Error() error {
+ return item.err
+}
+
+func (item *sliceItem) Path() *openapi.Path {
+ return item.path
+}
+
+func (item *sliceItem) VisitPrimitive(schema *openapi.Primitive) {
+ item.err = errors.New("expected slice, but got primitive")
+}
+
+func (item *sliceItem) VisitArray(schema *openapi.Array) {
+ if !item.hasVisitKind {
+ item.err = errors.New("expected visit kind first, then visit array")
+ }
+ subschema := schema.SubType
+ item.subschema = subschema
+}
+
+func (item *sliceItem) VisitMap(schema *openapi.Map) {
+ item.err = errors.New("expected slice, but got map")
+}
+
+func (item *sliceItem) VisitReference(schema openapi.Reference) {
+ if !item.hasVisitKind {
+ schema.SubSchema().Accept(item)
+ } else {
+ item.subschema = schema.SubSchema()
+ }
+}
+
+func (item *sliceItem) VisitKind(schema *openapi.Kind) {
+ subschema, ok := schema.Fields[item.key]
+ if !ok {
+ item.err = FieldNotFoundError{Path: schema.GetPath().String(), Field: item.key}
+ return
+ }
+
+ mergeKey, patchStrategies, err := parsePatchMetadata(subschema.GetExtensions())
+ if err != nil {
+ item.err = err
+ return
+ }
+ item.patchmeta = PatchMeta{
+ patchStrategies: patchStrategies,
+ patchMergeKey: mergeKey,
+ }
+ item.hasVisitKind = true
+ subschema.Accept(item)
+}
+
+func parsePatchMetadata(extensions map[string]interface{}) (string, []string, error) {
+ ps, foundPS := extensions[patchStrategyOpenapiextensionKey]
+ var patchStrategies []string
+ var mergeKey, patchStrategy string
+ var ok bool
+ if foundPS {
+ patchStrategy, ok = ps.(string)
+ if ok {
+ patchStrategies = strings.Split(patchStrategy, ",")
+ } else {
+ return "", nil, mergepatch.ErrBadArgType(patchStrategy, ps)
+ }
+ }
+ mk, foundMK := extensions[patchMergeKeyOpenapiextensionKey]
+ if foundMK {
+ mergeKey, ok = mk.(string)
+ if !ok {
+ return "", nil, mergepatch.ErrBadArgType(mergeKey, mk)
+ }
+ }
+ return mergeKey, patchStrategies, nil
+}
diff --git a/metrics-server/vendor/k8s.io/apimachinery/pkg/util/uuid/uuid.go b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/uuid/uuid.go
new file mode 100644
index 0000000..bf47822
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/uuid/uuid.go
@@ -0,0 +1,43 @@
+/*
+Copyright 2014 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package uuid
+
+import (
+ "sync"
+
+ "github.com/pborman/uuid"
+
+ "k8s.io/apimachinery/pkg/types"
+)
+
+var uuidLock sync.Mutex
+var lastUUID uuid.UUID
+
+func NewUUID() types.UID {
+ uuidLock.Lock()
+ defer uuidLock.Unlock()
+ result := uuid.NewUUID()
+ // The UUID package is naive and can generate identical UUIDs if the
+ // time interval is quick enough.
+ // The UUID uses 100 ns increments so it's short enough to actively
+ // wait for a new value.
+ for uuid.Equal(lastUUID, result) == true {
+ result = uuid.NewUUID()
+ }
+ lastUUID = result
+ return types.UID(result.String())
+}
diff --git a/metrics-server/vendor/k8s.io/apimachinery/pkg/util/validation/field/errors.go b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/validation/field/errors.go
new file mode 100644
index 0000000..31705de
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/validation/field/errors.go
@@ -0,0 +1,259 @@
+/*
+Copyright 2014 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package field
+
+import (
+ "fmt"
+ "reflect"
+ "strconv"
+ "strings"
+
+ utilerrors "k8s.io/apimachinery/pkg/util/errors"
+ "k8s.io/apimachinery/pkg/util/sets"
+)
+
+// Error is an implementation of the 'error' interface, which represents a
+// field-level validation error.
+type Error struct {
+ Type ErrorType
+ Field string
+ BadValue interface{}
+ Detail string
+}
+
+var _ error = &Error{}
+
+// Error implements the error interface.
+func (v *Error) Error() string {
+ return fmt.Sprintf("%s: %s", v.Field, v.ErrorBody())
+}
+
+// ErrorBody returns the error message without the field name. This is useful
+// for building nice-looking higher-level error reporting.
+func (v *Error) ErrorBody() string {
+ var s string
+ switch v.Type {
+ case ErrorTypeRequired, ErrorTypeForbidden, ErrorTypeTooLong, ErrorTypeInternal:
+ s = fmt.Sprintf("%s", v.Type)
+ default:
+ value := v.BadValue
+ valueType := reflect.TypeOf(value)
+ if value == nil || valueType == nil {
+ value = "null"
+ } else if valueType.Kind() == reflect.Ptr {
+ if reflectValue := reflect.ValueOf(value); reflectValue.IsNil() {
+ value = "null"
+ } else {
+ value = reflectValue.Elem().Interface()
+ }
+ }
+ switch t := value.(type) {
+ case int64, int32, float64, float32, bool:
+ // use simple printer for simple types
+ s = fmt.Sprintf("%s: %v", v.Type, value)
+ case string:
+ s = fmt.Sprintf("%s: %q", v.Type, t)
+ case fmt.Stringer:
+ // anything that defines String() is better than raw struct
+ s = fmt.Sprintf("%s: %s", v.Type, t.String())
+ default:
+ // fallback to raw struct
+ // TODO: internal types have panic guards against json.Marshalling to prevent
+ // accidental use of internal types in external serialized form. For now, use
+ // %#v, although it would be better to show a more expressive output in the future
+ s = fmt.Sprintf("%s: %#v", v.Type, value)
+ }
+ }
+ if len(v.Detail) != 0 {
+ s += fmt.Sprintf(": %s", v.Detail)
+ }
+ return s
+}
+
+// ErrorType is a machine readable value providing more detail about why
+// a field is invalid. These values are expected to match 1-1 with
+// CauseType in api/types.go.
+type ErrorType string
+
+// TODO: These values are duplicated in api/types.go, but there's a circular dep. Fix it.
+const (
+ // ErrorTypeNotFound is used to report failure to find a requested value
+ // (e.g. looking up an ID). See NotFound().
+ ErrorTypeNotFound ErrorType = "FieldValueNotFound"
+ // ErrorTypeRequired is used to report required values that are not
+ // provided (e.g. empty strings, null values, or empty arrays). See
+ // Required().
+ ErrorTypeRequired ErrorType = "FieldValueRequired"
+ // ErrorTypeDuplicate is used to report collisions of values that must be
+ // unique (e.g. unique IDs). See Duplicate().
+ ErrorTypeDuplicate ErrorType = "FieldValueDuplicate"
+ // ErrorTypeInvalid is used to report malformed values (e.g. failed regex
+ // match, too long, out of bounds). See Invalid().
+ ErrorTypeInvalid ErrorType = "FieldValueInvalid"
+ // ErrorTypeNotSupported is used to report unknown values for enumerated
+ // fields (e.g. a list of valid values). See NotSupported().
+ ErrorTypeNotSupported ErrorType = "FieldValueNotSupported"
+ // ErrorTypeForbidden is used to report valid (as per formatting rules)
+ // values which would be accepted under some conditions, but which are not
+ // permitted by the current conditions (such as security policy). See
+ // Forbidden().
+ ErrorTypeForbidden ErrorType = "FieldValueForbidden"
+ // ErrorTypeTooLong is used to report that the given value is too long.
+ // This is similar to ErrorTypeInvalid, but the error will not include the
+ // too-long value. See TooLong().
+ ErrorTypeTooLong ErrorType = "FieldValueTooLong"
+ // ErrorTypeInternal is used to report other errors that are not related
+ // to user input. See InternalError().
+ ErrorTypeInternal ErrorType = "InternalError"
+)
+
+// String converts a ErrorType into its corresponding canonical error message.
+func (t ErrorType) String() string {
+ switch t {
+ case ErrorTypeNotFound:
+ return "Not found"
+ case ErrorTypeRequired:
+ return "Required value"
+ case ErrorTypeDuplicate:
+ return "Duplicate value"
+ case ErrorTypeInvalid:
+ return "Invalid value"
+ case ErrorTypeNotSupported:
+ return "Unsupported value"
+ case ErrorTypeForbidden:
+ return "Forbidden"
+ case ErrorTypeTooLong:
+ return "Too long"
+ case ErrorTypeInternal:
+ return "Internal error"
+ default:
+ panic(fmt.Sprintf("unrecognized validation error: %q", string(t)))
+ }
+}
+
+// NotFound returns a *Error indicating "value not found". This is
+// used to report failure to find a requested value (e.g. looking up an ID).
+func NotFound(field *Path, value interface{}) *Error {
+ return &Error{ErrorTypeNotFound, field.String(), value, ""}
+}
+
+// Required returns a *Error indicating "value required". This is used
+// to report required values that are not provided (e.g. empty strings, null
+// values, or empty arrays).
+func Required(field *Path, detail string) *Error {
+ return &Error{ErrorTypeRequired, field.String(), "", detail}
+}
+
+// Duplicate returns a *Error indicating "duplicate value". This is
+// used to report collisions of values that must be unique (e.g. names or IDs).
+func Duplicate(field *Path, value interface{}) *Error {
+ return &Error{ErrorTypeDuplicate, field.String(), value, ""}
+}
+
+// Invalid returns a *Error indicating "invalid value". This is used
+// to report malformed values (e.g. failed regex match, too long, out of bounds).
+func Invalid(field *Path, value interface{}, detail string) *Error {
+ return &Error{ErrorTypeInvalid, field.String(), value, detail}
+}
+
+// NotSupported returns a *Error indicating "unsupported value".
+// This is used to report unknown values for enumerated fields (e.g. a list of
+// valid values).
+func NotSupported(field *Path, value interface{}, validValues []string) *Error {
+ detail := ""
+ if validValues != nil && len(validValues) > 0 {
+ quotedValues := make([]string, len(validValues))
+ for i, v := range validValues {
+ quotedValues[i] = strconv.Quote(v)
+ }
+ detail = "supported values: " + strings.Join(quotedValues, ", ")
+ }
+ return &Error{ErrorTypeNotSupported, field.String(), value, detail}
+}
+
+// Forbidden returns a *Error indicating "forbidden". This is used to
+// report valid (as per formatting rules) values which would be accepted under
+// some conditions, but which are not permitted by current conditions (e.g.
+// security policy).
+func Forbidden(field *Path, detail string) *Error {
+ return &Error{ErrorTypeForbidden, field.String(), "", detail}
+}
+
+// TooLong returns a *Error indicating "too long". This is used to
+// report that the given value is too long. This is similar to
+// Invalid, but the returned error will not include the too-long
+// value.
+func TooLong(field *Path, value interface{}, maxLength int) *Error {
+ return &Error{ErrorTypeTooLong, field.String(), value, fmt.Sprintf("must have at most %d characters", maxLength)}
+}
+
+// InternalError returns a *Error indicating "internal error". This is used
+// to signal that an error was found that was not directly related to user
+// input. The err argument must be non-nil.
+func InternalError(field *Path, err error) *Error {
+ return &Error{ErrorTypeInternal, field.String(), nil, err.Error()}
+}
+
+// ErrorList holds a set of Errors. It is plausible that we might one day have
+// non-field errors in this same umbrella package, but for now we don't, so
+// we can keep it simple and leave ErrorList here.
+type ErrorList []*Error
+
+// NewErrorTypeMatcher returns an errors.Matcher that returns true
+// if the provided error is a Error and has the provided ErrorType.
+func NewErrorTypeMatcher(t ErrorType) utilerrors.Matcher {
+ return func(err error) bool {
+ if e, ok := err.(*Error); ok {
+ return e.Type == t
+ }
+ return false
+ }
+}
+
+// ToAggregate converts the ErrorList into an errors.Aggregate.
+func (list ErrorList) ToAggregate() utilerrors.Aggregate {
+ errs := make([]error, 0, len(list))
+ errorMsgs := sets.NewString()
+ for _, err := range list {
+ msg := fmt.Sprintf("%v", err)
+ if errorMsgs.Has(msg) {
+ continue
+ }
+ errorMsgs.Insert(msg)
+ errs = append(errs, err)
+ }
+ return utilerrors.NewAggregate(errs)
+}
+
+func fromAggregate(agg utilerrors.Aggregate) ErrorList {
+ errs := agg.Errors()
+ list := make(ErrorList, len(errs))
+ for i := range errs {
+ list[i] = errs[i].(*Error)
+ }
+ return list
+}
+
+// Filter removes items from the ErrorList that match the provided fns.
+func (list ErrorList) Filter(fns ...utilerrors.Matcher) ErrorList {
+ err := utilerrors.FilterOut(list.ToAggregate(), fns...)
+ if err == nil {
+ return nil
+ }
+ // FilterOut takes an Aggregate and returns an Aggregate
+ return fromAggregate(err.(utilerrors.Aggregate))
+}
diff --git a/metrics-server/vendor/k8s.io/apimachinery/pkg/util/validation/field/path.go b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/validation/field/path.go
new file mode 100644
index 0000000..2efc8ee
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/validation/field/path.go
@@ -0,0 +1,91 @@
+/*
+Copyright 2015 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package field
+
+import (
+ "bytes"
+ "fmt"
+ "strconv"
+)
+
+// Path represents the path from some root to a particular field.
+type Path struct {
+ name string // the name of this field or "" if this is an index
+ index string // if name == "", this is a subscript (index or map key) of the previous element
+ parent *Path // nil if this is the root element
+}
+
+// NewPath creates a root Path object.
+func NewPath(name string, moreNames ...string) *Path {
+ r := &Path{name: name, parent: nil}
+ for _, anotherName := range moreNames {
+ r = &Path{name: anotherName, parent: r}
+ }
+ return r
+}
+
+// Root returns the root element of this Path.
+func (p *Path) Root() *Path {
+ for ; p.parent != nil; p = p.parent {
+ // Do nothing.
+ }
+ return p
+}
+
+// Child creates a new Path that is a child of the method receiver.
+func (p *Path) Child(name string, moreNames ...string) *Path {
+ r := NewPath(name, moreNames...)
+ r.Root().parent = p
+ return r
+}
+
+// Index indicates that the previous Path is to be subscripted by an int.
+// This sets the same underlying value as Key.
+func (p *Path) Index(index int) *Path {
+ return &Path{index: strconv.Itoa(index), parent: p}
+}
+
+// Key indicates that the previous Path is to be subscripted by a string.
+// This sets the same underlying value as Index.
+func (p *Path) Key(key string) *Path {
+ return &Path{index: key, parent: p}
+}
+
+// String produces a string representation of the Path.
+func (p *Path) String() string {
+ // make a slice to iterate
+ elems := []*Path{}
+ for ; p != nil; p = p.parent {
+ elems = append(elems, p)
+ }
+
+ // iterate, but it has to be backwards
+ buf := bytes.NewBuffer(nil)
+ for i := range elems {
+ p := elems[len(elems)-1-i]
+ if p.parent != nil && len(p.name) > 0 {
+ // This is either the root or it is a subscript.
+ buf.WriteString(".")
+ }
+ if len(p.name) > 0 {
+ buf.WriteString(p.name)
+ } else {
+ fmt.Fprintf(buf, "[%s]", p.index)
+ }
+ }
+ return buf.String()
+}
diff --git a/metrics-server/vendor/k8s.io/apimachinery/pkg/util/validation/validation.go b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/validation/validation.go
new file mode 100644
index 0000000..7da6a17
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/validation/validation.go
@@ -0,0 +1,391 @@
+/*
+Copyright 2014 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package validation
+
+import (
+ "fmt"
+ "math"
+ "net"
+ "regexp"
+ "strings"
+
+ "k8s.io/apimachinery/pkg/util/validation/field"
+)
+
+const qnameCharFmt string = "[A-Za-z0-9]"
+const qnameExtCharFmt string = "[-A-Za-z0-9_.]"
+const qualifiedNameFmt string = "(" + qnameCharFmt + qnameExtCharFmt + "*)?" + qnameCharFmt
+const qualifiedNameErrMsg string = "must consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character"
+const qualifiedNameMaxLength int = 63
+
+var qualifiedNameRegexp = regexp.MustCompile("^" + qualifiedNameFmt + "$")
+
+// IsQualifiedName tests whether the value passed is what Kubernetes calls a
+// "qualified name". This is a format used in various places throughout the
+// system. If the value is not valid, a list of error strings is returned.
+// Otherwise an empty list (or nil) is returned.
+func IsQualifiedName(value string) []string {
+ var errs []string
+ parts := strings.Split(value, "/")
+ var name string
+ switch len(parts) {
+ case 1:
+ name = parts[0]
+ case 2:
+ var prefix string
+ prefix, name = parts[0], parts[1]
+ if len(prefix) == 0 {
+ errs = append(errs, "prefix part "+EmptyError())
+ } else if msgs := IsDNS1123Subdomain(prefix); len(msgs) != 0 {
+ errs = append(errs, prefixEach(msgs, "prefix part ")...)
+ }
+ default:
+ return append(errs, "a qualified name "+RegexError(qualifiedNameErrMsg, qualifiedNameFmt, "MyName", "my.name", "123-abc")+
+ " with an optional DNS subdomain prefix and '/' (e.g. 'example.com/MyName')")
+ }
+
+ if len(name) == 0 {
+ errs = append(errs, "name part "+EmptyError())
+ } else if len(name) > qualifiedNameMaxLength {
+ errs = append(errs, "name part "+MaxLenError(qualifiedNameMaxLength))
+ }
+ if !qualifiedNameRegexp.MatchString(name) {
+ errs = append(errs, "name part "+RegexError(qualifiedNameErrMsg, qualifiedNameFmt, "MyName", "my.name", "123-abc"))
+ }
+ return errs
+}
+
+// IsFullyQualifiedName checks if the name is fully qualified.
+func IsFullyQualifiedName(fldPath *field.Path, name string) field.ErrorList {
+ var allErrors field.ErrorList
+ if len(name) == 0 {
+ return append(allErrors, field.Required(fldPath, ""))
+ }
+ if errs := IsDNS1123Subdomain(name); len(errs) > 0 {
+ return append(allErrors, field.Invalid(fldPath, name, strings.Join(errs, ",")))
+ }
+ if len(strings.Split(name, ".")) < 3 {
+ return append(allErrors, field.Invalid(fldPath, name, "should be a domain with at least three segments separated by dots"))
+ }
+ return allErrors
+}
+
+const labelValueFmt string = "(" + qualifiedNameFmt + ")?"
+const labelValueErrMsg string = "a valid label must be an empty string or consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character"
+const LabelValueMaxLength int = 63
+
+var labelValueRegexp = regexp.MustCompile("^" + labelValueFmt + "$")
+
+// IsValidLabelValue tests whether the value passed is a valid label value. If
+// the value is not valid, a list of error strings is returned. Otherwise an
+// empty list (or nil) is returned.
+func IsValidLabelValue(value string) []string {
+ var errs []string
+ if len(value) > LabelValueMaxLength {
+ errs = append(errs, MaxLenError(LabelValueMaxLength))
+ }
+ if !labelValueRegexp.MatchString(value) {
+ errs = append(errs, RegexError(labelValueErrMsg, labelValueFmt, "MyValue", "my_value", "12345"))
+ }
+ return errs
+}
+
+const dns1123LabelFmt string = "[a-z0-9]([-a-z0-9]*[a-z0-9])?"
+const dns1123LabelErrMsg string = "a DNS-1123 label must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character"
+const DNS1123LabelMaxLength int = 63
+
+var dns1123LabelRegexp = regexp.MustCompile("^" + dns1123LabelFmt + "$")
+
+// IsDNS1123Label tests for a string that conforms to the definition of a label in
+// DNS (RFC 1123).
+func IsDNS1123Label(value string) []string {
+ var errs []string
+ if len(value) > DNS1123LabelMaxLength {
+ errs = append(errs, MaxLenError(DNS1123LabelMaxLength))
+ }
+ if !dns1123LabelRegexp.MatchString(value) {
+ errs = append(errs, RegexError(dns1123LabelErrMsg, dns1123LabelFmt, "my-name", "123-abc"))
+ }
+ return errs
+}
+
+const dns1123SubdomainFmt string = dns1123LabelFmt + "(\\." + dns1123LabelFmt + ")*"
+const dns1123SubdomainErrorMsg string = "a DNS-1123 subdomain must consist of lower case alphanumeric characters, '-' or '.', and must start and end with an alphanumeric character"
+const DNS1123SubdomainMaxLength int = 253
+
+var dns1123SubdomainRegexp = regexp.MustCompile("^" + dns1123SubdomainFmt + "$")
+
+// IsDNS1123Subdomain tests for a string that conforms to the definition of a
+// subdomain in DNS (RFC 1123).
+func IsDNS1123Subdomain(value string) []string {
+ var errs []string
+ if len(value) > DNS1123SubdomainMaxLength {
+ errs = append(errs, MaxLenError(DNS1123SubdomainMaxLength))
+ }
+ if !dns1123SubdomainRegexp.MatchString(value) {
+ errs = append(errs, RegexError(dns1123SubdomainErrorMsg, dns1123SubdomainFmt, "example.com"))
+ }
+ return errs
+}
+
+const dns1035LabelFmt string = "[a-z]([-a-z0-9]*[a-z0-9])?"
+const dns1035LabelErrMsg string = "a DNS-1035 label must consist of lower case alphanumeric characters or '-', start with an alphabetic character, and end with an alphanumeric character"
+const DNS1035LabelMaxLength int = 63
+
+var dns1035LabelRegexp = regexp.MustCompile("^" + dns1035LabelFmt + "$")
+
+// IsDNS1035Label tests for a string that conforms to the definition of a label in
+// DNS (RFC 1035).
+func IsDNS1035Label(value string) []string {
+ var errs []string
+ if len(value) > DNS1035LabelMaxLength {
+ errs = append(errs, MaxLenError(DNS1035LabelMaxLength))
+ }
+ if !dns1035LabelRegexp.MatchString(value) {
+ errs = append(errs, RegexError(dns1035LabelErrMsg, dns1035LabelFmt, "my-name", "abc-123"))
+ }
+ return errs
+}
+
+// wildcard definition - RFC 1034 section 4.3.3.
+// examples:
+// - valid: *.bar.com, *.foo.bar.com
+// - invalid: *.*.bar.com, *.foo.*.com, *bar.com, f*.bar.com, *
+const wildcardDNS1123SubdomainFmt = "\\*\\." + dns1123SubdomainFmt
+const wildcardDNS1123SubdomainErrMsg = "a wildcard DNS-1123 subdomain must start with '*.', followed by a valid DNS subdomain, which must consist of lower case alphanumeric characters, '-' or '.' and end with an alphanumeric character"
+
+// IsWildcardDNS1123Subdomain tests for a string that conforms to the definition of a
+// wildcard subdomain in DNS (RFC 1034 section 4.3.3).
+func IsWildcardDNS1123Subdomain(value string) []string {
+ wildcardDNS1123SubdomainRegexp := regexp.MustCompile("^" + wildcardDNS1123SubdomainFmt + "$")
+
+ var errs []string
+ if len(value) > DNS1123SubdomainMaxLength {
+ errs = append(errs, MaxLenError(DNS1123SubdomainMaxLength))
+ }
+ if !wildcardDNS1123SubdomainRegexp.MatchString(value) {
+ errs = append(errs, RegexError(wildcardDNS1123SubdomainErrMsg, wildcardDNS1123SubdomainFmt, "*.example.com"))
+ }
+ return errs
+}
+
+const cIdentifierFmt string = "[A-Za-z_][A-Za-z0-9_]*"
+const identifierErrMsg string = "a valid C identifier must start with alphabetic character or '_', followed by a string of alphanumeric characters or '_'"
+
+var cIdentifierRegexp = regexp.MustCompile("^" + cIdentifierFmt + "$")
+
+// IsCIdentifier tests for a string that conforms the definition of an identifier
+// in C. This checks the format, but not the length.
+func IsCIdentifier(value string) []string {
+ if !cIdentifierRegexp.MatchString(value) {
+ return []string{RegexError(identifierErrMsg, cIdentifierFmt, "my_name", "MY_NAME", "MyName")}
+ }
+ return nil
+}
+
+// IsValidPortNum tests that the argument is a valid, non-zero port number.
+func IsValidPortNum(port int) []string {
+ if 1 <= port && port <= 65535 {
+ return nil
+ }
+ return []string{InclusiveRangeError(1, 65535)}
+}
+
+// IsInRange tests that the argument is in an inclusive range.
+func IsInRange(value int, min int, max int) []string {
+ if value >= min && value <= max {
+ return nil
+ }
+ return []string{InclusiveRangeError(min, max)}
+}
+
+// Now in libcontainer UID/GID limits is 0 ~ 1<<31 - 1
+// TODO: once we have a type for UID/GID we should make these that type.
+const (
+ minUserID = 0
+ maxUserID = math.MaxInt32
+ minGroupID = 0
+ maxGroupID = math.MaxInt32
+)
+
+// IsValidGroupID tests that the argument is a valid Unix GID.
+func IsValidGroupID(gid int64) []string {
+ if minGroupID <= gid && gid <= maxGroupID {
+ return nil
+ }
+ return []string{InclusiveRangeError(minGroupID, maxGroupID)}
+}
+
+// IsValidUserID tests that the argument is a valid Unix UID.
+func IsValidUserID(uid int64) []string {
+ if minUserID <= uid && uid <= maxUserID {
+ return nil
+ }
+ return []string{InclusiveRangeError(minUserID, maxUserID)}
+}
+
+var portNameCharsetRegex = regexp.MustCompile("^[-a-z0-9]+$")
+var portNameOneLetterRegexp = regexp.MustCompile("[a-z]")
+
+// IsValidPortName check that the argument is valid syntax. It must be
+// non-empty and no more than 15 characters long. It may contain only [-a-z0-9]
+// and must contain at least one letter [a-z]. It must not start or end with a
+// hyphen, nor contain adjacent hyphens.
+//
+// Note: We only allow lower-case characters, even though RFC 6335 is case
+// insensitive.
+func IsValidPortName(port string) []string {
+ var errs []string
+ if len(port) > 15 {
+ errs = append(errs, MaxLenError(15))
+ }
+ if !portNameCharsetRegex.MatchString(port) {
+ errs = append(errs, "must contain only alpha-numeric characters (a-z, 0-9), and hyphens (-)")
+ }
+ if !portNameOneLetterRegexp.MatchString(port) {
+ errs = append(errs, "must contain at least one letter or number (a-z, 0-9)")
+ }
+ if strings.Contains(port, "--") {
+ errs = append(errs, "must not contain consecutive hyphens")
+ }
+ if len(port) > 0 && (port[0] == '-' || port[len(port)-1] == '-') {
+ errs = append(errs, "must not begin or end with a hyphen")
+ }
+ return errs
+}
+
+// IsValidIP tests that the argument is a valid IP address.
+func IsValidIP(value string) []string {
+ if net.ParseIP(value) == nil {
+ return []string{"must be a valid IP address, (e.g. 10.9.8.7)"}
+ }
+ return nil
+}
+
+const percentFmt string = "[0-9]+%"
+const percentErrMsg string = "a valid percent string must be a numeric string followed by an ending '%'"
+
+var percentRegexp = regexp.MustCompile("^" + percentFmt + "$")
+
+func IsValidPercent(percent string) []string {
+ if !percentRegexp.MatchString(percent) {
+ return []string{RegexError(percentErrMsg, percentFmt, "1%", "93%")}
+ }
+ return nil
+}
+
+const httpHeaderNameFmt string = "[-A-Za-z0-9]+"
+const httpHeaderNameErrMsg string = "a valid HTTP header must consist of alphanumeric characters or '-'"
+
+var httpHeaderNameRegexp = regexp.MustCompile("^" + httpHeaderNameFmt + "$")
+
+// IsHTTPHeaderName checks that a string conforms to the Go HTTP library's
+// definition of a valid header field name (a stricter subset than RFC7230).
+func IsHTTPHeaderName(value string) []string {
+ if !httpHeaderNameRegexp.MatchString(value) {
+ return []string{RegexError(httpHeaderNameErrMsg, httpHeaderNameFmt, "X-Header-Name")}
+ }
+ return nil
+}
+
+const envVarNameFmt = "[-._a-zA-Z][-._a-zA-Z0-9]*"
+const envVarNameFmtErrMsg string = "a valid environment variable name must consist of alphabetic characters, digits, '_', '-', or '.', and must not start with a digit"
+
+var envVarNameRegexp = regexp.MustCompile("^" + envVarNameFmt + "$")
+
+// IsEnvVarName tests if a string is a valid environment variable name.
+func IsEnvVarName(value string) []string {
+ var errs []string
+ if !envVarNameRegexp.MatchString(value) {
+ errs = append(errs, RegexError(envVarNameFmtErrMsg, envVarNameFmt, "my.env-name", "MY_ENV.NAME", "MyEnvName1"))
+ }
+
+ errs = append(errs, hasChDirPrefix(value)...)
+ return errs
+}
+
+const configMapKeyFmt = `[-._a-zA-Z0-9]+`
+const configMapKeyErrMsg string = "a valid config key must consist of alphanumeric characters, '-', '_' or '.'"
+
+var configMapKeyRegexp = regexp.MustCompile("^" + configMapKeyFmt + "$")
+
+// IsConfigMapKey tests for a string that is a valid key for a ConfigMap or Secret
+func IsConfigMapKey(value string) []string {
+ var errs []string
+ if len(value) > DNS1123SubdomainMaxLength {
+ errs = append(errs, MaxLenError(DNS1123SubdomainMaxLength))
+ }
+ if !configMapKeyRegexp.MatchString(value) {
+ errs = append(errs, RegexError(configMapKeyErrMsg, configMapKeyFmt, "key.name", "KEY_NAME", "key-name"))
+ }
+ errs = append(errs, hasChDirPrefix(value)...)
+ return errs
+}
+
+// MaxLenError returns a string explanation of a "string too long" validation
+// failure.
+func MaxLenError(length int) string {
+ return fmt.Sprintf("must be no more than %d characters", length)
+}
+
+// RegexError returns a string explanation of a regex validation failure.
+func RegexError(msg string, fmt string, examples ...string) string {
+ if len(examples) == 0 {
+ return msg + " (regex used for validation is '" + fmt + "')"
+ }
+ msg += " (e.g. "
+ for i := range examples {
+ if i > 0 {
+ msg += " or "
+ }
+ msg += "'" + examples[i] + "', "
+ }
+ msg += "regex used for validation is '" + fmt + "')"
+ return msg
+}
+
+// EmptyError returns a string explanation of a "must not be empty" validation
+// failure.
+func EmptyError() string {
+ return "must be non-empty"
+}
+
+func prefixEach(msgs []string, prefix string) []string {
+ for i := range msgs {
+ msgs[i] = prefix + msgs[i]
+ }
+ return msgs
+}
+
+// InclusiveRangeError returns a string explanation of a numeric "must be
+// between" validation failure.
+func InclusiveRangeError(lo, hi int) string {
+ return fmt.Sprintf(`must be between %d and %d, inclusive`, lo, hi)
+}
+
+func hasChDirPrefix(value string) []string {
+ var errs []string
+ switch {
+ case value == ".":
+ errs = append(errs, `must not be '.'`)
+ case value == "..":
+ errs = append(errs, `must not be '..'`)
+ case strings.HasPrefix(value, ".."):
+ errs = append(errs, `must not start with '..'`)
+ }
+ return errs
+}
diff --git a/metrics-server/vendor/k8s.io/apimachinery/pkg/util/wait/doc.go b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/wait/doc.go
new file mode 100644
index 0000000..3f0c968
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/wait/doc.go
@@ -0,0 +1,19 @@
+/*
+Copyright 2014 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Package wait provides tools for polling or listening for changes
+// to a condition.
+package wait // import "k8s.io/apimachinery/pkg/util/wait"
diff --git a/metrics-server/vendor/k8s.io/apimachinery/pkg/util/wait/wait.go b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/wait/wait.go
new file mode 100644
index 0000000..a25e924
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/wait/wait.go
@@ -0,0 +1,405 @@
+/*
+Copyright 2014 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package wait
+
+import (
+ "context"
+ "errors"
+ "math/rand"
+ "sync"
+ "time"
+
+ "k8s.io/apimachinery/pkg/util/runtime"
+)
+
+// For any test of the style:
+// ...
+// <- time.After(timeout):
+// t.Errorf("Timed out")
+// The value for timeout should effectively be "forever." Obviously we don't want our tests to truly lock up forever, but 30s
+// is long enough that it is effectively forever for the things that can slow down a run on a heavily contended machine
+// (GC, seeks, etc), but not so long as to make a developer ctrl-c a test run if they do happen to break that test.
+var ForeverTestTimeout = time.Second * 30
+
+// NeverStop may be passed to Until to make it never stop.
+var NeverStop <-chan struct{} = make(chan struct{})
+
+// Group allows to start a group of goroutines and wait for their completion.
+type Group struct {
+ wg sync.WaitGroup
+}
+
+func (g *Group) Wait() {
+ g.wg.Wait()
+}
+
+// StartWithChannel starts f in a new goroutine in the group.
+// stopCh is passed to f as an argument. f should stop when stopCh is available.
+func (g *Group) StartWithChannel(stopCh <-chan struct{}, f func(stopCh <-chan struct{})) {
+ g.Start(func() {
+ f(stopCh)
+ })
+}
+
+// StartWithContext starts f in a new goroutine in the group.
+// ctx is passed to f as an argument. f should stop when ctx.Done() is available.
+func (g *Group) StartWithContext(ctx context.Context, f func(context.Context)) {
+ g.Start(func() {
+ f(ctx)
+ })
+}
+
+// Start starts f in a new goroutine in the group.
+func (g *Group) Start(f func()) {
+ g.wg.Add(1)
+ go func() {
+ defer g.wg.Done()
+ f()
+ }()
+}
+
+// Forever calls f every period for ever.
+//
+// Forever is syntactic sugar on top of Until.
+func Forever(f func(), period time.Duration) {
+ Until(f, period, NeverStop)
+}
+
+// Until loops until stop channel is closed, running f every period.
+//
+// Until is syntactic sugar on top of JitterUntil with zero jitter factor and
+// with sliding = true (which means the timer for period starts after the f
+// completes).
+func Until(f func(), period time.Duration, stopCh <-chan struct{}) {
+ JitterUntil(f, period, 0.0, true, stopCh)
+}
+
+// NonSlidingUntil loops until stop channel is closed, running f every
+// period.
+//
+// NonSlidingUntil is syntactic sugar on top of JitterUntil with zero jitter
+// factor, with sliding = false (meaning the timer for period starts at the same
+// time as the function starts).
+func NonSlidingUntil(f func(), period time.Duration, stopCh <-chan struct{}) {
+ JitterUntil(f, period, 0.0, false, stopCh)
+}
+
+// JitterUntil loops until stop channel is closed, running f every period.
+//
+// If jitterFactor is positive, the period is jittered before every run of f.
+// If jitterFactor is not positive, the period is unchanged and not jittered.
+//
+// If sliding is true, the period is computed after f runs. If it is false then
+// period includes the runtime for f.
+//
+// Close stopCh to stop. f may not be invoked if stop channel is already
+// closed. Pass NeverStop to if you don't want it stop.
+func JitterUntil(f func(), period time.Duration, jitterFactor float64, sliding bool, stopCh <-chan struct{}) {
+ var t *time.Timer
+ var sawTimeout bool
+
+ for {
+ select {
+ case <-stopCh:
+ return
+ default:
+ }
+
+ jitteredPeriod := period
+ if jitterFactor > 0.0 {
+ jitteredPeriod = Jitter(period, jitterFactor)
+ }
+
+ if !sliding {
+ t = resetOrReuseTimer(t, jitteredPeriod, sawTimeout)
+ }
+
+ func() {
+ defer runtime.HandleCrash()
+ f()
+ }()
+
+ if sliding {
+ t = resetOrReuseTimer(t, jitteredPeriod, sawTimeout)
+ }
+
+ // NOTE: b/c there is no priority selection in golang
+ // it is possible for this to race, meaning we could
+ // trigger t.C and stopCh, and t.C select falls through.
+ // In order to mitigate we re-check stopCh at the beginning
+ // of every loop to prevent extra executions of f().
+ select {
+ case <-stopCh:
+ return
+ case <-t.C:
+ sawTimeout = true
+ }
+ }
+}
+
+// Jitter returns a time.Duration between duration and duration + maxFactor *
+// duration.
+//
+// This allows clients to avoid converging on periodic behavior. If maxFactor
+// is 0.0, a suggested default value will be chosen.
+func Jitter(duration time.Duration, maxFactor float64) time.Duration {
+ if maxFactor <= 0.0 {
+ maxFactor = 1.0
+ }
+ wait := duration + time.Duration(rand.Float64()*maxFactor*float64(duration))
+ return wait
+}
+
+// ErrWaitTimeout is returned when the condition exited without success.
+var ErrWaitTimeout = errors.New("timed out waiting for the condition")
+
+// ConditionFunc returns true if the condition is satisfied, or an error
+// if the loop should be aborted.
+type ConditionFunc func() (done bool, err error)
+
+// Backoff holds parameters applied to a Backoff function.
+type Backoff struct {
+ Duration time.Duration // the base duration
+ Factor float64 // Duration is multiplied by factor each iteration
+ Jitter float64 // The amount of jitter applied each iteration
+ Steps int // Exit with error after this many steps
+}
+
+// ExponentialBackoff repeats a condition check with exponential backoff.
+//
+// It checks the condition up to Steps times, increasing the wait by multiplying
+// the previous duration by Factor.
+//
+// If Jitter is greater than zero, a random amount of each duration is added
+// (between duration and duration*(1+jitter)).
+//
+// If the condition never returns true, ErrWaitTimeout is returned. All other
+// errors terminate immediately.
+func ExponentialBackoff(backoff Backoff, condition ConditionFunc) error {
+ duration := backoff.Duration
+ for i := 0; i < backoff.Steps; i++ {
+ if i != 0 {
+ adjusted := duration
+ if backoff.Jitter > 0.0 {
+ adjusted = Jitter(duration, backoff.Jitter)
+ }
+ time.Sleep(adjusted)
+ duration = time.Duration(float64(duration) * backoff.Factor)
+ }
+ if ok, err := condition(); err != nil || ok {
+ return err
+ }
+ }
+ return ErrWaitTimeout
+}
+
+// Poll tries a condition func until it returns true, an error, or the timeout
+// is reached.
+//
+// Poll always waits the interval before the run of 'condition'.
+// 'condition' will always be invoked at least once.
+//
+// Some intervals may be missed if the condition takes too long or the time
+// window is too short.
+//
+// If you want to Poll something forever, see PollInfinite.
+func Poll(interval, timeout time.Duration, condition ConditionFunc) error {
+ return pollInternal(poller(interval, timeout), condition)
+}
+
+func pollInternal(wait WaitFunc, condition ConditionFunc) error {
+ done := make(chan struct{})
+ defer close(done)
+ return WaitFor(wait, condition, done)
+}
+
+// PollImmediate tries a condition func until it returns true, an error, or the timeout
+// is reached.
+//
+// Poll always checks 'condition' before waiting for the interval. 'condition'
+// will always be invoked at least once.
+//
+// Some intervals may be missed if the condition takes too long or the time
+// window is too short.
+//
+// If you want to Poll something forever, see PollInfinite.
+func PollImmediate(interval, timeout time.Duration, condition ConditionFunc) error {
+ return pollImmediateInternal(poller(interval, timeout), condition)
+}
+
+func pollImmediateInternal(wait WaitFunc, condition ConditionFunc) error {
+ done, err := condition()
+ if err != nil {
+ return err
+ }
+ if done {
+ return nil
+ }
+ return pollInternal(wait, condition)
+}
+
+// PollInfinite tries a condition func until it returns true or an error
+//
+// PollInfinite always waits the interval before the run of 'condition'.
+//
+// Some intervals may be missed if the condition takes too long or the time
+// window is too short.
+func PollInfinite(interval time.Duration, condition ConditionFunc) error {
+ done := make(chan struct{})
+ defer close(done)
+ return PollUntil(interval, condition, done)
+}
+
+// PollImmediateInfinite tries a condition func until it returns true or an error
+//
+// PollImmediateInfinite runs the 'condition' before waiting for the interval.
+//
+// Some intervals may be missed if the condition takes too long or the time
+// window is too short.
+func PollImmediateInfinite(interval time.Duration, condition ConditionFunc) error {
+ done, err := condition()
+ if err != nil {
+ return err
+ }
+ if done {
+ return nil
+ }
+ return PollInfinite(interval, condition)
+}
+
+// PollUntil tries a condition func until it returns true, an error or stopCh is
+// closed.
+//
+// PollUntil always waits interval before the first run of 'condition'.
+// 'condition' will always be invoked at least once.
+func PollUntil(interval time.Duration, condition ConditionFunc, stopCh <-chan struct{}) error {
+ return WaitFor(poller(interval, 0), condition, stopCh)
+}
+
+// PollImmediateUntil tries a condition func until it returns true, an error or stopCh is closed.
+//
+// PollImmediateUntil runs the 'condition' before waiting for the interval.
+// 'condition' will always be invoked at least once.
+func PollImmediateUntil(interval time.Duration, condition ConditionFunc, stopCh <-chan struct{}) error {
+ done, err := condition()
+ if err != nil {
+ return err
+ }
+ if done {
+ return nil
+ }
+ select {
+ case <-stopCh:
+ return ErrWaitTimeout
+ default:
+ return PollUntil(interval, condition, stopCh)
+ }
+}
+
+// WaitFunc creates a channel that receives an item every time a test
+// should be executed and is closed when the last test should be invoked.
+type WaitFunc func(done <-chan struct{}) <-chan struct{}
+
+// WaitFor continually checks 'fn' as driven by 'wait'.
+//
+// WaitFor gets a channel from 'wait()'', and then invokes 'fn' once for every value
+// placed on the channel and once more when the channel is closed.
+//
+// If 'fn' returns an error the loop ends and that error is returned, and if
+// 'fn' returns true the loop ends and nil is returned.
+//
+// ErrWaitTimeout will be returned if the channel is closed without fn ever
+// returning true.
+func WaitFor(wait WaitFunc, fn ConditionFunc, done <-chan struct{}) error {
+ c := wait(done)
+ for {
+ _, open := <-c
+ ok, err := fn()
+ if err != nil {
+ return err
+ }
+ if ok {
+ return nil
+ }
+ if !open {
+ break
+ }
+ }
+ return ErrWaitTimeout
+}
+
+// poller returns a WaitFunc that will send to the channel every interval until
+// timeout has elapsed and then closes the channel.
+//
+// Over very short intervals you may receive no ticks before the channel is
+// closed. A timeout of 0 is interpreted as an infinity.
+//
+// Output ticks are not buffered. If the channel is not ready to receive an
+// item, the tick is skipped.
+func poller(interval, timeout time.Duration) WaitFunc {
+ return WaitFunc(func(done <-chan struct{}) <-chan struct{} {
+ ch := make(chan struct{})
+
+ go func() {
+ defer close(ch)
+
+ tick := time.NewTicker(interval)
+ defer tick.Stop()
+
+ var after <-chan time.Time
+ if timeout != 0 {
+ // time.After is more convenient, but it
+ // potentially leaves timers around much longer
+ // than necessary if we exit early.
+ timer := time.NewTimer(timeout)
+ after = timer.C
+ defer timer.Stop()
+ }
+
+ for {
+ select {
+ case <-tick.C:
+ // If the consumer isn't ready for this signal drop it and
+ // check the other channels.
+ select {
+ case ch <- struct{}{}:
+ default:
+ }
+ case <-after:
+ return
+ case <-done:
+ return
+ }
+ }
+ }()
+
+ return ch
+ })
+}
+
+// resetOrReuseTimer avoids allocating a new timer if one is already in use.
+// Not safe for multiple threads.
+func resetOrReuseTimer(t *time.Timer, d time.Duration, sawTimeout bool) *time.Timer {
+ if t == nil {
+ return time.NewTimer(d)
+ }
+ if !t.Stop() && !sawTimeout {
+ <-t.C
+ }
+ t.Reset(d)
+ return t
+}
diff --git a/metrics-server/vendor/k8s.io/apimachinery/pkg/util/waitgroup/doc.go b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/waitgroup/doc.go
new file mode 100644
index 0000000..a6f29cd
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/waitgroup/doc.go
@@ -0,0 +1,19 @@
+/*
+Copyright 2017 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Package waitgroup implements SafeWaitGroup wrap of sync.WaitGroup.
+// Add with positive delta when waiting will fail, to prevent sync.WaitGroup race issue.
+package waitgroup // import "k8s.io/apimachinery/pkg/util/waitgroup"
diff --git a/metrics-server/vendor/k8s.io/apimachinery/pkg/util/waitgroup/waitgroup.go b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/waitgroup/waitgroup.go
new file mode 100644
index 0000000..e080a5e
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/waitgroup/waitgroup.go
@@ -0,0 +1,57 @@
+/*
+Copyright 2017 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package waitgroup
+
+import (
+ "fmt"
+ "sync"
+)
+
+// SafeWaitGroup must not be copied after first use.
+type SafeWaitGroup struct {
+ wg sync.WaitGroup
+ mu sync.RWMutex
+ // wait indicate whether Wait is called, if true,
+ // then any Add with positive delta will return error.
+ wait bool
+}
+
+// Add adds delta, which may be negative, similar to sync.WaitGroup.
+// If Add with a positive delta happens after Wait, it will return error,
+// which prevent unsafe Add.
+func (wg *SafeWaitGroup) Add(delta int) error {
+ wg.mu.RLock()
+ defer wg.mu.RUnlock()
+ if wg.wait && delta > 0 {
+ return fmt.Errorf("add with positive delta after Wait is forbidden")
+ }
+ wg.wg.Add(delta)
+ return nil
+}
+
+// Done decrements the WaitGroup counter.
+func (wg *SafeWaitGroup) Done() {
+ wg.wg.Done()
+}
+
+// Wait blocks until the WaitGroup counter is zero.
+func (wg *SafeWaitGroup) Wait() {
+ wg.mu.Lock()
+ wg.wait = true
+ wg.mu.Unlock()
+ wg.wg.Wait()
+}
diff --git a/metrics-server/vendor/k8s.io/apimachinery/pkg/util/yaml/decoder.go b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/yaml/decoder.go
new file mode 100644
index 0000000..3cd8551
--- /dev/null
+++ b/metrics-server/vendor/k8s.io/apimachinery/pkg/util/yaml/decoder.go
@@ -0,0 +1,346 @@
+/*
+Copyright 2014 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package yaml
+
+import (
+ "bufio"
+ "bytes"
+ "encoding/json"
+ "fmt"
+ "io"
+ "io/ioutil"
+ "strings"
+ "unicode"
+
+ "github.com/ghodss/yaml"
+ "github.com/golang/glog"
+)
+
+// ToJSON converts a single YAML document into a JSON document
+// or returns an error. If the document appears to be JSON the
+// YAML decoding path is not used (so that error messages are
+// JSON specific).
+func ToJSON(data []byte) ([]byte, error) {
+ if hasJSONPrefix(data) {
+ return data, nil
+ }
+ return yaml.YAMLToJSON(data)
+}
+
+// YAMLToJSONDecoder decodes YAML documents from an io.Reader by
+// separating individual documents. It first converts the YAML
+// body to JSON, then unmarshals the JSON.
+type YAMLToJSONDecoder struct {
+ reader Reader
+}
+
+// NewYAMLToJSONDecoder decodes YAML documents from the provided
+// stream in chunks by converting each document (as defined by
+// the YAML spec) into its own chunk, converting it to JSON via
+// yaml.YAMLToJSON, and then passing it to json.Decoder.
+func NewYAMLToJSONDecoder(r io.Reader) *YAMLToJSONDecoder {
+ reader := bufio.NewReader(r)
+ return &YAMLToJSONDecoder{
+ reader: NewYAMLReader(reader),
+ }
+}
+
+// Decode reads a YAML document as JSON from the stream or returns
+// an error. The decoding rules match json.Unmarshal, not
+// yaml.Unmarshal.
+func (d *YAMLToJSONDecoder) Decode(into interface{}) error {
+ bytes, err := d.reader.Read()
+ if err != nil && err != io.EOF {
+ return err
+ }
+
+ if len(bytes) != 0 {
+ err := yaml.Unmarshal(bytes, into)
+ if err != nil {
+ return YAMLSyntaxError{err}
+ }
+ }
+ return err
+}
+
+// YAMLDecoder reads chunks of objects and returns ErrShortBuffer if
+// the data is not sufficient.
+type YAMLDecoder struct {
+ r io.ReadCloser
+ scanner *bufio.Scanner
+ remaining []byte
+}
+
+// NewDocumentDecoder decodes YAML documents from the provided
+// stream in chunks by converting each document (as defined by
+// the YAML spec) into its own chunk. io.ErrShortBuffer will be
+// returned if the entire buffer could not be read to assist
+// the caller in framing the chunk.
+func NewDocumentDecoder(r io.ReadCloser) io.ReadCloser {
+ scanner := bufio.NewScanner(r)
+ scanner.Split(splitYAMLDocument)
+ return &YAMLDecoder{
+ r: r,
+ scanner: scanner,
+ }
+}
+
+// Read reads the previous slice into the buffer, or attempts to read
+// the next chunk.
+// TODO: switch to readline approach.
+func (d *YAMLDecoder) Read(data []byte) (n int, err error) {
+ left := len(d.remaining)
+ if left == 0 {
+ // return the next chunk from the stream
+ if !d.scanner.Scan() {
+ err := d.scanner.Err()
+ if err == nil {
+ err = io.EOF
+ }
+ return 0, err
+ }
+ out := d.scanner.Bytes()
+ d.remaining = out
+ left = len(out)
+ }
+
+ // fits within data
+ if left <= len(data) {
+ copy(data, d.remaining)
+ d.remaining = nil
+ return left, nil
+ }
+
+ // caller will need to reread
+ copy(data, d.remaining[:len(data)])
+ d.remaining = d.remaining[len(data):]
+ return len(data), io.ErrShortBuffer
+}
+
+func (d *YAMLDecoder) Close() error {
+ return d.r.Close()
+}
+
+const yamlSeparator = "\n---"
+const separator = "---"
+
+// splitYAMLDocument is a bufio.SplitFunc for splitting YAML streams into individual documents.
+func splitYAMLDocument(data []byte, atEOF bool) (advance int, token []byte, err error) {
+ if atEOF && len(data) == 0 {
+ return 0, nil, nil
+ }
+ sep := len([]byte(yamlSeparator))
+ if i := bytes.Index(data, []byte(yamlSeparator)); i >= 0 {
+ // We have a potential document terminator
+ i += sep
+ after := data[i:]
+ if len(after) == 0 {
+ // we can't read any more characters
+ if atEOF {
+ return len(data), data[:len(data)-sep], nil
+ }
+ return 0, nil, nil
+ }
+ if j := bytes.IndexByte(after, '\n'); j >= 0 {
+ return i + j + 1, data[0 : i-sep], nil
+ }
+ return 0, nil, nil
+ }
+ // If we're at EOF, we have a final, non-terminated line. Return it.
+ if atEOF {
+ return len(data), data, nil
+ }
+ // Request more data.
+ return 0, nil, nil
+}
+
+// decoder is a convenience interface for Decode.
+type decoder interface {
+ Decode(into interface{}) error
+}
+
+// YAMLOrJSONDecoder attempts to decode a stream of JSON documents or
+// YAML documents by sniffing for a leading { character.
+type YAMLOrJSONDecoder struct {
+ r io.Reader
+ bufferSize int
+
+ decoder decoder
+ rawData []byte
+}
+
+type JSONSyntaxError struct {
+ Line int
+ Err error
+}
+
+func (e JSONSyntaxError) Error() string {
+ return fmt.Sprintf("json: line %d: %s", e.Line, e.Err.Error())
+}
+
+type YAMLSyntaxError struct {
+ err error
+}
+
+func (e YAMLSyntaxError) Error() string {
+ return e.err.Error()
+}
+
+// NewYAMLOrJSONDecoder returns a decoder that will process YAML documents
+// or JSON documents from the given reader as a stream. bufferSize determines
+// how far into the stream the decoder will look to figure out whether this
+// is a JSON stream (has whitespace followed by an open brace).
+func NewYAMLOrJSONDecoder(r io.Reader, bufferSize int) *YAMLOrJSONDecoder {
+ return &YAMLOrJSONDecoder{
+ r: r,
+ bufferSize: bufferSize,
+ }
+}
+
+// Decode unmarshals the next object from the underlying stream into the
+// provide object, or returns an error.
+func (d *YAMLOrJSONDecoder) Decode(into interface{}) error {
+ if d.decoder == nil {
+ buffer, origData, isJSON := GuessJSONStream(d.r, d.bufferSize)
+ if isJSON {
+ glog.V(4).Infof("decoding stream as JSON")
+ d.decoder = json.NewDecoder(buffer)
+ d.rawData = origData
+ } else {
+ glog.V(4).Infof("decoding stream as YAML")
+ d.decoder = NewYAMLToJSONDecoder(buffer)
+ }
+ }
+ err := d.decoder.Decode(into)
+ if jsonDecoder, ok := d.decoder.(*json.Decoder); ok {
+ if syntax, ok := err.(*json.SyntaxError); ok {
+ data, readErr := ioutil.ReadAll(jsonDecoder.Buffered())
+ if readErr != nil {
+ glog.V(4).Infof("reading stream failed: %v", readErr)
+ }
+ js := string(data)
+
+ // if contents from io.Reader are not complete,
+ // use the original raw data to prevent panic
+ if int64(len(js)) <= syntax.Offset {
+ js = string(d.rawData)
+ }
+
+ start := strings.LastIndex(js[:syntax.Offset], "\n") + 1
+ line := strings.Count(js[:start], "\n")
+ return JSONSyntaxError{
+ Line: line,
+ Err: fmt.Errorf(syntax.Error()),
+ }
+ }
+ }
+ return err
+}
+
+type Reader interface {
+ Read() ([]byte, error)
+}
+
+type YAMLReader struct {
+ reader Reader
+}
+
+func NewYAMLReader(r *bufio.Reader) *YAMLReader {
+ return &YAMLReader{
+ reader: &LineReader{reader: r},
+ }
+}
+
+// Read returns a full YAML document.
+func (r *YAMLReader) Read() ([]byte, error) {
+ var buffer bytes.Buffer
+ for {
+ line, err := r.reader.Read()
+ if err != nil && err != io.EOF {
+ return nil, err
+ }
+
+ sep := len([]byte(separator))
+ if i := bytes.Index(line, []byte(separator)); i == 0 {
+ // We have a potential document terminator
+ i += sep
+ after := line[i:]
+ if len(strings.TrimRightFunc(string(after), unicode.IsSpace)) == 0 {
+ if buffer.Len() != 0 {
+ return buffer.Bytes(), nil
+ }
+ if err == io.EOF {
+ return nil, err
+ }
+ }
+ }
+ if err == io.EOF {
+ if buffer.Len() != 0 {
+ // If we're at EOF, we have a final, non-terminated line. Return it.
+ return buffer.Bytes(), nil
+ }
+ return nil, err
+ }
+ buffer.Write(line)
+ }
+}
+
+type LineReader struct {
+ reader *bufio.Reader
+}
+
+// Read returns a single line (with '\n' ended) from the underlying reader.
+// An error is returned iff there is an error with the underlying reader.
+func (r *LineReader) Read() ([]byte, error) {
+ var (
+ isPrefix bool = true
+ err error = nil
+ line []byte
+ buffer bytes.Buffer
+ )
+
+ for isPrefix && err == nil {
+ line, isPrefix, err = r.reader.ReadLine()
+ buffer.Write(line)
+ }
+ buffer.WriteByte('\n')
+ return buffer.Bytes(), err
+}
+
+// GuessJSONStream scans the provided reader up to size, looking
+// for an open brace indicating this is JSON. It will return the
+// bufio.Reader it creates for the consumer.
+func GuessJSONStream(r io.Reader, size int) (io.Reader, []byte, bool) {
+ buffer := bufio.NewReaderSize(r, size)
+ b, _ := buffer.Peek(size)
+ return buffer, b, hasJSONPrefix(b)
+}
+
+var jsonPrefix = []byte("{")
+
+// hasJSONPrefix returns true if the provided buffer appears to start with
+// a JSON open brace.
+func hasJSONPrefix(buf []byte) bool {
+ return hasPrefix(buf, jsonPrefix)
+}
+
+// Return true if the first non-whitespace bytes in buf is
+// prefix.
+func hasPrefix(buf []byte, prefix []byte) bool {
+ trim := bytes.TrimLeftFunc(buf, unicode.IsSpace)
+ return bytes.HasPrefix(trim, prefix)
+}