mirror of
https://github.com/ceph/ceph-csi.git
synced 2025-06-13 02:33:34 +00:00
vendor update for E2E framework
Signed-off-by: Madhu Rajanna <madhupr007@gmail.com>
This commit is contained in:
83
vendor/k8s.io/apimachinery/pkg/util/cache/cache.go
generated
vendored
Normal file
83
vendor/k8s.io/apimachinery/pkg/util/cache/cache.go
generated
vendored
Normal file
@ -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
|
||||
}
|
102
vendor/k8s.io/apimachinery/pkg/util/cache/lruexpirecache.go
generated
vendored
Normal file
102
vendor/k8s.io/apimachinery/pkg/util/cache/lruexpirecache.go
generated
vendored
Normal file
@ -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()
|
||||
}
|
313
vendor/k8s.io/apimachinery/pkg/util/diff/diff.go
generated
vendored
Normal file
313
vendor/k8s.io/apimachinery/pkg/util/diff/diff.go
generated
vendored
Normal file
@ -0,0 +1,313 @@
|
||||
/*
|
||||
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)
|
||||
|
||||
if aObj != nil && bObj != nil {
|
||||
if aType, bType := fmt.Sprintf("%T", aObj), fmt.Sprintf("%T", bObj); aType != bType {
|
||||
a = fmt.Sprintf("%s (%s)", a, aType)
|
||||
b = fmt.Sprintf("%s (%s)", b, bType)
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
89
vendor/k8s.io/apimachinery/pkg/util/duration/duration.go
generated
vendored
Normal file
89
vendor/k8s.io/apimachinery/pkg/util/duration/duration.go
generated
vendored
Normal file
@ -0,0 +1,89 @@
|
||||
/*
|
||||
Copyright 2018 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 duration
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
)
|
||||
|
||||
// ShortHumanDuration returns a succint representation of the provided duration
|
||||
// with limited precision for consumption by humans.
|
||||
func ShortHumanDuration(d time.Duration) string {
|
||||
// Allow deviation no more than 2 seconds(excluded) to tolerate machine time
|
||||
// inconsistence, it can be considered as almost now.
|
||||
if seconds := int(d.Seconds()); seconds < -1 {
|
||||
return fmt.Sprintf("<invalid>")
|
||||
} else if seconds < 0 {
|
||||
return fmt.Sprintf("0s")
|
||||
} else if seconds < 60 {
|
||||
return fmt.Sprintf("%ds", seconds)
|
||||
} else if minutes := int(d.Minutes()); minutes < 60 {
|
||||
return fmt.Sprintf("%dm", minutes)
|
||||
} else if hours := int(d.Hours()); hours < 24 {
|
||||
return fmt.Sprintf("%dh", hours)
|
||||
} else if hours < 24*365 {
|
||||
return fmt.Sprintf("%dd", hours/24)
|
||||
}
|
||||
return fmt.Sprintf("%dy", int(d.Hours()/24/365))
|
||||
}
|
||||
|
||||
// HumanDuration returns a succint representation of the provided duration
|
||||
// with limited precision for consumption by humans. It provides ~2-3 significant
|
||||
// figures of duration.
|
||||
func HumanDuration(d time.Duration) string {
|
||||
// Allow deviation no more than 2 seconds(excluded) to tolerate machine time
|
||||
// inconsistence, it can be considered as almost now.
|
||||
if seconds := int(d.Seconds()); seconds < -1 {
|
||||
return fmt.Sprintf("<invalid>")
|
||||
} else if seconds < 0 {
|
||||
return fmt.Sprintf("0s")
|
||||
} else if seconds < 60*2 {
|
||||
return fmt.Sprintf("%ds", seconds)
|
||||
}
|
||||
minutes := int(d / time.Minute)
|
||||
if minutes < 10 {
|
||||
s := int(d/time.Second) % 60
|
||||
if s == 0 {
|
||||
return fmt.Sprintf("%dm", minutes)
|
||||
}
|
||||
return fmt.Sprintf("%dm%ds", minutes, s)
|
||||
} else if minutes < 60*3 {
|
||||
return fmt.Sprintf("%dm", minutes)
|
||||
}
|
||||
hours := int(d / time.Hour)
|
||||
if hours < 8 {
|
||||
m := int(d/time.Minute) % 60
|
||||
if m == 0 {
|
||||
return fmt.Sprintf("%dh", hours)
|
||||
}
|
||||
return fmt.Sprintf("%dh%dm", hours, m)
|
||||
} else if hours < 48 {
|
||||
return fmt.Sprintf("%dh", hours)
|
||||
} else if hours < 24*8 {
|
||||
h := hours % 24
|
||||
if h == 0 {
|
||||
return fmt.Sprintf("%dd", hours/24)
|
||||
}
|
||||
return fmt.Sprintf("%dd%dh", hours/24, h)
|
||||
} else if hours < 24*365*2 {
|
||||
return fmt.Sprintf("%dd", hours/24)
|
||||
} else if hours < 24*365*8 {
|
||||
return fmt.Sprintf("%dy%dd", hours/24/365, (hours/24)%365)
|
||||
}
|
||||
return fmt.Sprintf("%dy", int(hours/24/365))
|
||||
}
|
19
vendor/k8s.io/apimachinery/pkg/util/httpstream/doc.go
generated
vendored
Normal file
19
vendor/k8s.io/apimachinery/pkg/util/httpstream/doc.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
/*
|
||||
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 httpstream adds multiplexed streaming support to HTTP requests and
|
||||
// responses via connection upgrades.
|
||||
package httpstream // import "k8s.io/apimachinery/pkg/util/httpstream"
|
149
vendor/k8s.io/apimachinery/pkg/util/httpstream/httpstream.go
generated
vendored
Normal file
149
vendor/k8s.io/apimachinery/pkg/util/httpstream/httpstream.go
generated
vendored
Normal file
@ -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 httpstream
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
HeaderConnection = "Connection"
|
||||
HeaderUpgrade = "Upgrade"
|
||||
HeaderProtocolVersion = "X-Stream-Protocol-Version"
|
||||
HeaderAcceptedProtocolVersions = "X-Accepted-Stream-Protocol-Versions"
|
||||
)
|
||||
|
||||
// NewStreamHandler defines a function that is called when a new Stream is
|
||||
// received. If no error is returned, the Stream is accepted; otherwise,
|
||||
// the stream is rejected. After the reply frame has been sent, replySent is closed.
|
||||
type NewStreamHandler func(stream Stream, replySent <-chan struct{}) error
|
||||
|
||||
// NoOpNewStreamHandler is a stream handler that accepts a new stream and
|
||||
// performs no other logic.
|
||||
func NoOpNewStreamHandler(stream Stream, replySent <-chan struct{}) error { return nil }
|
||||
|
||||
// Dialer knows how to open a streaming connection to a server.
|
||||
type Dialer interface {
|
||||
|
||||
// Dial opens a streaming connection to a server using one of the protocols
|
||||
// specified (in order of most preferred to least preferred).
|
||||
Dial(protocols ...string) (Connection, string, error)
|
||||
}
|
||||
|
||||
// UpgradeRoundTripper is a type of http.RoundTripper that is able to upgrade
|
||||
// HTTP requests to support multiplexed bidirectional streams. After RoundTrip()
|
||||
// is invoked, if the upgrade is successful, clients may retrieve the upgraded
|
||||
// connection by calling UpgradeRoundTripper.Connection().
|
||||
type UpgradeRoundTripper interface {
|
||||
http.RoundTripper
|
||||
// NewConnection validates the response and creates a new Connection.
|
||||
NewConnection(resp *http.Response) (Connection, error)
|
||||
}
|
||||
|
||||
// ResponseUpgrader knows how to upgrade HTTP requests and responses to
|
||||
// add streaming support to them.
|
||||
type ResponseUpgrader interface {
|
||||
// UpgradeResponse upgrades an HTTP response to one that supports multiplexed
|
||||
// streams. newStreamHandler will be called asynchronously whenever the
|
||||
// other end of the upgraded connection creates a new stream.
|
||||
UpgradeResponse(w http.ResponseWriter, req *http.Request, newStreamHandler NewStreamHandler) Connection
|
||||
}
|
||||
|
||||
// Connection represents an upgraded HTTP connection.
|
||||
type Connection interface {
|
||||
// CreateStream creates a new Stream with the supplied headers.
|
||||
CreateStream(headers http.Header) (Stream, error)
|
||||
// Close resets all streams and closes the connection.
|
||||
Close() error
|
||||
// CloseChan returns a channel that is closed when the underlying connection is closed.
|
||||
CloseChan() <-chan bool
|
||||
// SetIdleTimeout sets the amount of time the connection may remain idle before
|
||||
// it is automatically closed.
|
||||
SetIdleTimeout(timeout time.Duration)
|
||||
}
|
||||
|
||||
// Stream represents a bidirectional communications channel that is part of an
|
||||
// upgraded connection.
|
||||
type Stream interface {
|
||||
io.ReadWriteCloser
|
||||
// Reset closes both directions of the stream, indicating that neither client
|
||||
// or server can use it any more.
|
||||
Reset() error
|
||||
// Headers returns the headers used to create the stream.
|
||||
Headers() http.Header
|
||||
// Identifier returns the stream's ID.
|
||||
Identifier() uint32
|
||||
}
|
||||
|
||||
// IsUpgradeRequest returns true if the given request is a connection upgrade request
|
||||
func IsUpgradeRequest(req *http.Request) bool {
|
||||
for _, h := range req.Header[http.CanonicalHeaderKey(HeaderConnection)] {
|
||||
if strings.Contains(strings.ToLower(h), strings.ToLower(HeaderUpgrade)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func negotiateProtocol(clientProtocols, serverProtocols []string) string {
|
||||
for i := range clientProtocols {
|
||||
for j := range serverProtocols {
|
||||
if clientProtocols[i] == serverProtocols[j] {
|
||||
return clientProtocols[i]
|
||||
}
|
||||
}
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
// Handshake performs a subprotocol negotiation. If the client did request a
|
||||
// subprotocol, Handshake will select the first common value found in
|
||||
// serverProtocols. If a match is found, Handshake adds a response header
|
||||
// indicating the chosen subprotocol. If no match is found, HTTP forbidden is
|
||||
// returned, along with a response header containing the list of protocols the
|
||||
// server can accept.
|
||||
func Handshake(req *http.Request, w http.ResponseWriter, serverProtocols []string) (string, error) {
|
||||
clientProtocols := req.Header[http.CanonicalHeaderKey(HeaderProtocolVersion)]
|
||||
if len(clientProtocols) == 0 {
|
||||
// Kube 1.0 clients didn't support subprotocol negotiation.
|
||||
// TODO require clientProtocols once Kube 1.0 is no longer supported
|
||||
return "", nil
|
||||
}
|
||||
|
||||
if len(serverProtocols) == 0 {
|
||||
// Kube 1.0 servers didn't support subprotocol negotiation. This is mainly for testing.
|
||||
// TODO require serverProtocols once Kube 1.0 is no longer supported
|
||||
return "", nil
|
||||
}
|
||||
|
||||
negotiatedProtocol := negotiateProtocol(clientProtocols, serverProtocols)
|
||||
if len(negotiatedProtocol) == 0 {
|
||||
for i := range serverProtocols {
|
||||
w.Header().Add(HeaderAcceptedProtocolVersions, serverProtocols[i])
|
||||
}
|
||||
err := fmt.Errorf("unable to upgrade: unable to negotiate protocol: client supports %v, server accepts %v", clientProtocols, serverProtocols)
|
||||
http.Error(w, err.Error(), http.StatusForbidden)
|
||||
return "", err
|
||||
}
|
||||
|
||||
w.Header().Add(HeaderProtocolVersion, negotiatedProtocol)
|
||||
return negotiatedProtocol, nil
|
||||
}
|
145
vendor/k8s.io/apimachinery/pkg/util/httpstream/spdy/connection.go
generated
vendored
Normal file
145
vendor/k8s.io/apimachinery/pkg/util/httpstream/spdy/connection.go
generated
vendored
Normal file
@ -0,0 +1,145 @@
|
||||
/*
|
||||
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 spdy
|
||||
|
||||
import (
|
||||
"net"
|
||||
"net/http"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/docker/spdystream"
|
||||
"k8s.io/apimachinery/pkg/util/httpstream"
|
||||
"k8s.io/klog"
|
||||
)
|
||||
|
||||
// connection maintains state about a spdystream.Connection and its associated
|
||||
// streams.
|
||||
type connection struct {
|
||||
conn *spdystream.Connection
|
||||
streams []httpstream.Stream
|
||||
streamLock sync.Mutex
|
||||
newStreamHandler httpstream.NewStreamHandler
|
||||
}
|
||||
|
||||
// NewClientConnection creates a new SPDY client connection.
|
||||
func NewClientConnection(conn net.Conn) (httpstream.Connection, error) {
|
||||
spdyConn, err := spdystream.NewConnection(conn, false)
|
||||
if err != nil {
|
||||
defer conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return newConnection(spdyConn, httpstream.NoOpNewStreamHandler), nil
|
||||
}
|
||||
|
||||
// NewServerConnection creates a new SPDY server connection. newStreamHandler
|
||||
// will be invoked when the server receives a newly created stream from the
|
||||
// client.
|
||||
func NewServerConnection(conn net.Conn, newStreamHandler httpstream.NewStreamHandler) (httpstream.Connection, error) {
|
||||
spdyConn, err := spdystream.NewConnection(conn, true)
|
||||
if err != nil {
|
||||
defer conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return newConnection(spdyConn, newStreamHandler), nil
|
||||
}
|
||||
|
||||
// newConnection returns a new connection wrapping conn. newStreamHandler
|
||||
// will be invoked when the server receives a newly created stream from the
|
||||
// client.
|
||||
func newConnection(conn *spdystream.Connection, newStreamHandler httpstream.NewStreamHandler) httpstream.Connection {
|
||||
c := &connection{conn: conn, newStreamHandler: newStreamHandler}
|
||||
go conn.Serve(c.newSpdyStream)
|
||||
return c
|
||||
}
|
||||
|
||||
// createStreamResponseTimeout indicates how long to wait for the other side to
|
||||
// acknowledge the new stream before timing out.
|
||||
const createStreamResponseTimeout = 30 * time.Second
|
||||
|
||||
// Close first sends a reset for all of the connection's streams, and then
|
||||
// closes the underlying spdystream.Connection.
|
||||
func (c *connection) Close() error {
|
||||
c.streamLock.Lock()
|
||||
for _, s := range c.streams {
|
||||
// calling Reset instead of Close ensures that all streams are fully torn down
|
||||
s.Reset()
|
||||
}
|
||||
c.streams = make([]httpstream.Stream, 0)
|
||||
c.streamLock.Unlock()
|
||||
|
||||
// now that all streams are fully torn down, it's safe to call close on the underlying connection,
|
||||
// which should be able to terminate immediately at this point, instead of waiting for any
|
||||
// remaining graceful stream termination.
|
||||
return c.conn.Close()
|
||||
}
|
||||
|
||||
// CreateStream creates a new stream with the specified headers and registers
|
||||
// it with the connection.
|
||||
func (c *connection) CreateStream(headers http.Header) (httpstream.Stream, error) {
|
||||
stream, err := c.conn.CreateStream(headers, nil, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err = stream.WaitTimeout(createStreamResponseTimeout); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
c.registerStream(stream)
|
||||
return stream, nil
|
||||
}
|
||||
|
||||
// registerStream adds the stream s to the connection's list of streams that
|
||||
// it owns.
|
||||
func (c *connection) registerStream(s httpstream.Stream) {
|
||||
c.streamLock.Lock()
|
||||
c.streams = append(c.streams, s)
|
||||
c.streamLock.Unlock()
|
||||
}
|
||||
|
||||
// CloseChan returns a channel that, when closed, indicates that the underlying
|
||||
// spdystream.Connection has been closed.
|
||||
func (c *connection) CloseChan() <-chan bool {
|
||||
return c.conn.CloseChan()
|
||||
}
|
||||
|
||||
// newSpdyStream is the internal new stream handler used by spdystream.Connection.Serve.
|
||||
// It calls connection's newStreamHandler, giving it the opportunity to accept or reject
|
||||
// the stream. If newStreamHandler returns an error, the stream is rejected. If not, the
|
||||
// stream is accepted and registered with the connection.
|
||||
func (c *connection) newSpdyStream(stream *spdystream.Stream) {
|
||||
replySent := make(chan struct{})
|
||||
err := c.newStreamHandler(stream, replySent)
|
||||
rejectStream := (err != nil)
|
||||
if rejectStream {
|
||||
klog.Warningf("Stream rejected: %v", err)
|
||||
stream.Reset()
|
||||
return
|
||||
}
|
||||
|
||||
c.registerStream(stream)
|
||||
stream.SendReply(http.Header{}, rejectStream)
|
||||
close(replySent)
|
||||
}
|
||||
|
||||
// SetIdleTimeout sets the amount of time the connection may remain idle before
|
||||
// it is automatically closed.
|
||||
func (c *connection) SetIdleTimeout(timeout time.Duration) {
|
||||
c.conn.SetIdleTimeout(timeout)
|
||||
}
|
335
vendor/k8s.io/apimachinery/pkg/util/httpstream/spdy/roundtripper.go
generated
vendored
Normal file
335
vendor/k8s.io/apimachinery/pkg/util/httpstream/spdy/roundtripper.go
generated
vendored
Normal file
@ -0,0 +1,335 @@
|
||||
/*
|
||||
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 spdy
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"context"
|
||||
"crypto/tls"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/http/httputil"
|
||||
"net/url"
|
||||
"strings"
|
||||
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/apimachinery/pkg/runtime/serializer"
|
||||
"k8s.io/apimachinery/pkg/util/httpstream"
|
||||
utilnet "k8s.io/apimachinery/pkg/util/net"
|
||||
"k8s.io/apimachinery/third_party/forked/golang/netutil"
|
||||
)
|
||||
|
||||
// SpdyRoundTripper knows how to upgrade an HTTP request to one that supports
|
||||
// multiplexed streams. After RoundTrip() is invoked, Conn will be set
|
||||
// and usable. SpdyRoundTripper implements the UpgradeRoundTripper interface.
|
||||
type SpdyRoundTripper struct {
|
||||
//tlsConfig holds the TLS configuration settings to use when connecting
|
||||
//to the remote server.
|
||||
tlsConfig *tls.Config
|
||||
|
||||
/* TODO according to http://golang.org/pkg/net/http/#RoundTripper, a RoundTripper
|
||||
must be safe for use by multiple concurrent goroutines. If this is absolutely
|
||||
necessary, we could keep a map from http.Request to net.Conn. In practice,
|
||||
a client will create an http.Client, set the transport to a new insteace of
|
||||
SpdyRoundTripper, and use it a single time, so this hopefully won't be an issue.
|
||||
*/
|
||||
// conn is the underlying network connection to the remote server.
|
||||
conn net.Conn
|
||||
|
||||
// Dialer is the dialer used to connect. Used if non-nil.
|
||||
Dialer *net.Dialer
|
||||
|
||||
// proxier knows which proxy to use given a request, defaults to http.ProxyFromEnvironment
|
||||
// Used primarily for mocking the proxy discovery in tests.
|
||||
proxier func(req *http.Request) (*url.URL, error)
|
||||
|
||||
// followRedirects indicates if the round tripper should examine responses for redirects and
|
||||
// follow them.
|
||||
followRedirects bool
|
||||
// requireSameHostRedirects restricts redirect following to only follow redirects to the same host
|
||||
// as the original request.
|
||||
requireSameHostRedirects bool
|
||||
}
|
||||
|
||||
var _ utilnet.TLSClientConfigHolder = &SpdyRoundTripper{}
|
||||
var _ httpstream.UpgradeRoundTripper = &SpdyRoundTripper{}
|
||||
var _ utilnet.Dialer = &SpdyRoundTripper{}
|
||||
|
||||
// NewRoundTripper creates a new SpdyRoundTripper that will use
|
||||
// the specified tlsConfig.
|
||||
func NewRoundTripper(tlsConfig *tls.Config, followRedirects, requireSameHostRedirects bool) httpstream.UpgradeRoundTripper {
|
||||
return NewSpdyRoundTripper(tlsConfig, followRedirects, requireSameHostRedirects)
|
||||
}
|
||||
|
||||
// NewSpdyRoundTripper creates a new SpdyRoundTripper that will use
|
||||
// the specified tlsConfig. This function is mostly meant for unit tests.
|
||||
func NewSpdyRoundTripper(tlsConfig *tls.Config, followRedirects, requireSameHostRedirects bool) *SpdyRoundTripper {
|
||||
return &SpdyRoundTripper{
|
||||
tlsConfig: tlsConfig,
|
||||
followRedirects: followRedirects,
|
||||
requireSameHostRedirects: requireSameHostRedirects,
|
||||
}
|
||||
}
|
||||
|
||||
// TLSClientConfig implements pkg/util/net.TLSClientConfigHolder for proper TLS checking during
|
||||
// proxying with a spdy roundtripper.
|
||||
func (s *SpdyRoundTripper) TLSClientConfig() *tls.Config {
|
||||
return s.tlsConfig
|
||||
}
|
||||
|
||||
// Dial implements k8s.io/apimachinery/pkg/util/net.Dialer.
|
||||
func (s *SpdyRoundTripper) Dial(req *http.Request) (net.Conn, error) {
|
||||
conn, err := s.dial(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := req.Write(conn); err != nil {
|
||||
conn.Close()
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// dial dials the host specified by req, using TLS if appropriate, optionally
|
||||
// using a proxy server if one is configured via environment variables.
|
||||
func (s *SpdyRoundTripper) dial(req *http.Request) (net.Conn, error) {
|
||||
proxier := s.proxier
|
||||
if proxier == nil {
|
||||
proxier = utilnet.NewProxierWithNoProxyCIDR(http.ProxyFromEnvironment)
|
||||
}
|
||||
proxyURL, err := proxier(req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if proxyURL == nil {
|
||||
return s.dialWithoutProxy(req.Context(), req.URL)
|
||||
}
|
||||
|
||||
// ensure we use a canonical host with proxyReq
|
||||
targetHost := netutil.CanonicalAddr(req.URL)
|
||||
|
||||
// proxying logic adapted from http://blog.h6t.eu/post/74098062923/golang-websocket-with-http-proxy-support
|
||||
proxyReq := http.Request{
|
||||
Method: "CONNECT",
|
||||
URL: &url.URL{},
|
||||
Host: targetHost,
|
||||
}
|
||||
|
||||
if pa := s.proxyAuth(proxyURL); pa != "" {
|
||||
proxyReq.Header = http.Header{}
|
||||
proxyReq.Header.Set("Proxy-Authorization", pa)
|
||||
}
|
||||
|
||||
proxyDialConn, err := s.dialWithoutProxy(req.Context(), proxyURL)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
proxyClientConn := httputil.NewProxyClientConn(proxyDialConn, nil)
|
||||
_, err = proxyClientConn.Do(&proxyReq)
|
||||
if err != nil && err != httputil.ErrPersistEOF {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
rwc, _ := proxyClientConn.Hijack()
|
||||
|
||||
if req.URL.Scheme != "https" {
|
||||
return rwc, nil
|
||||
}
|
||||
|
||||
host, _, err := net.SplitHostPort(targetHost)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
tlsConfig := s.tlsConfig
|
||||
switch {
|
||||
case tlsConfig == nil:
|
||||
tlsConfig = &tls.Config{ServerName: host}
|
||||
case len(tlsConfig.ServerName) == 0:
|
||||
tlsConfig = tlsConfig.Clone()
|
||||
tlsConfig.ServerName = host
|
||||
}
|
||||
|
||||
tlsConn := tls.Client(rwc, tlsConfig)
|
||||
|
||||
// need to manually call Handshake() so we can call VerifyHostname() below
|
||||
if err := tlsConn.Handshake(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return if we were configured to skip validation
|
||||
if tlsConfig.InsecureSkipVerify {
|
||||
return tlsConn, nil
|
||||
}
|
||||
|
||||
if err := tlsConn.VerifyHostname(tlsConfig.ServerName); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return tlsConn, nil
|
||||
}
|
||||
|
||||
// dialWithoutProxy dials the host specified by url, using TLS if appropriate.
|
||||
func (s *SpdyRoundTripper) dialWithoutProxy(ctx context.Context, url *url.URL) (net.Conn, error) {
|
||||
dialAddr := netutil.CanonicalAddr(url)
|
||||
|
||||
if url.Scheme == "http" {
|
||||
if s.Dialer == nil {
|
||||
var d net.Dialer
|
||||
return d.DialContext(ctx, "tcp", dialAddr)
|
||||
} else {
|
||||
return s.Dialer.DialContext(ctx, "tcp", dialAddr)
|
||||
}
|
||||
}
|
||||
|
||||
// TODO validate the TLSClientConfig is set up?
|
||||
var conn *tls.Conn
|
||||
var err error
|
||||
if s.Dialer == nil {
|
||||
conn, err = tls.Dial("tcp", dialAddr, s.tlsConfig)
|
||||
} else {
|
||||
conn, err = tls.DialWithDialer(s.Dialer, "tcp", dialAddr, s.tlsConfig)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Return if we were configured to skip validation
|
||||
if s.tlsConfig != nil && s.tlsConfig.InsecureSkipVerify {
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
host, _, err := net.SplitHostPort(dialAddr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if s.tlsConfig != nil && len(s.tlsConfig.ServerName) > 0 {
|
||||
host = s.tlsConfig.ServerName
|
||||
}
|
||||
err = conn.VerifyHostname(host)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return conn, nil
|
||||
}
|
||||
|
||||
// proxyAuth returns, for a given proxy URL, the value to be used for the Proxy-Authorization header
|
||||
func (s *SpdyRoundTripper) proxyAuth(proxyURL *url.URL) string {
|
||||
if proxyURL == nil || proxyURL.User == nil {
|
||||
return ""
|
||||
}
|
||||
credentials := proxyURL.User.String()
|
||||
encodedAuth := base64.StdEncoding.EncodeToString([]byte(credentials))
|
||||
return fmt.Sprintf("Basic %s", encodedAuth)
|
||||
}
|
||||
|
||||
// RoundTrip executes the Request and upgrades it. After a successful upgrade,
|
||||
// clients may call SpdyRoundTripper.Connection() to retrieve the upgraded
|
||||
// connection.
|
||||
func (s *SpdyRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
|
||||
header := utilnet.CloneHeader(req.Header)
|
||||
header.Add(httpstream.HeaderConnection, httpstream.HeaderUpgrade)
|
||||
header.Add(httpstream.HeaderUpgrade, HeaderSpdy31)
|
||||
|
||||
var (
|
||||
conn net.Conn
|
||||
rawResponse []byte
|
||||
err error
|
||||
)
|
||||
|
||||
if s.followRedirects {
|
||||
conn, rawResponse, err = utilnet.ConnectWithRedirects(req.Method, req.URL, header, req.Body, s, s.requireSameHostRedirects)
|
||||
} else {
|
||||
clone := utilnet.CloneRequest(req)
|
||||
clone.Header = header
|
||||
conn, err = s.Dial(clone)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
responseReader := bufio.NewReader(
|
||||
io.MultiReader(
|
||||
bytes.NewBuffer(rawResponse),
|
||||
conn,
|
||||
),
|
||||
)
|
||||
|
||||
resp, err := http.ReadResponse(responseReader, nil)
|
||||
if err != nil {
|
||||
if conn != nil {
|
||||
conn.Close()
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s.conn = conn
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
// NewConnection validates the upgrade response, creating and returning a new
|
||||
// httpstream.Connection if there were no errors.
|
||||
func (s *SpdyRoundTripper) NewConnection(resp *http.Response) (httpstream.Connection, error) {
|
||||
connectionHeader := strings.ToLower(resp.Header.Get(httpstream.HeaderConnection))
|
||||
upgradeHeader := strings.ToLower(resp.Header.Get(httpstream.HeaderUpgrade))
|
||||
if (resp.StatusCode != http.StatusSwitchingProtocols) || !strings.Contains(connectionHeader, strings.ToLower(httpstream.HeaderUpgrade)) || !strings.Contains(upgradeHeader, strings.ToLower(HeaderSpdy31)) {
|
||||
defer resp.Body.Close()
|
||||
responseError := ""
|
||||
responseErrorBytes, err := ioutil.ReadAll(resp.Body)
|
||||
if err != nil {
|
||||
responseError = "unable to read error from server response"
|
||||
} else {
|
||||
// TODO: I don't belong here, I should be abstracted from this class
|
||||
if obj, _, err := statusCodecs.UniversalDecoder().Decode(responseErrorBytes, nil, &metav1.Status{}); err == nil {
|
||||
if status, ok := obj.(*metav1.Status); ok {
|
||||
return nil, &apierrors.StatusError{ErrStatus: *status}
|
||||
}
|
||||
}
|
||||
responseError = string(responseErrorBytes)
|
||||
responseError = strings.TrimSpace(responseError)
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unable to upgrade connection: %s", responseError)
|
||||
}
|
||||
|
||||
return NewClientConnection(s.conn)
|
||||
}
|
||||
|
||||
// statusScheme is private scheme for the decoding here until someone fixes the TODO in NewConnection
|
||||
var statusScheme = runtime.NewScheme()
|
||||
|
||||
// ParameterCodec knows about query parameters used with the meta v1 API spec.
|
||||
var statusCodecs = serializer.NewCodecFactory(statusScheme)
|
||||
|
||||
func init() {
|
||||
statusScheme.AddUnversionedTypes(metav1.SchemeGroupVersion,
|
||||
&metav1.Status{},
|
||||
)
|
||||
}
|
107
vendor/k8s.io/apimachinery/pkg/util/httpstream/spdy/upgrade.go
generated
vendored
Normal file
107
vendor/k8s.io/apimachinery/pkg/util/httpstream/spdy/upgrade.go
generated
vendored
Normal file
@ -0,0 +1,107 @@
|
||||
/*
|
||||
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 spdy
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"io"
|
||||
"net"
|
||||
"net/http"
|
||||
"strings"
|
||||
"sync/atomic"
|
||||
|
||||
"k8s.io/apimachinery/pkg/util/httpstream"
|
||||
"k8s.io/apimachinery/pkg/util/runtime"
|
||||
)
|
||||
|
||||
const HeaderSpdy31 = "SPDY/3.1"
|
||||
|
||||
// responseUpgrader knows how to upgrade HTTP responses. It
|
||||
// implements the httpstream.ResponseUpgrader interface.
|
||||
type responseUpgrader struct {
|
||||
}
|
||||
|
||||
// connWrapper is used to wrap a hijacked connection and its bufio.Reader. All
|
||||
// calls will be handled directly by the underlying net.Conn with the exception
|
||||
// of Read and Close calls, which will consider data in the bufio.Reader. This
|
||||
// ensures that data already inside the used bufio.Reader instance is also
|
||||
// read.
|
||||
type connWrapper struct {
|
||||
net.Conn
|
||||
closed int32
|
||||
bufReader *bufio.Reader
|
||||
}
|
||||
|
||||
func (w *connWrapper) Read(b []byte) (n int, err error) {
|
||||
if atomic.LoadInt32(&w.closed) == 1 {
|
||||
return 0, io.EOF
|
||||
}
|
||||
return w.bufReader.Read(b)
|
||||
}
|
||||
|
||||
func (w *connWrapper) Close() error {
|
||||
err := w.Conn.Close()
|
||||
atomic.StoreInt32(&w.closed, 1)
|
||||
return err
|
||||
}
|
||||
|
||||
// NewResponseUpgrader returns a new httpstream.ResponseUpgrader that is
|
||||
// capable of upgrading HTTP responses using SPDY/3.1 via the
|
||||
// spdystream package.
|
||||
func NewResponseUpgrader() httpstream.ResponseUpgrader {
|
||||
return responseUpgrader{}
|
||||
}
|
||||
|
||||
// UpgradeResponse upgrades an HTTP response to one that supports multiplexed
|
||||
// streams. newStreamHandler will be called synchronously whenever the
|
||||
// other end of the upgraded connection creates a new stream.
|
||||
func (u responseUpgrader) UpgradeResponse(w http.ResponseWriter, req *http.Request, newStreamHandler httpstream.NewStreamHandler) httpstream.Connection {
|
||||
connectionHeader := strings.ToLower(req.Header.Get(httpstream.HeaderConnection))
|
||||
upgradeHeader := strings.ToLower(req.Header.Get(httpstream.HeaderUpgrade))
|
||||
if !strings.Contains(connectionHeader, strings.ToLower(httpstream.HeaderUpgrade)) || !strings.Contains(upgradeHeader, strings.ToLower(HeaderSpdy31)) {
|
||||
errorMsg := fmt.Sprintf("unable to upgrade: missing upgrade headers in request: %#v", req.Header)
|
||||
http.Error(w, errorMsg, http.StatusBadRequest)
|
||||
return nil
|
||||
}
|
||||
|
||||
hijacker, ok := w.(http.Hijacker)
|
||||
if !ok {
|
||||
errorMsg := fmt.Sprintf("unable to upgrade: unable to hijack response")
|
||||
http.Error(w, errorMsg, http.StatusInternalServerError)
|
||||
return nil
|
||||
}
|
||||
|
||||
w.Header().Add(httpstream.HeaderConnection, httpstream.HeaderUpgrade)
|
||||
w.Header().Add(httpstream.HeaderUpgrade, HeaderSpdy31)
|
||||
w.WriteHeader(http.StatusSwitchingProtocols)
|
||||
|
||||
conn, bufrw, err := hijacker.Hijack()
|
||||
if err != nil {
|
||||
runtime.HandleError(fmt.Errorf("unable to upgrade: error hijacking response: %v", err))
|
||||
return nil
|
||||
}
|
||||
|
||||
connWithBuf := &connWrapper{Conn: conn, bufReader: bufrw.Reader}
|
||||
spdyConn, err := NewServerConnection(connWithBuf, newStreamHandler)
|
||||
if err != nil {
|
||||
runtime.HandleError(fmt.Errorf("unable to upgrade: error creating SPDY server connection: %v", err))
|
||||
return nil
|
||||
}
|
||||
|
||||
return spdyConn
|
||||
}
|
102
vendor/k8s.io/apimachinery/pkg/util/mergepatch/errors.go
generated
vendored
Normal file
102
vendor/k8s.io/apimachinery/pkg/util/mergepatch/errors.go
generated
vendored
Normal file
@ -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
|
||||
}
|
133
vendor/k8s.io/apimachinery/pkg/util/mergepatch/util.go
generated
vendored
Normal file
133
vendor/k8s.io/apimachinery/pkg/util/mergepatch/util.go
generated
vendored
Normal file
@ -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"
|
||||
"sigs.k8s.io/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, int64, nil:
|
||||
return !reflect.DeepEqual(left, right), nil
|
||||
default:
|
||||
return true, fmt.Errorf("unknown type: %v", reflect.TypeOf(left))
|
||||
}
|
||||
}
|
127
vendor/k8s.io/apimachinery/pkg/util/rand/rand.go
generated
vendored
Normal file
127
vendor/k8s.io/apimachinery/pkg/util/rand/rand.go
generated
vendored
Normal file
@ -0,0 +1,127 @@
|
||||
/*
|
||||
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().UnixNano())),
|
||||
}
|
||||
|
||||
// Int returns a non-negative pseudo-random int.
|
||||
func Int() int {
|
||||
rng.Lock()
|
||||
defer rng.Unlock()
|
||||
return rng.rand.Int()
|
||||
}
|
||||
|
||||
// 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)
|
||||
}
|
53
vendor/k8s.io/apimachinery/pkg/util/remotecommand/constants.go
generated
vendored
Normal file
53
vendor/k8s.io/apimachinery/pkg/util/remotecommand/constants.go
generated
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
/*
|
||||
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 remotecommand
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
DefaultStreamCreationTimeout = 30 * time.Second
|
||||
|
||||
// The SPDY subprotocol "channel.k8s.io" is used for remote command
|
||||
// attachment/execution. This represents the initial unversioned subprotocol,
|
||||
// which has the known bugs http://issues.k8s.io/13394 and
|
||||
// http://issues.k8s.io/13395.
|
||||
StreamProtocolV1Name = "channel.k8s.io"
|
||||
|
||||
// The SPDY subprotocol "v2.channel.k8s.io" is used for remote command
|
||||
// attachment/execution. It is the second version of the subprotocol and
|
||||
// resolves the issues present in the first version.
|
||||
StreamProtocolV2Name = "v2.channel.k8s.io"
|
||||
|
||||
// The SPDY subprotocol "v3.channel.k8s.io" is used for remote command
|
||||
// attachment/execution. It is the third version of the subprotocol and
|
||||
// adds support for resizing container terminals.
|
||||
StreamProtocolV3Name = "v3.channel.k8s.io"
|
||||
|
||||
// The SPDY subprotocol "v4.channel.k8s.io" is used for remote command
|
||||
// attachment/execution. It is the 4th version of the subprotocol and
|
||||
// adds support for exit codes.
|
||||
StreamProtocolV4Name = "v4.channel.k8s.io"
|
||||
|
||||
NonZeroExitCodeReason = metav1.StatusReason("NonZeroExitCode")
|
||||
ExitCodeCauseType = metav1.CauseType("ExitCode")
|
||||
)
|
||||
|
||||
var SupportedStreamingProtocols = []string{StreamProtocolV4Name, StreamProtocolV3Name, StreamProtocolV2Name, StreamProtocolV1Name}
|
49
vendor/k8s.io/apimachinery/pkg/util/strategicpatch/errors.go
generated
vendored
Normal file
49
vendor/k8s.io/apimachinery/pkg/util/strategicpatch/errors.go
generated
vendored
Normal file
@ -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)
|
||||
}
|
194
vendor/k8s.io/apimachinery/pkg/util/strategicpatch/meta.go
generated
vendored
Normal file
194
vendor/k8s.io/apimachinery/pkg/util/strategicpatch/meta.go
generated
vendored
Normal file
@ -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()
|
||||
}
|
2174
vendor/k8s.io/apimachinery/pkg/util/strategicpatch/patch.go
generated
vendored
Normal file
2174
vendor/k8s.io/apimachinery/pkg/util/strategicpatch/patch.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
193
vendor/k8s.io/apimachinery/pkg/util/strategicpatch/types.go
generated
vendored
Normal file
193
vendor/k8s.io/apimachinery/pkg/util/strategicpatch/types.go
generated
vendored
Normal file
@ -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
|
||||
}
|
43
vendor/k8s.io/apimachinery/pkg/util/uuid/uuid.go
generated
vendored
Normal file
43
vendor/k8s.io/apimachinery/pkg/util/uuid/uuid.go
generated
vendored
Normal file
@ -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())
|
||||
}
|
18
vendor/k8s.io/apimachinery/pkg/util/version/doc.go
generated
vendored
Normal file
18
vendor/k8s.io/apimachinery/pkg/util/version/doc.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
/*
|
||||
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 version provides utilities for version number comparisons
|
||||
package version // import "k8s.io/apimachinery/pkg/util/version"
|
314
vendor/k8s.io/apimachinery/pkg/util/version/version.go
generated
vendored
Normal file
314
vendor/k8s.io/apimachinery/pkg/util/version/version.go
generated
vendored
Normal file
@ -0,0 +1,314 @@
|
||||
/*
|
||||
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 version
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Version is an opqaue representation of a version number
|
||||
type Version struct {
|
||||
components []uint
|
||||
semver bool
|
||||
preRelease string
|
||||
buildMetadata string
|
||||
}
|
||||
|
||||
var (
|
||||
// versionMatchRE splits a version string into numeric and "extra" parts
|
||||
versionMatchRE = regexp.MustCompile(`^\s*v?([0-9]+(?:\.[0-9]+)*)(.*)*$`)
|
||||
// extraMatchRE splits the "extra" part of versionMatchRE into semver pre-release and build metadata; it does not validate the "no leading zeroes" constraint for pre-release
|
||||
extraMatchRE = regexp.MustCompile(`^(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?\s*$`)
|
||||
)
|
||||
|
||||
func parse(str string, semver bool) (*Version, error) {
|
||||
parts := versionMatchRE.FindStringSubmatch(str)
|
||||
if parts == nil {
|
||||
return nil, fmt.Errorf("could not parse %q as version", str)
|
||||
}
|
||||
numbers, extra := parts[1], parts[2]
|
||||
|
||||
components := strings.Split(numbers, ".")
|
||||
if (semver && len(components) != 3) || (!semver && len(components) < 2) {
|
||||
return nil, fmt.Errorf("illegal version string %q", str)
|
||||
}
|
||||
|
||||
v := &Version{
|
||||
components: make([]uint, len(components)),
|
||||
semver: semver,
|
||||
}
|
||||
for i, comp := range components {
|
||||
if (i == 0 || semver) && strings.HasPrefix(comp, "0") && comp != "0" {
|
||||
return nil, fmt.Errorf("illegal zero-prefixed version component %q in %q", comp, str)
|
||||
}
|
||||
num, err := strconv.ParseUint(comp, 10, 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("illegal non-numeric version component %q in %q: %v", comp, str, err)
|
||||
}
|
||||
v.components[i] = uint(num)
|
||||
}
|
||||
|
||||
if semver && extra != "" {
|
||||
extraParts := extraMatchRE.FindStringSubmatch(extra)
|
||||
if extraParts == nil {
|
||||
return nil, fmt.Errorf("could not parse pre-release/metadata (%s) in version %q", extra, str)
|
||||
}
|
||||
v.preRelease, v.buildMetadata = extraParts[1], extraParts[2]
|
||||
|
||||
for _, comp := range strings.Split(v.preRelease, ".") {
|
||||
if _, err := strconv.ParseUint(comp, 10, 0); err == nil {
|
||||
if strings.HasPrefix(comp, "0") && comp != "0" {
|
||||
return nil, fmt.Errorf("illegal zero-prefixed version component %q in %q", comp, str)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return v, nil
|
||||
}
|
||||
|
||||
// ParseGeneric parses a "generic" version string. The version string must consist of two
|
||||
// or more dot-separated numeric fields (the first of which can't have leading zeroes),
|
||||
// followed by arbitrary uninterpreted data (which need not be separated from the final
|
||||
// numeric field by punctuation). For convenience, leading and trailing whitespace is
|
||||
// ignored, and the version can be preceded by the letter "v". See also ParseSemantic.
|
||||
func ParseGeneric(str string) (*Version, error) {
|
||||
return parse(str, false)
|
||||
}
|
||||
|
||||
// MustParseGeneric is like ParseGeneric except that it panics on error
|
||||
func MustParseGeneric(str string) *Version {
|
||||
v, err := ParseGeneric(str)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// ParseSemantic parses a version string that exactly obeys the syntax and semantics of
|
||||
// the "Semantic Versioning" specification (http://semver.org/) (although it ignores
|
||||
// leading and trailing whitespace, and allows the version to be preceded by "v"). For
|
||||
// version strings that are not guaranteed to obey the Semantic Versioning syntax, use
|
||||
// ParseGeneric.
|
||||
func ParseSemantic(str string) (*Version, error) {
|
||||
return parse(str, true)
|
||||
}
|
||||
|
||||
// MustParseSemantic is like ParseSemantic except that it panics on error
|
||||
func MustParseSemantic(str string) *Version {
|
||||
v, err := ParseSemantic(str)
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return v
|
||||
}
|
||||
|
||||
// Major returns the major release number
|
||||
func (v *Version) Major() uint {
|
||||
return v.components[0]
|
||||
}
|
||||
|
||||
// Minor returns the minor release number
|
||||
func (v *Version) Minor() uint {
|
||||
return v.components[1]
|
||||
}
|
||||
|
||||
// Patch returns the patch release number if v is a Semantic Version, or 0
|
||||
func (v *Version) Patch() uint {
|
||||
if len(v.components) < 3 {
|
||||
return 0
|
||||
}
|
||||
return v.components[2]
|
||||
}
|
||||
|
||||
// BuildMetadata returns the build metadata, if v is a Semantic Version, or ""
|
||||
func (v *Version) BuildMetadata() string {
|
||||
return v.buildMetadata
|
||||
}
|
||||
|
||||
// PreRelease returns the prerelease metadata, if v is a Semantic Version, or ""
|
||||
func (v *Version) PreRelease() string {
|
||||
return v.preRelease
|
||||
}
|
||||
|
||||
// Components returns the version number components
|
||||
func (v *Version) Components() []uint {
|
||||
return v.components
|
||||
}
|
||||
|
||||
// WithMajor returns copy of the version object with requested major number
|
||||
func (v *Version) WithMajor(major uint) *Version {
|
||||
result := *v
|
||||
result.components = []uint{major, v.Minor(), v.Patch()}
|
||||
return &result
|
||||
}
|
||||
|
||||
// WithMinor returns copy of the version object with requested minor number
|
||||
func (v *Version) WithMinor(minor uint) *Version {
|
||||
result := *v
|
||||
result.components = []uint{v.Major(), minor, v.Patch()}
|
||||
return &result
|
||||
}
|
||||
|
||||
// WithPatch returns copy of the version object with requested patch number
|
||||
func (v *Version) WithPatch(patch uint) *Version {
|
||||
result := *v
|
||||
result.components = []uint{v.Major(), v.Minor(), patch}
|
||||
return &result
|
||||
}
|
||||
|
||||
// WithPreRelease returns copy of the version object with requested prerelease
|
||||
func (v *Version) WithPreRelease(preRelease string) *Version {
|
||||
result := *v
|
||||
result.components = []uint{v.Major(), v.Minor(), v.Patch()}
|
||||
result.preRelease = preRelease
|
||||
return &result
|
||||
}
|
||||
|
||||
// String converts a Version back to a string; note that for versions parsed with
|
||||
// ParseGeneric, this will not include the trailing uninterpreted portion of the version
|
||||
// number.
|
||||
func (v *Version) String() string {
|
||||
var buffer bytes.Buffer
|
||||
|
||||
for i, comp := range v.components {
|
||||
if i > 0 {
|
||||
buffer.WriteString(".")
|
||||
}
|
||||
buffer.WriteString(fmt.Sprintf("%d", comp))
|
||||
}
|
||||
if v.preRelease != "" {
|
||||
buffer.WriteString("-")
|
||||
buffer.WriteString(v.preRelease)
|
||||
}
|
||||
if v.buildMetadata != "" {
|
||||
buffer.WriteString("+")
|
||||
buffer.WriteString(v.buildMetadata)
|
||||
}
|
||||
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// compareInternal returns -1 if v is less than other, 1 if it is greater than other, or 0
|
||||
// if they are equal
|
||||
func (v *Version) compareInternal(other *Version) int {
|
||||
|
||||
vLen := len(v.components)
|
||||
oLen := len(other.components)
|
||||
for i := 0; i < vLen && i < oLen; i++ {
|
||||
switch {
|
||||
case other.components[i] < v.components[i]:
|
||||
return 1
|
||||
case other.components[i] > v.components[i]:
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
// If components are common but one has more items and they are not zeros, it is bigger
|
||||
switch {
|
||||
case oLen < vLen && !onlyZeros(v.components[oLen:]):
|
||||
return 1
|
||||
case oLen > vLen && !onlyZeros(other.components[vLen:]):
|
||||
return -1
|
||||
}
|
||||
|
||||
if !v.semver || !other.semver {
|
||||
return 0
|
||||
}
|
||||
|
||||
switch {
|
||||
case v.preRelease == "" && other.preRelease != "":
|
||||
return 1
|
||||
case v.preRelease != "" && other.preRelease == "":
|
||||
return -1
|
||||
case v.preRelease == other.preRelease: // includes case where both are ""
|
||||
return 0
|
||||
}
|
||||
|
||||
vPR := strings.Split(v.preRelease, ".")
|
||||
oPR := strings.Split(other.preRelease, ".")
|
||||
for i := 0; i < len(vPR) && i < len(oPR); i++ {
|
||||
vNum, err := strconv.ParseUint(vPR[i], 10, 0)
|
||||
if err == nil {
|
||||
oNum, err := strconv.ParseUint(oPR[i], 10, 0)
|
||||
if err == nil {
|
||||
switch {
|
||||
case oNum < vNum:
|
||||
return 1
|
||||
case oNum > vNum:
|
||||
return -1
|
||||
default:
|
||||
continue
|
||||
}
|
||||
}
|
||||
}
|
||||
if oPR[i] < vPR[i] {
|
||||
return 1
|
||||
} else if oPR[i] > vPR[i] {
|
||||
return -1
|
||||
}
|
||||
}
|
||||
|
||||
switch {
|
||||
case len(oPR) < len(vPR):
|
||||
return 1
|
||||
case len(oPR) > len(vPR):
|
||||
return -1
|
||||
}
|
||||
|
||||
return 0
|
||||
}
|
||||
|
||||
// returns false if array contain any non-zero element
|
||||
func onlyZeros(array []uint) bool {
|
||||
for _, num := range array {
|
||||
if num != 0 {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// AtLeast tests if a version is at least equal to a given minimum version. If both
|
||||
// Versions are Semantic Versions, this will use the Semantic Version comparison
|
||||
// algorithm. Otherwise, it will compare only the numeric components, with non-present
|
||||
// components being considered "0" (ie, "1.4" is equal to "1.4.0").
|
||||
func (v *Version) AtLeast(min *Version) bool {
|
||||
return v.compareInternal(min) != -1
|
||||
}
|
||||
|
||||
// LessThan tests if a version is less than a given version. (It is exactly the opposite
|
||||
// of AtLeast, for situations where asking "is v too old?" makes more sense than asking
|
||||
// "is v new enough?".)
|
||||
func (v *Version) LessThan(other *Version) bool {
|
||||
return v.compareInternal(other) == -1
|
||||
}
|
||||
|
||||
// Compare compares v against a version string (which will be parsed as either Semantic
|
||||
// or non-Semantic depending on v). On success it returns -1 if v is less than other, 1 if
|
||||
// it is greater than other, or 0 if they are equal.
|
||||
func (v *Version) Compare(other string) (int, error) {
|
||||
ov, err := parse(other, v.semver)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return v.compareInternal(ov), nil
|
||||
}
|
Reference in New Issue
Block a user