summaryrefslogtreecommitdiff
path: root/vendor/k8s.io/apimachinery/pkg/util
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/k8s.io/apimachinery/pkg/util')
-rw-r--r--vendor/k8s.io/apimachinery/pkg/util/diff/diff.go11
-rw-r--r--vendor/k8s.io/apimachinery/pkg/util/errors/errors.go2
-rw-r--r--vendor/k8s.io/apimachinery/pkg/util/httpstream/spdy/roundtripper.go2
-rw-r--r--vendor/k8s.io/apimachinery/pkg/util/intstr/generated.pb.go2
-rw-r--r--vendor/k8s.io/apimachinery/pkg/util/intstr/generated.proto2
-rw-r--r--vendor/k8s.io/apimachinery/pkg/util/intstr/intstr.go22
-rw-r--r--vendor/k8s.io/apimachinery/pkg/util/json/json.go12
-rw-r--r--vendor/k8s.io/apimachinery/pkg/util/mergepatch/errors.go1
-rw-r--r--vendor/k8s.io/apimachinery/pkg/util/net/http.go49
-rw-r--r--vendor/k8s.io/apimachinery/pkg/util/net/interface.go314
-rw-r--r--vendor/k8s.io/apimachinery/pkg/util/net/util.go14
-rw-r--r--vendor/k8s.io/apimachinery/pkg/util/rand/rand.go85
-rw-r--r--vendor/k8s.io/apimachinery/pkg/util/runtime/runtime.go6
-rw-r--r--vendor/k8s.io/apimachinery/pkg/util/strategicpatch/errors.go49
-rw-r--r--vendor/k8s.io/apimachinery/pkg/util/strategicpatch/meta.go194
-rw-r--r--vendor/k8s.io/apimachinery/pkg/util/strategicpatch/patch.go336
-rw-r--r--vendor/k8s.io/apimachinery/pkg/util/strategicpatch/types.go193
-rw-r--r--vendor/k8s.io/apimachinery/pkg/util/validation/field/errors.go7
-rw-r--r--vendor/k8s.io/apimachinery/pkg/util/validation/validation.go64
-rw-r--r--vendor/k8s.io/apimachinery/pkg/util/wait/wait.go36
-rw-r--r--vendor/k8s.io/apimachinery/pkg/util/yaml/decoder.go6
21 files changed, 1016 insertions, 391 deletions
diff --git a/vendor/k8s.io/apimachinery/pkg/util/diff/diff.go b/vendor/k8s.io/apimachinery/pkg/util/diff/diff.go
index 0f730875e..3d5ec14bf 100644
--- a/vendor/k8s.io/apimachinery/pkg/util/diff/diff.go
+++ b/vendor/k8s.io/apimachinery/pkg/util/diff/diff.go
@@ -142,10 +142,6 @@ func objectReflectDiff(path *field.Path, a, b reflect.Value) []diff {
}
if sub := objectReflectDiff(path.Child(a.Type().Field(i).Name), a.Field(i), b.Field(i)); len(sub) > 0 {
changes = append(changes, sub...)
- } else {
- if !reflect.DeepEqual(a.Field(i).Interface(), b.Field(i).Interface()) {
- changes = append(changes, diff{path: path, a: a.Field(i).Interface(), b: b.Field(i).Interface()})
- }
}
}
return changes
@@ -178,21 +174,18 @@ func objectReflectDiff(path *field.Path, a, b reflect.Value) []diff {
}
return nil
}
+ var diffs []diff
for i := 0; i < l; i++ {
if !reflect.DeepEqual(a.Index(i), b.Index(i)) {
- return objectReflectDiff(path.Index(i), a.Index(i), b.Index(i))
+ diffs = append(diffs, objectReflectDiff(path.Index(i), a.Index(i), b.Index(i))...)
}
}
- var diffs []diff
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)})
}
- if len(diffs) == 0 {
- diffs = append(diffs, diff{path: path, a: a, b: b})
- }
return diffs
case reflect.Map:
if reflect.DeepEqual(a.Interface(), b.Interface()) {
diff --git a/vendor/k8s.io/apimachinery/pkg/util/errors/errors.go b/vendor/k8s.io/apimachinery/pkg/util/errors/errors.go
index bdea0e16c..88e937679 100644
--- a/vendor/k8s.io/apimachinery/pkg/util/errors/errors.go
+++ b/vendor/k8s.io/apimachinery/pkg/util/errors/errors.go
@@ -21,7 +21,7 @@ import (
"fmt"
)
-// MessagesgCountMap contains occurance for each error message.
+// MessageCountMap contains occurrence for each error message.
type MessageCountMap map[string]int
// Aggregate represents an object that contains multiple errors, but does not
diff --git a/vendor/k8s.io/apimachinery/pkg/util/httpstream/spdy/roundtripper.go b/vendor/k8s.io/apimachinery/pkg/util/httpstream/spdy/roundtripper.go
index 12bef075d..d2d3ad8cb 100644
--- a/vendor/k8s.io/apimachinery/pkg/util/httpstream/spdy/roundtripper.go
+++ b/vendor/k8s.io/apimachinery/pkg/util/httpstream/spdy/roundtripper.go
@@ -110,7 +110,7 @@ func (s *SpdyRoundTripper) Dial(req *http.Request) (net.Conn, error) {
func (s *SpdyRoundTripper) dial(req *http.Request) (net.Conn, error) {
proxier := s.proxier
if proxier == nil {
- proxier = http.ProxyFromEnvironment
+ proxier = utilnet.NewProxierWithNoProxyCIDR(http.ProxyFromEnvironment)
}
proxyURL, err := proxier(req)
if err != nil {
diff --git a/vendor/k8s.io/apimachinery/pkg/util/intstr/generated.pb.go b/vendor/k8s.io/apimachinery/pkg/util/intstr/generated.pb.go
index 433dfa5cd..161e9a6f8 100644
--- a/vendor/k8s.io/apimachinery/pkg/util/intstr/generated.pb.go
+++ b/vendor/k8s.io/apimachinery/pkg/util/intstr/generated.pb.go
@@ -1,5 +1,5 @@
/*
-Copyright 2017 The Kubernetes Authors.
+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.
diff --git a/vendor/k8s.io/apimachinery/pkg/util/intstr/generated.proto b/vendor/k8s.io/apimachinery/pkg/util/intstr/generated.proto
index cccaf6f68..6819d468d 100644
--- a/vendor/k8s.io/apimachinery/pkg/util/intstr/generated.proto
+++ b/vendor/k8s.io/apimachinery/pkg/util/intstr/generated.proto
@@ -1,5 +1,5 @@
/*
-Copyright 2017 The Kubernetes Authors.
+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.
diff --git a/vendor/k8s.io/apimachinery/pkg/util/intstr/intstr.go b/vendor/k8s.io/apimachinery/pkg/util/intstr/intstr.go
index 02586b348..231498ca0 100644
--- a/vendor/k8s.io/apimachinery/pkg/util/intstr/intstr.go
+++ b/vendor/k8s.io/apimachinery/pkg/util/intstr/intstr.go
@@ -24,9 +24,6 @@ import (
"strconv"
"strings"
- "k8s.io/apimachinery/pkg/openapi"
-
- "github.com/go-openapi/spec"
"github.com/golang/glog"
"github.com/google/gofuzz"
)
@@ -120,16 +117,15 @@ func (intstr IntOrString) MarshalJSON() ([]byte, error) {
}
}
-func (_ IntOrString) OpenAPIDefinition() openapi.OpenAPIDefinition {
- return openapi.OpenAPIDefinition{
- Schema: spec.Schema{
- SchemaProps: spec.SchemaProps{
- Type: []string{"string"},
- Format: "int-or-string",
- },
- },
- }
-}
+// OpenAPISchemaType is used by the kube-openapi generator when constructing
+// the OpenAPI spec of this type.
+//
+// See: https://github.com/kubernetes/kube-openapi/tree/master/pkg/generators
+func (_ IntOrString) OpenAPISchemaType() []string { return []string{"string"} }
+
+// OpenAPISchemaFormat is used by the kube-openapi generator when constructing
+// the OpenAPI spec of this type.
+func (_ IntOrString) OpenAPISchemaFormat() string { return "int-or-string" }
func (intstr *IntOrString) Fuzz(c fuzz.Continue) {
if intstr == nil {
diff --git a/vendor/k8s.io/apimachinery/pkg/util/json/json.go b/vendor/k8s.io/apimachinery/pkg/util/json/json.go
index e8054a12e..10c8cb837 100644
--- a/vendor/k8s.io/apimachinery/pkg/util/json/json.go
+++ b/vendor/k8s.io/apimachinery/pkg/util/json/json.go
@@ -50,6 +50,18 @@ func Unmarshal(data []byte, v interface{}) error {
// If the decode succeeds, post-process the map to convert json.Number objects to int64 or float64
return convertMapNumbers(*v)
+ case *[]interface{}:
+ // Build a decoder from the given data
+ decoder := json.NewDecoder(bytes.NewBuffer(data))
+ // Preserve numbers, rather than casting to float64 automatically
+ decoder.UseNumber()
+ // Run the decode
+ if err := decoder.Decode(v); err != nil {
+ return err
+ }
+ // If the decode succeeds, post-process the map to convert json.Number objects to int64 or float64
+ return convertSliceNumbers(*v)
+
default:
return json.Unmarshal(data, v)
}
diff --git a/vendor/k8s.io/apimachinery/pkg/util/mergepatch/errors.go b/vendor/k8s.io/apimachinery/pkg/util/mergepatch/errors.go
index ac3c1e8cf..16501d5af 100644
--- a/vendor/k8s.io/apimachinery/pkg/util/mergepatch/errors.go
+++ b/vendor/k8s.io/apimachinery/pkg/util/mergepatch/errors.go
@@ -29,6 +29,7 @@ var (
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 {
diff --git a/vendor/k8s.io/apimachinery/pkg/util/net/http.go b/vendor/k8s.io/apimachinery/pkg/util/net/http.go
index adb80813b..bc2a531b9 100644
--- a/vendor/k8s.io/apimachinery/pkg/util/net/http.go
+++ b/vendor/k8s.io/apimachinery/pkg/util/net/http.go
@@ -26,6 +26,7 @@ import (
"net/http"
"net/url"
"os"
+ "path"
"strconv"
"strings"
@@ -33,6 +34,26 @@ import (
"golang.org/x/net/http2"
)
+// JoinPreservingTrailingSlash does a path.Join of the specified elements,
+// preserving any trailing slash on the last non-empty segment
+func JoinPreservingTrailingSlash(elem ...string) string {
+ // do the basic path join
+ result := path.Join(elem...)
+
+ // find the last non-empty segment
+ for i := len(elem) - 1; i >= 0; i-- {
+ if len(elem[i]) > 0 {
+ // if the last segment ended in a slash, ensure our result does as well
+ if strings.HasSuffix(elem[i], "/") && !strings.HasSuffix(result, "/") {
+ result += "/"
+ }
+ break
+ }
+ }
+
+ return result
+}
+
// IsProbableEOF returns true if the given error resembles a connection termination
// scenario that would justify assuming that the watch is empty.
// These errors are what the Go http stack returns back to us which are general
@@ -108,7 +129,7 @@ func DialerFor(transport http.RoundTripper) (DialFunc, error) {
case RoundTripperWrapper:
return DialerFor(transport.WrappedRoundTripper())
default:
- return nil, fmt.Errorf("unknown transport type: %v", transport)
+ return nil, fmt.Errorf("unknown transport type: %T", transport)
}
}
@@ -129,7 +150,7 @@ func TLSClientConfig(transport http.RoundTripper) (*tls.Config, error) {
case RoundTripperWrapper:
return TLSClientConfig(transport.WrappedRoundTripper())
default:
- return nil, fmt.Errorf("unknown transport type: %v", transport)
+ return nil, fmt.Errorf("unknown transport type: %T", transport)
}
}
@@ -235,8 +256,11 @@ func isDefault(transportProxier func(*http.Request) (*url.URL, error)) bool {
// NewProxierWithNoProxyCIDR constructs a Proxier function that respects CIDRs in NO_PROXY and delegates if
// no matching CIDRs are found
func NewProxierWithNoProxyCIDR(delegate func(req *http.Request) (*url.URL, error)) func(req *http.Request) (*url.URL, error) {
- // we wrap the default method, so we only need to perform our check if the NO_PROXY envvar has a CIDR in it
+ // we wrap the default method, so we only need to perform our check if the NO_PROXY (or no_proxy) envvar has a CIDR in it
noProxyEnv := os.Getenv("NO_PROXY")
+ if noProxyEnv == "" {
+ noProxyEnv = os.Getenv("no_proxy")
+ }
noProxyRules := strings.Split(noProxyEnv, ",")
cidrs := []*net.IPNet{}
@@ -252,17 +276,7 @@ func NewProxierWithNoProxyCIDR(delegate func(req *http.Request) (*url.URL, error
}
return func(req *http.Request) (*url.URL, error) {
- host := req.URL.Host
- // for some urls, the Host is already the host, not the host:port
- if net.ParseIP(host) == nil {
- var err error
- host, _, err = net.SplitHostPort(req.URL.Host)
- if err != nil {
- return delegate(req)
- }
- }
-
- ip := net.ParseIP(host)
+ ip := net.ParseIP(req.URL.Hostname())
if ip == nil {
return delegate(req)
}
@@ -277,6 +291,13 @@ func NewProxierWithNoProxyCIDR(delegate func(req *http.Request) (*url.URL, error
}
}
+// DialerFunc implements Dialer for the provided function.
+type DialerFunc func(req *http.Request) (net.Conn, error)
+
+func (fn DialerFunc) Dial(req *http.Request) (net.Conn, error) {
+ return fn(req)
+}
+
// Dialer dials a host and writes a request to it.
type Dialer interface {
// Dial connects to the host specified by req's URL, writes the request to the connection, and
diff --git a/vendor/k8s.io/apimachinery/pkg/util/net/interface.go b/vendor/k8s.io/apimachinery/pkg/util/net/interface.go
index a1e53d2e4..42816bd70 100644
--- a/vendor/k8s.io/apimachinery/pkg/util/net/interface.go
+++ b/vendor/k8s.io/apimachinery/pkg/util/net/interface.go
@@ -29,18 +29,47 @@ import (
"github.com/golang/glog"
)
+type AddressFamily uint
+
+const (
+ familyIPv4 AddressFamily = 4
+ familyIPv6 AddressFamily = 6
+)
+
+const (
+ ipv4RouteFile = "/proc/net/route"
+ ipv6RouteFile = "/proc/net/ipv6_route"
+)
+
type Route struct {
Interface string
Destination net.IP
Gateway net.IP
- // TODO: add more fields here if needed
+ Family AddressFamily
}
-func getRoutes(input io.Reader) ([]Route, error) {
- routes := []Route{}
- if input == nil {
- return nil, fmt.Errorf("input is nil")
+type RouteFile struct {
+ name string
+ parse func(input io.Reader) ([]Route, error)
+}
+
+var (
+ v4File = RouteFile{name: ipv4RouteFile, parse: getIPv4DefaultRoutes}
+ v6File = RouteFile{name: ipv6RouteFile, parse: getIPv6DefaultRoutes}
+)
+
+func (rf RouteFile) extract() ([]Route, error) {
+ file, err := os.Open(rf.name)
+ if err != nil {
+ return nil, err
}
+ defer file.Close()
+ return rf.parse(file)
+}
+
+// getIPv4DefaultRoutes obtains the IPv4 routes, and filters out non-default routes.
+func getIPv4DefaultRoutes(input io.Reader) ([]Route, error) {
+ routes := []Route{}
scanner := bufio.NewReader(input)
for {
line, err := scanner.ReadString('\n')
@@ -52,24 +81,71 @@ func getRoutes(input io.Reader) ([]Route, error) {
continue
}
fields := strings.Fields(line)
- routes = append(routes, Route{})
- route := &routes[len(routes)-1]
- route.Interface = fields[0]
- ip, err := parseIP(fields[1])
+ // Interested in fields:
+ // 0 - interface name
+ // 1 - destination address
+ // 2 - gateway
+ dest, err := parseIP(fields[1], familyIPv4)
+ if err != nil {
+ return nil, err
+ }
+ gw, err := parseIP(fields[2], familyIPv4)
+ if err != nil {
+ return nil, err
+ }
+ if !dest.Equal(net.IPv4zero) {
+ continue
+ }
+ routes = append(routes, Route{
+ Interface: fields[0],
+ Destination: dest,
+ Gateway: gw,
+ Family: familyIPv4,
+ })
+ }
+ return routes, nil
+}
+
+func getIPv6DefaultRoutes(input io.Reader) ([]Route, error) {
+ routes := []Route{}
+ scanner := bufio.NewReader(input)
+ for {
+ line, err := scanner.ReadString('\n')
+ if err == io.EOF {
+ break
+ }
+ fields := strings.Fields(line)
+ // Interested in fields:
+ // 0 - destination address
+ // 4 - gateway
+ // 9 - interface name
+ dest, err := parseIP(fields[0], familyIPv6)
if err != nil {
return nil, err
}
- route.Destination = ip
- ip, err = parseIP(fields[2])
+ gw, err := parseIP(fields[4], familyIPv6)
if err != nil {
return nil, err
}
- route.Gateway = ip
+ if !dest.Equal(net.IPv6zero) {
+ continue
+ }
+ if gw.Equal(net.IPv6zero) {
+ continue // loopback
+ }
+ routes = append(routes, Route{
+ Interface: fields[9],
+ Destination: dest,
+ Gateway: gw,
+ Family: familyIPv6,
+ })
}
return routes, nil
}
-func parseIP(str string) (net.IP, error) {
+// parseIP takes the hex IP address string from route file and converts it
+// to a net.IP address. For IPv4, the value must be converted to big endian.
+func parseIP(str string, family AddressFamily) (net.IP, error) {
if str == "" {
return nil, fmt.Errorf("input is nil")
}
@@ -77,11 +153,16 @@ func parseIP(str string) (net.IP, error) {
if err != nil {
return nil, err
}
- //TODO add ipv6 support
- if len(bytes) != net.IPv4len {
- return nil, fmt.Errorf("only IPv4 is supported")
+ if family == familyIPv4 {
+ if len(bytes) != net.IPv4len {
+ return nil, fmt.Errorf("invalid IPv4 address in route")
+ }
+ return net.IP([]byte{bytes[3], bytes[2], bytes[1], bytes[0]}), nil
+ }
+ // Must be IPv6
+ if len(bytes) != net.IPv6len {
+ return nil, fmt.Errorf("invalid IPv6 address in route")
}
- bytes[0], bytes[1], bytes[2], bytes[3] = bytes[3], bytes[2], bytes[1], bytes[0]
return net.IP(bytes), nil
}
@@ -96,10 +177,13 @@ func isInterfaceUp(intf *net.Interface) bool {
return false
}
-//getFinalIP method receives all the IP addrs of a Interface
-//and returns a nil if the address is Loopback, Ipv6, link-local or nil.
-//It returns a valid IPv4 if an Ipv4 address is found in the array.
-func getFinalIP(addrs []net.Addr) (net.IP, error) {
+func isLoopbackOrPointToPoint(intf *net.Interface) bool {
+ return intf.Flags&(net.FlagLoopback|net.FlagPointToPoint) != 0
+}
+
+// getMatchingGlobalIP returns the first valid global unicast address of the given
+// 'family' from the list of 'addrs'.
+func getMatchingGlobalIP(addrs []net.Addr, family AddressFamily) (net.IP, error) {
if len(addrs) > 0 {
for i := range addrs {
glog.V(4).Infof("Checking addr %s.", addrs[i].String())
@@ -107,17 +191,15 @@ func getFinalIP(addrs []net.Addr) (net.IP, error) {
if err != nil {
return nil, err
}
- //Only IPv4
- //TODO : add IPv6 support
- if ip.To4() != nil {
- if !ip.IsLoopback() && !ip.IsLinkLocalMulticast() && !ip.IsLinkLocalUnicast() {
+ if memberOf(ip, family) {
+ if ip.IsGlobalUnicast() {
glog.V(4).Infof("IP found %v", ip)
return ip, nil
} else {
- glog.V(4).Infof("Loopback/link-local found %v", ip)
+ glog.V(4).Infof("Non-global unicast address found %v", ip)
}
} else {
- glog.V(4).Infof("%v is not a valid IPv4 address", ip)
+ glog.V(4).Infof("%v is not an IPv%d address", ip, int(family))
}
}
@@ -125,7 +207,9 @@ func getFinalIP(addrs []net.Addr) (net.IP, error) {
return nil, nil
}
-func getIPFromInterface(intfName string, nw networkInterfacer) (net.IP, error) {
+// getIPFromInterface gets the IPs on an interface and returns a global unicast address, if any. The
+// interface must be up, the IP must in the family requested, and the IP must be a global unicast address.
+func getIPFromInterface(intfName string, forFamily AddressFamily, nw networkInterfacer) (net.IP, error) {
intf, err := nw.InterfaceByName(intfName)
if err != nil {
return nil, err
@@ -136,131 +220,161 @@ func getIPFromInterface(intfName string, nw networkInterfacer) (net.IP, error) {
return nil, err
}
glog.V(4).Infof("Interface %q has %d addresses :%v.", intfName, len(addrs), addrs)
- finalIP, err := getFinalIP(addrs)
+ matchingIP, err := getMatchingGlobalIP(addrs, forFamily)
if err != nil {
return nil, err
}
- if finalIP != nil {
- glog.V(4).Infof("valid IPv4 address for interface %q found as %v.", intfName, finalIP)
- return finalIP, nil
+ if matchingIP != nil {
+ glog.V(4).Infof("Found valid IPv%d address %v for interface %q.", int(forFamily), matchingIP, intfName)
+ return matchingIP, nil
}
}
-
return nil, nil
}
-func flagsSet(flags net.Flags, test net.Flags) bool {
- return flags&test != 0
-}
-
-func flagsClear(flags net.Flags, test net.Flags) bool {
- return flags&test == 0
+// memberOF tells if the IP is of the desired family. Used for checking interface addresses.
+func memberOf(ip net.IP, family AddressFamily) bool {
+ if ip.To4() != nil {
+ return family == familyIPv4
+ } else {
+ return family == familyIPv6
+ }
}
-func chooseHostInterfaceNativeGo() (net.IP, error) {
- intfs, err := net.Interfaces()
+// chooseIPFromHostInterfaces looks at all system interfaces, trying to find one that is up that
+// has a global unicast address (non-loopback, non-link local, non-point2point), and returns the IP.
+// Searches for IPv4 addresses, and then IPv6 addresses.
+func chooseIPFromHostInterfaces(nw networkInterfacer) (net.IP, error) {
+ intfs, err := nw.Interfaces()
if err != nil {
return nil, err
}
- i := 0
- var ip net.IP
- for i = range intfs {
- if flagsSet(intfs[i].Flags, net.FlagUp) && flagsClear(intfs[i].Flags, net.FlagLoopback|net.FlagPointToPoint) {
- addrs, err := intfs[i].Addrs()
+ if len(intfs) == 0 {
+ return nil, fmt.Errorf("no interfaces found on host.")
+ }
+ for _, family := range []AddressFamily{familyIPv4, familyIPv6} {
+ glog.V(4).Infof("Looking for system interface with a global IPv%d address", uint(family))
+ for _, intf := range intfs {
+ if !isInterfaceUp(&intf) {
+ glog.V(4).Infof("Skipping: down interface %q", intf.Name)
+ continue
+ }
+ if isLoopbackOrPointToPoint(&intf) {
+ glog.V(4).Infof("Skipping: LB or P2P interface %q", intf.Name)
+ continue
+ }
+ addrs, err := nw.Addrs(&intf)
if err != nil {
return nil, err
}
- if len(addrs) > 0 {
- for _, addr := range addrs {
- if addrIP, _, err := net.ParseCIDR(addr.String()); err == nil {
- if addrIP.To4() != nil {
- ip = addrIP.To4()
- if !ip.IsLinkLocalMulticast() && !ip.IsLinkLocalUnicast() {
- break
- }
- }
- }
+ if len(addrs) == 0 {
+ glog.V(4).Infof("Skipping: no addresses on interface %q", intf.Name)
+ continue
+ }
+ for _, addr := range addrs {
+ ip, _, err := net.ParseCIDR(addr.String())
+ if err != nil {
+ return nil, fmt.Errorf("Unable to parse CIDR for interface %q: %s", intf.Name, err)
+ }
+ if !memberOf(ip, family) {
+ glog.V(4).Infof("Skipping: no address family match for %q on interface %q.", ip, intf.Name)
+ continue
}
- if ip != nil {
- // This interface should suffice.
- break
+ // TODO: Decide if should open up to allow IPv6 LLAs in future.
+ if !ip.IsGlobalUnicast() {
+ glog.V(4).Infof("Skipping: non-global address %q on interface %q.", ip, intf.Name)
+ continue
}
+ glog.V(4).Infof("Found global unicast address %q on interface %q.", ip, intf.Name)
+ return ip, nil
}
}
}
- if ip == nil {
- return nil, fmt.Errorf("no acceptable interface from host")
- }
- glog.V(4).Infof("Choosing interface %s (IP %v) as default", intfs[i].Name, ip)
- return ip, nil
+ return nil, fmt.Errorf("no acceptable interface with global unicast address found on host")
}
-//ChooseHostInterface is a method used fetch an IP for a daemon.
-//It uses data from /proc/net/route file.
-//For a node with no internet connection ,it returns error
-//For a multi n/w interface node it returns the IP of the interface with gateway on it.
+// ChooseHostInterface is a method used fetch an IP for a daemon.
+// If there is no routing info file, it will choose a global IP from the system
+// interfaces. Otherwise, it will use IPv4 and IPv6 route information to return the
+// IP of the interface with a gateway on it (with priority given to IPv4). For a node
+// with no internet connection, it returns error.
func ChooseHostInterface() (net.IP, error) {
- inFile, err := os.Open("/proc/net/route")
+ var nw networkInterfacer = networkInterface{}
+ if _, err := os.Stat(ipv4RouteFile); os.IsNotExist(err) {
+ return chooseIPFromHostInterfaces(nw)
+ }
+ routes, err := getAllDefaultRoutes()
if err != nil {
- if os.IsNotExist(err) {
- return chooseHostInterfaceNativeGo()
- }
return nil, err
}
- defer inFile.Close()
- var nw networkInterfacer = networkInterface{}
- return chooseHostInterfaceFromRoute(inFile, nw)
+ return chooseHostInterfaceFromRoute(routes, nw)
}
+// networkInterfacer defines an interface for several net library functions. Production
+// code will forward to net library functions, and unit tests will override the methods
+// for testing purposes.
type networkInterfacer interface {
InterfaceByName(intfName string) (*net.Interface, error)
Addrs(intf *net.Interface) ([]net.Addr, error)
+ Interfaces() ([]net.Interface, error)
}
+// networkInterface implements the networkInterfacer interface for production code, just
+// wrapping the underlying net library function calls.
type networkInterface struct{}
func (_ networkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
- intf, err := net.InterfaceByName(intfName)
- if err != nil {
- return nil, err
- }
- return intf, nil
+ return net.InterfaceByName(intfName)
}
func (_ networkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
- addrs, err := intf.Addrs()
- if err != nil {
- return nil, err
- }
- return addrs, nil
+ return intf.Addrs()
}
-func chooseHostInterfaceFromRoute(inFile io.Reader, nw networkInterfacer) (net.IP, error) {
- routes, err := getRoutes(inFile)
+func (_ networkInterface) Interfaces() ([]net.Interface, error) {
+ return net.Interfaces()
+}
+
+// getAllDefaultRoutes obtains IPv4 and IPv6 default routes on the node. If unable
+// to read the IPv4 routing info file, we return an error. If unable to read the IPv6
+// routing info file (which is optional), we'll just use the IPv4 route information.
+// Using all the routing info, if no default routes are found, an error is returned.
+func getAllDefaultRoutes() ([]Route, error) {
+ routes, err := v4File.extract()
if err != nil {
return nil, err
}
- zero := net.IP{0, 0, 0, 0}
- var finalIP net.IP
- for i := range routes {
- //find interface with gateway
- if routes[i].Destination.Equal(zero) {
- glog.V(4).Infof("Default route transits interface %q", routes[i].Interface)
- finalIP, err := getIPFromInterface(routes[i].Interface, nw)
+ v6Routes, _ := v6File.extract()
+ routes = append(routes, v6Routes...)
+ if len(routes) == 0 {
+ return nil, fmt.Errorf("No default routes.")
+ }
+ return routes, nil
+}
+
+// chooseHostInterfaceFromRoute cycles through each default route provided, looking for a
+// global IP address from the interface for the route. Will first look all each IPv4 route for
+// an IPv4 IP, and then will look at each IPv6 route for an IPv6 IP.
+func chooseHostInterfaceFromRoute(routes []Route, nw networkInterfacer) (net.IP, error) {
+ for _, family := range []AddressFamily{familyIPv4, familyIPv6} {
+ glog.V(4).Infof("Looking for default routes with IPv%d addresses", uint(family))
+ for _, route := range routes {
+ if route.Family != family {
+ continue
+ }
+ glog.V(4).Infof("Default route transits interface %q", route.Interface)
+ finalIP, err := getIPFromInterface(route.Interface, family, nw)
if err != nil {
return nil, err
}
if finalIP != nil {
- glog.V(4).Infof("Choosing IP %v ", finalIP)
+ glog.V(4).Infof("Found active IP %v ", finalIP)
return finalIP, nil
}
}
}
- glog.V(4).Infof("No valid IP found")
- if finalIP == nil {
- return nil, fmt.Errorf("Unable to select an IP.")
- }
- return nil, nil
+ glog.V(4).Infof("No active IP found by looking at default routes")
+ return nil, fmt.Errorf("unable to select an IP from default routes.")
}
// If bind-address is usable, return it directly
diff --git a/vendor/k8s.io/apimachinery/pkg/util/net/util.go b/vendor/k8s.io/apimachinery/pkg/util/net/util.go
index 461144f0b..8344d10c8 100644
--- a/vendor/k8s.io/apimachinery/pkg/util/net/util.go
+++ b/vendor/k8s.io/apimachinery/pkg/util/net/util.go
@@ -18,6 +18,8 @@ package net
import (
"net"
+ "net/url"
+ "os"
"reflect"
"syscall"
)
@@ -38,8 +40,16 @@ func IPNetEqual(ipnet1, ipnet2 *net.IPNet) bool {
// Returns if the given err is "connection reset by peer" error.
func IsConnectionReset(err error) bool {
- opErr, ok := err.(*net.OpError)
- if ok && opErr.Err.Error() == syscall.ECONNRESET.Error() {
+ if urlErr, ok := err.(*url.Error); ok {
+ err = urlErr.Err
+ }
+ if opErr, ok := err.(*net.OpError); ok {
+ err = opErr.Err
+ }
+ if osErr, ok := err.(*os.SyscallError); ok {
+ err = osErr.Err
+ }
+ if errno, ok := err.(syscall.Errno); ok && errno == syscall.ECONNRESET {
return true
}
return false
diff --git a/vendor/k8s.io/apimachinery/pkg/util/rand/rand.go b/vendor/k8s.io/apimachinery/pkg/util/rand/rand.go
deleted file mode 100644
index db109c2cd..000000000
--- a/vendor/k8s.io/apimachinery/pkg/util/rand/rand.go
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
-Copyright 2015 The Kubernetes Authors.
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.
-*/
-
-// Package rand provides utilities related to randomization.
-package rand
-
-import (
- "math/rand"
- "sync"
- "time"
-)
-
-var rng = struct {
- sync.Mutex
- rand *rand.Rand
-}{
- rand: rand.New(rand.NewSource(time.Now().UTC().UnixNano())),
-}
-
-// Intn generates an integer in range [0,max).
-// By design this should panic if input is invalid, <= 0.
-func Intn(max int) int {
- rng.Lock()
- defer rng.Unlock()
- return rng.rand.Intn(max)
-}
-
-// IntnRange generates an integer in range [min,max).
-// By design this should panic if input is invalid, <= 0.
-func IntnRange(min, max int) int {
- rng.Lock()
- defer rng.Unlock()
- return rng.rand.Intn(max-min) + min
-}
-
-// IntnRange generates an int64 integer in range [min,max).
-// By design this should panic if input is invalid, <= 0.
-func Int63nRange(min, max int64) int64 {
- rng.Lock()
- defer rng.Unlock()
- return rng.rand.Int63n(max-min) + min
-}
-
-// Seed seeds the rng with the provided seed.
-func Seed(seed int64) {
- rng.Lock()
- defer rng.Unlock()
-
- rng.rand = rand.New(rand.NewSource(seed))
-}
-
-// Perm returns, as a slice of n ints, a pseudo-random permutation of the integers [0,n)
-// from the default Source.
-func Perm(n int) []int {
- rng.Lock()
- defer rng.Unlock()
- return rng.rand.Perm(n)
-}
-
-// We omit vowels from the set of available characters to reduce the chances
-// of "bad words" being formed.
-var alphanums = []rune("bcdfghjklmnpqrstvwxz0123456789")
-
-// String generates a random alphanumeric string, without vowels, which is n
-// characters long. This will panic if n is less than zero.
-func String(length int) string {
- b := make([]rune, length)
- for i := range b {
- b[i] = alphanums[Intn(len(alphanums))]
- }
- return string(b)
-}
diff --git a/vendor/k8s.io/apimachinery/pkg/util/runtime/runtime.go b/vendor/k8s.io/apimachinery/pkg/util/runtime/runtime.go
index 748174e19..d4cec0b88 100644
--- a/vendor/k8s.io/apimachinery/pkg/util/runtime/runtime.go
+++ b/vendor/k8s.io/apimachinery/pkg/util/runtime/runtime.go
@@ -43,7 +43,7 @@ var PanicHandlers = []func(interface{}){logPanic}
// TODO: remove this function. We are switching to a world where it's safe for
// apiserver to panic, since it will be restarted by kubelet. At the beginning
// of the Kubernetes project, nothing was going to restart apiserver and so
-// catching panics was important. But it's actually much simpler for montoring
+// catching panics was important. But it's actually much simpler for monitoring
// software if we just exit when an unexpected panic happens.
func HandleCrash(additionalHandlers ...func(interface{})) {
if r := recover(); r != nil {
@@ -128,7 +128,9 @@ func (r *rudimentaryErrorBackoff) OnError(error) {
r.lastErrorTimeLock.Lock()
defer r.lastErrorTimeLock.Unlock()
d := time.Since(r.lastErrorTime)
- if d < r.minPeriod {
+ if d < r.minPeriod && d >= 0 {
+ // If the time moves backwards for any reason, do nothing
+ // TODO: remove check "d >= 0" after go 1.8 is no longer supported
time.Sleep(r.minPeriod - d)
}
r.lastErrorTime = time.Now()
diff --git a/vendor/k8s.io/apimachinery/pkg/util/strategicpatch/errors.go b/vendor/k8s.io/apimachinery/pkg/util/strategicpatch/errors.go
new file mode 100644
index 000000000..ab66d0452
--- /dev/null
+++ b/vendor/k8s.io/apimachinery/pkg/util/strategicpatch/errors.go
@@ -0,0 +1,49 @@
+/*
+Copyright 2017 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package strategicpatch
+
+import (
+ "fmt"
+)
+
+type LookupPatchMetaError struct {
+ Path string
+ Err error
+}
+
+func (e LookupPatchMetaError) Error() string {
+ return fmt.Sprintf("LookupPatchMetaError(%s): %v", e.Path, e.Err)
+}
+
+type FieldNotFoundError struct {
+ Path string
+ Field string
+}
+
+func (e FieldNotFoundError) Error() string {
+ return fmt.Sprintf("unable to find api field %q in %s", e.Field, e.Path)
+}
+
+type InvalidTypeError struct {
+ Path string
+ Expected string
+ Actual string
+}
+
+func (e InvalidTypeError) Error() string {
+ return fmt.Sprintf("invalid type for %s: got %q, expected %q", e.Path, e.Actual, e.Expected)
+}
diff --git a/vendor/k8s.io/apimachinery/pkg/util/strategicpatch/meta.go b/vendor/k8s.io/apimachinery/pkg/util/strategicpatch/meta.go
new file mode 100644
index 000000000..c31de15e7
--- /dev/null
+++ b/vendor/k8s.io/apimachinery/pkg/util/strategicpatch/meta.go
@@ -0,0 +1,194 @@
+/*
+Copyright 2017 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package strategicpatch
+
+import (
+ "errors"
+ "fmt"
+ "reflect"
+
+ "k8s.io/apimachinery/pkg/util/mergepatch"
+ forkedjson "k8s.io/apimachinery/third_party/forked/golang/json"
+ openapi "k8s.io/kube-openapi/pkg/util/proto"
+)
+
+type PatchMeta struct {
+ patchStrategies []string
+ patchMergeKey string
+}
+
+func (pm PatchMeta) GetPatchStrategies() []string {
+ if pm.patchStrategies == nil {
+ return []string{}
+ }
+ return pm.patchStrategies
+}
+
+func (pm PatchMeta) SetPatchStrategies(ps []string) {
+ pm.patchStrategies = ps
+}
+
+func (pm PatchMeta) GetPatchMergeKey() string {
+ return pm.patchMergeKey
+}
+
+func (pm PatchMeta) SetPatchMergeKey(pmk string) {
+ pm.patchMergeKey = pmk
+}
+
+type LookupPatchMeta interface {
+ // LookupPatchMetadataForStruct gets subschema and the patch metadata (e.g. patch strategy and merge key) for map.
+ LookupPatchMetadataForStruct(key string) (LookupPatchMeta, PatchMeta, error)
+ // LookupPatchMetadataForSlice get subschema and the patch metadata for slice.
+ LookupPatchMetadataForSlice(key string) (LookupPatchMeta, PatchMeta, error)
+ // Get the type name of the field
+ Name() string
+}
+
+type PatchMetaFromStruct struct {
+ T reflect.Type
+}
+
+func NewPatchMetaFromStruct(dataStruct interface{}) (PatchMetaFromStruct, error) {
+ t, err := getTagStructType(dataStruct)
+ return PatchMetaFromStruct{T: t}, err
+}
+
+var _ LookupPatchMeta = PatchMetaFromStruct{}
+
+func (s PatchMetaFromStruct) LookupPatchMetadataForStruct(key string) (LookupPatchMeta, PatchMeta, error) {
+ fieldType, fieldPatchStrategies, fieldPatchMergeKey, err := forkedjson.LookupPatchMetadataForStruct(s.T, key)
+ if err != nil {
+ return nil, PatchMeta{}, err
+ }
+
+ return PatchMetaFromStruct{T: fieldType},
+ PatchMeta{
+ patchStrategies: fieldPatchStrategies,
+ patchMergeKey: fieldPatchMergeKey,
+ }, nil
+}
+
+func (s PatchMetaFromStruct) LookupPatchMetadataForSlice(key string) (LookupPatchMeta, PatchMeta, error) {
+ subschema, patchMeta, err := s.LookupPatchMetadataForStruct(key)
+ if err != nil {
+ return nil, PatchMeta{}, err
+ }
+ elemPatchMetaFromStruct := subschema.(PatchMetaFromStruct)
+ t := elemPatchMetaFromStruct.T
+
+ var elemType reflect.Type
+ switch t.Kind() {
+ // If t is an array or a slice, get the element type.
+ // If element is still an array or a slice, return an error.
+ // Otherwise, return element type.
+ case reflect.Array, reflect.Slice:
+ elemType = t.Elem()
+ if elemType.Kind() == reflect.Array || elemType.Kind() == reflect.Slice {
+ return nil, PatchMeta{}, errors.New("unexpected slice of slice")
+ }
+ // If t is an pointer, get the underlying element.
+ // If the underlying element is neither an array nor a slice, the pointer is pointing to a slice,
+ // e.g. https://github.com/kubernetes/kubernetes/blob/bc22e206c79282487ea0bf5696d5ccec7e839a76/staging/src/k8s.io/apimachinery/pkg/util/strategicpatch/patch_test.go#L2782-L2822
+ // If the underlying element is either an array or a slice, return its element type.
+ case reflect.Ptr:
+ t = t.Elem()
+ if t.Kind() == reflect.Array || t.Kind() == reflect.Slice {
+ t = t.Elem()
+ }
+ elemType = t
+ default:
+ return nil, PatchMeta{}, fmt.Errorf("expected slice or array type, but got: %s", s.T.Kind().String())
+ }
+
+ return PatchMetaFromStruct{T: elemType}, patchMeta, nil
+}
+
+func (s PatchMetaFromStruct) Name() string {
+ return s.T.Kind().String()
+}
+
+func getTagStructType(dataStruct interface{}) (reflect.Type, error) {
+ if dataStruct == nil {
+ return nil, mergepatch.ErrBadArgKind(struct{}{}, nil)
+ }
+
+ t := reflect.TypeOf(dataStruct)
+ // Get the underlying type for pointers
+ if t.Kind() == reflect.Ptr {
+ t = t.Elem()
+ }
+
+ if t.Kind() != reflect.Struct {
+ return nil, mergepatch.ErrBadArgKind(struct{}{}, dataStruct)
+ }
+
+ return t, nil
+}
+
+func GetTagStructTypeOrDie(dataStruct interface{}) reflect.Type {
+ t, err := getTagStructType(dataStruct)
+ if err != nil {
+ panic(err)
+ }
+ return t
+}
+
+type PatchMetaFromOpenAPI struct {
+ Schema openapi.Schema
+}
+
+func NewPatchMetaFromOpenAPI(s openapi.Schema) PatchMetaFromOpenAPI {
+ return PatchMetaFromOpenAPI{Schema: s}
+}
+
+var _ LookupPatchMeta = PatchMetaFromOpenAPI{}
+
+func (s PatchMetaFromOpenAPI) LookupPatchMetadataForStruct(key string) (LookupPatchMeta, PatchMeta, error) {
+ if s.Schema == nil {
+ return nil, PatchMeta{}, nil
+ }
+ kindItem := NewKindItem(key, s.Schema.GetPath())
+ s.Schema.Accept(kindItem)
+
+ err := kindItem.Error()
+ if err != nil {
+ return nil, PatchMeta{}, err
+ }
+ return PatchMetaFromOpenAPI{Schema: kindItem.subschema},
+ kindItem.patchmeta, nil
+}
+
+func (s PatchMetaFromOpenAPI) LookupPatchMetadataForSlice(key string) (LookupPatchMeta, PatchMeta, error) {
+ if s.Schema == nil {
+ return nil, PatchMeta{}, nil
+ }
+ sliceItem := NewSliceItem(key, s.Schema.GetPath())
+ s.Schema.Accept(sliceItem)
+
+ err := sliceItem.Error()
+ if err != nil {
+ return nil, PatchMeta{}, err
+ }
+ return PatchMetaFromOpenAPI{Schema: sliceItem.subschema},
+ sliceItem.patchmeta, nil
+}
+
+func (s PatchMetaFromOpenAPI) Name() string {
+ schema := s.Schema
+ return schema.GetName()
+}
diff --git a/vendor/k8s.io/apimachinery/pkg/util/strategicpatch/patch.go b/vendor/k8s.io/apimachinery/pkg/util/strategicpatch/patch.go
index 8884c738e..2f6ade2be 100644
--- a/vendor/k8s.io/apimachinery/pkg/util/strategicpatch/patch.go
+++ b/vendor/k8s.io/apimachinery/pkg/util/strategicpatch/patch.go
@@ -22,9 +22,9 @@ import (
"sort"
"strings"
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/util/json"
"k8s.io/apimachinery/pkg/util/mergepatch"
- forkedjson "k8s.io/apimachinery/third_party/forked/golang/json"
)
// An alternate implementation of JSON Merge Patch
@@ -92,6 +92,16 @@ type MergeOptions struct {
// return a patch that yields the modified document when applied to the original document, or an error
// if either of the two documents is invalid.
func CreateTwoWayMergePatch(original, modified []byte, dataStruct interface{}, fns ...mergepatch.PreconditionFunc) ([]byte, error) {
+ schema, err := NewPatchMetaFromStruct(dataStruct)
+ if err != nil {
+ return nil, err
+ }
+
+ return CreateTwoWayMergePatchUsingLookupPatchMeta(original, modified, schema, fns...)
+}
+
+func CreateTwoWayMergePatchUsingLookupPatchMeta(
+ original, modified []byte, schema LookupPatchMeta, fns ...mergepatch.PreconditionFunc) ([]byte, error) {
originalMap := map[string]interface{}{}
if len(original) > 0 {
if err := json.Unmarshal(original, &originalMap); err != nil {
@@ -106,7 +116,7 @@ func CreateTwoWayMergePatch(original, modified []byte, dataStruct interface{}, f
}
}
- patchMap, err := CreateTwoWayMergeMapPatch(originalMap, modifiedMap, dataStruct, fns...)
+ patchMap, err := CreateTwoWayMergeMapPatchUsingLookupPatchMeta(originalMap, modifiedMap, schema, fns...)
if err != nil {
return nil, err
}
@@ -118,15 +128,19 @@ func CreateTwoWayMergePatch(original, modified []byte, dataStruct interface{}, f
// encoded JSONMap.
// The serialized version of the map can then be passed to StrategicMergeMapPatch.
func CreateTwoWayMergeMapPatch(original, modified JSONMap, dataStruct interface{}, fns ...mergepatch.PreconditionFunc) (JSONMap, error) {
- t, err := getTagStructType(dataStruct)
+ schema, err := NewPatchMetaFromStruct(dataStruct)
if err != nil {
return nil, err
}
+ return CreateTwoWayMergeMapPatchUsingLookupPatchMeta(original, modified, schema, fns...)
+}
+
+func CreateTwoWayMergeMapPatchUsingLookupPatchMeta(original, modified JSONMap, schema LookupPatchMeta, fns ...mergepatch.PreconditionFunc) (JSONMap, error) {
diffOptions := DiffOptions{
SetElementOrder: true,
}
- patchMap, err := diffMaps(original, modified, t, diffOptions)
+ patchMap, err := diffMaps(original, modified, schema, diffOptions)
if err != nil {
return nil, err
}
@@ -151,12 +165,9 @@ func CreateTwoWayMergeMapPatch(original, modified JSONMap, dataStruct interface{
// - IFF list of primitives && merge strategy - use parallel deletion list
// - IFF list of maps or primitives with replace strategy (default) - set patch value to the value in modified
// - Build $retainKeys directive for fields with retainKeys patch strategy
-func diffMaps(original, modified map[string]interface{}, t reflect.Type, diffOptions DiffOptions) (map[string]interface{}, error) {
+func diffMaps(original, modified map[string]interface{}, schema LookupPatchMeta, diffOptions DiffOptions) (map[string]interface{}, error) {
patch := map[string]interface{}{}
- // Get the underlying type for pointers
- if t.Kind() == reflect.Ptr {
- t = t.Elem()
- }
+
// This will be used to build the $retainKeys directive sent in the patch
retainKeysList := make([]interface{}, 0, len(modified))
@@ -198,10 +209,10 @@ func diffMaps(original, modified map[string]interface{}, t reflect.Type, diffOpt
switch originalValueTyped := originalValue.(type) {
case map[string]interface{}:
modifiedValueTyped := modifiedValue.(map[string]interface{})
- err = handleMapDiff(key, originalValueTyped, modifiedValueTyped, patch, t, diffOptions)
+ err = handleMapDiff(key, originalValueTyped, modifiedValueTyped, patch, schema, diffOptions)
case []interface{}:
modifiedValueTyped := modifiedValue.([]interface{})
- err = handleSliceDiff(key, originalValueTyped, modifiedValueTyped, patch, t, diffOptions)
+ err = handleSliceDiff(key, originalValueTyped, modifiedValueTyped, patch, schema, diffOptions)
default:
replacePatchFieldIfNotEqual(key, originalValue, modifiedValue, patch, diffOptions)
}
@@ -248,8 +259,9 @@ func handleDirectiveMarker(key string, originalValue, modifiedValue interface{},
// patch is the patch map that contains key and the updated value, and it is the parent of originalValue, modifiedValue
// diffOptions contains multiple options to control how we do the diff.
func handleMapDiff(key string, originalValue, modifiedValue, patch map[string]interface{},
- t reflect.Type, diffOptions DiffOptions) error {
- fieldType, fieldPatchStrategies, _, err := forkedjson.LookupPatchMetadata(t, key)
+ schema LookupPatchMeta, diffOptions DiffOptions) error {
+ subschema, patchMeta, err := schema.LookupPatchMetadataForStruct(key)
+
if err != nil {
// We couldn't look up metadata for the field
// If the values are identical, this doesn't matter, no patch is needed
@@ -259,7 +271,7 @@ func handleMapDiff(key string, originalValue, modifiedValue, patch map[string]in
// Otherwise, return the error
return err
}
- retainKeys, patchStrategy, err := extractRetainKeysPatchStrategy(fieldPatchStrategies)
+ retainKeys, patchStrategy, err := extractRetainKeysPatchStrategy(patchMeta.GetPatchStrategies())
if err != nil {
return err
}
@@ -271,7 +283,7 @@ func handleMapDiff(key string, originalValue, modifiedValue, patch map[string]in
patch[key] = modifiedValue
}
default:
- patchValue, err := diffMaps(originalValue, modifiedValue, fieldType, diffOptions)
+ patchValue, err := diffMaps(originalValue, modifiedValue, subschema, diffOptions)
if err != nil {
return err
}
@@ -290,8 +302,8 @@ func handleMapDiff(key string, originalValue, modifiedValue, patch map[string]in
// patch is the patch map that contains key and the updated value, and it is the parent of originalValue, modifiedValue
// diffOptions contains multiple options to control how we do the diff.
func handleSliceDiff(key string, originalValue, modifiedValue []interface{}, patch map[string]interface{},
- t reflect.Type, diffOptions DiffOptions) error {
- fieldType, fieldPatchStrategies, fieldPatchMergeKey, err := forkedjson.LookupPatchMetadata(t, key)
+ schema LookupPatchMeta, diffOptions DiffOptions) error {
+ subschema, patchMeta, err := schema.LookupPatchMetadataForSlice(key)
if err != nil {
// We couldn't look up metadata for the field
// If the values are identical, this doesn't matter, no patch is needed
@@ -301,7 +313,7 @@ func handleSliceDiff(key string, originalValue, modifiedValue []interface{}, pat
// Otherwise, return the error
return err
}
- retainKeys, patchStrategy, err := extractRetainKeysPatchStrategy(fieldPatchStrategies)
+ retainKeys, patchStrategy, err := extractRetainKeysPatchStrategy(patchMeta.GetPatchStrategies())
if err != nil {
return err
}
@@ -309,7 +321,7 @@ func handleSliceDiff(key string, originalValue, modifiedValue []interface{}, pat
// Merge the 2 slices using mergePatchKey
case mergeDirective:
diffOptions.BuildRetainKeysDirective = retainKeys
- addList, deletionList, setOrderList, err := diffLists(originalValue, modifiedValue, fieldType.Elem(), fieldPatchMergeKey, diffOptions)
+ addList, deletionList, setOrderList, err := diffLists(originalValue, modifiedValue, subschema, patchMeta.GetPatchMergeKey(), diffOptions)
if err != nil {
return err
}
@@ -411,7 +423,7 @@ func normalizeElementOrder(patch, serverOnly, patchOrder, serverOrder []interfac
// scan from the place of last insertion in `right` to the end of `right`,
// the place is before the first item that is greater than the item we want to insert.
// example usage: using server-only items as left and patch items as right. We insert server-only items
-// to patch list. We use the order of live object as record for comparision.
+// to patch list. We use the order of live object as record for comparison.
func mergeSortedSlice(left, right, serverOrder []interface{}, mergeKey string, kind reflect.Kind) []interface{} {
// Returns if l is less than r, and if both have been found.
// If l and r both present and l is in front of r, l is less than r.
@@ -515,6 +527,9 @@ func normalizeSliceOrder(toSort, order []interface{}, mergeKey string, kind refl
return nil, err
}
toSort, toDelete, err = extractToDeleteItems(toSort)
+ if err != nil {
+ return nil, err
+ }
}
sort.SliceStable(toSort, func(i, j int) bool {
@@ -533,7 +548,7 @@ func normalizeSliceOrder(toSort, order []interface{}, mergeKey string, kind refl
// another list to set the order of the list
// Only list of primitives with merge strategy will generate a parallel deletion list.
// These two lists should yield modified when applied to original, for lists with merge semantics.
-func diffLists(original, modified []interface{}, t reflect.Type, mergeKey string, diffOptions DiffOptions) ([]interface{}, []interface{}, []interface{}, error) {
+func diffLists(original, modified []interface{}, schema LookupPatchMeta, mergeKey string, diffOptions DiffOptions) ([]interface{}, []interface{}, []interface{}, error) {
if len(original) == 0 {
// Both slices are empty - do nothing
if len(modified) == 0 || diffOptions.IgnoreChangesAndAdditions {
@@ -553,8 +568,14 @@ func diffLists(original, modified []interface{}, t reflect.Type, mergeKey string
kind := elementType.Kind()
switch kind {
case reflect.Map:
- patchList, deleteList, err = diffListsOfMaps(original, modified, t, mergeKey, diffOptions)
+ patchList, deleteList, err = diffListsOfMaps(original, modified, schema, mergeKey, diffOptions)
+ if err != nil {
+ return nil, nil, nil, err
+ }
patchList, err = normalizeSliceOrder(patchList, modified, mergeKey, kind)
+ if err != nil {
+ return nil, nil, nil, err
+ }
orderSame, err := isOrderSame(original, modified, mergeKey)
if err != nil {
return nil, nil, nil, err
@@ -580,6 +601,9 @@ func diffLists(original, modified []interface{}, t reflect.Type, mergeKey string
return nil, nil, nil, mergepatch.ErrNoListOfLists
default:
patchList, deleteList, err = diffListsOfScalars(original, modified, diffOptions)
+ if err != nil {
+ return nil, nil, nil, err
+ }
patchList, err = normalizeSliceOrder(patchList, modified, mergeKey, kind)
// generate the setElementOrder list when there are content changes or order changes
if diffOptions.SetElementOrder && ((!diffOptions.IgnoreDeletions && len(deleteList) > 0) ||
@@ -690,15 +714,15 @@ func compareListValuesAtIndex(list1Inbounds, list2Inbounds bool, list1Value, lis
// diffListsOfMaps takes a pair of lists and
// returns a (recursive) strategic merge patch list contains additions and changes and
// a deletion list contains deletions
-func diffListsOfMaps(original, modified []interface{}, t reflect.Type, mergeKey string, diffOptions DiffOptions) ([]interface{}, []interface{}, error) {
+func diffListsOfMaps(original, modified []interface{}, schema LookupPatchMeta, mergeKey string, diffOptions DiffOptions) ([]interface{}, []interface{}, error) {
patch := make([]interface{}, 0, len(modified))
deletionList := make([]interface{}, 0, len(original))
- originalSorted, err := sortMergeListsByNameArray(original, t, mergeKey, false)
+ originalSorted, err := sortMergeListsByNameArray(original, schema, mergeKey, false)
if err != nil {
return nil, nil, err
}
- modifiedSorted, err := sortMergeListsByNameArray(modified, t, mergeKey, false)
+ modifiedSorted, err := sortMergeListsByNameArray(modified, schema, mergeKey, false)
if err != nil {
return nil, nil, err
}
@@ -733,7 +757,7 @@ func diffListsOfMaps(original, modified []interface{}, t reflect.Type, mergeKey
switch {
case bothInBounds && ItemMatchesOriginalAndModifiedSlice(originalElementMergeKeyValueString, modifiedElementMergeKeyValueString):
// Merge key values are equal, so recurse
- patchValue, err := diffMaps(originalElement, modifiedElement, t, diffOptions)
+ patchValue, err := diffMaps(originalElement, modifiedElement, schema, diffOptions)
if err != nil {
return nil, nil, err
}
@@ -786,6 +810,15 @@ func getMapAndMergeKeyValueByIndex(index int, mergeKey string, listOfMaps []inte
// must be json encoded content. A patch can be created from an original and a modified document
// by calling CreateStrategicMergePatch.
func StrategicMergePatch(original, patch []byte, dataStruct interface{}) ([]byte, error) {
+ schema, err := NewPatchMetaFromStruct(dataStruct)
+ if err != nil {
+ return nil, err
+ }
+
+ return StrategicMergePatchUsingLookupPatchMeta(original, patch, schema)
+}
+
+func StrategicMergePatchUsingLookupPatchMeta(original, patch []byte, schema LookupPatchMeta) ([]byte, error) {
originalMap, err := handleUnmarshal(original)
if err != nil {
return nil, err
@@ -795,7 +828,7 @@ func StrategicMergePatch(original, patch []byte, dataStruct interface{}) ([]byte
return nil, err
}
- result, err := StrategicMergeMapPatch(originalMap, patchMap, dataStruct)
+ result, err := StrategicMergeMapPatchUsingLookupPatchMeta(originalMap, patchMap, schema)
if err != nil {
return nil, err
}
@@ -816,38 +849,35 @@ func handleUnmarshal(j []byte) (map[string]interface{}, error) {
return m, nil
}
-// StrategicMergePatch applies a strategic merge patch. The original and patch documents
+// StrategicMergeMapPatch applies a strategic merge patch. The original and patch documents
// must be JSONMap. A patch can be created from an original and modified document by
// calling CreateTwoWayMergeMapPatch.
// Warning: the original and patch JSONMap objects are mutated by this function and should not be reused.
func StrategicMergeMapPatch(original, patch JSONMap, dataStruct interface{}) (JSONMap, error) {
- t, err := getTagStructType(dataStruct)
+ schema, err := NewPatchMetaFromStruct(dataStruct)
if err != nil {
return nil, err
}
- mergeOptions := MergeOptions{
- MergeParallelList: true,
- IgnoreUnmatchedNulls: true,
- }
- return mergeMap(original, patch, t, mergeOptions)
-}
-func getTagStructType(dataStruct interface{}) (reflect.Type, error) {
- if dataStruct == nil {
- return nil, mergepatch.ErrBadArgKind(struct{}{}, nil)
- }
+ // We need the go struct tags `patchMergeKey` and `patchStrategy` for fields that support a strategic merge patch.
+ // For native resources, we can easily figure out these tags since we know the fields.
- t := reflect.TypeOf(dataStruct)
- // Get the underlying type for pointers
- if t.Kind() == reflect.Ptr {
- t = t.Elem()
+ // Because custom resources are decoded as Unstructured and because we're missing the metadata about how to handle
+ // each field in a strategic merge patch, we can't find the go struct tags. Hence, we can't easily do a strategic merge
+ // for custom resources. So we should fail fast and return an error.
+ if _, ok := dataStruct.(*unstructured.Unstructured); ok {
+ return nil, mergepatch.ErrUnsupportedStrategicMergePatchFormat
}
- if t.Kind() != reflect.Struct {
- return nil, mergepatch.ErrBadArgKind(struct{}{}, dataStruct)
- }
+ return StrategicMergeMapPatchUsingLookupPatchMeta(original, patch, schema)
+}
- return t, nil
+func StrategicMergeMapPatchUsingLookupPatchMeta(original, patch JSONMap, schema LookupPatchMeta) (JSONMap, error) {
+ mergeOptions := MergeOptions{
+ MergeParallelList: true,
+ IgnoreUnmatchedNulls: true,
+ }
+ return mergeMap(original, patch, schema, mergeOptions)
}
// handleDirectiveInMergeMap handles the patch directive when merging 2 maps.
@@ -1054,8 +1084,8 @@ func applyRetainKeysDirective(original, patch map[string]interface{}, options Me
// Then, sort them by the relative order in setElementOrder, patch list and live list.
// The precedence is $setElementOrder > order in patch list > order in live list.
// This function will delete the item after merging it to prevent process it again in the future.
-// Ref: https://git.k8s.io/community/contributors/design-proposals/preserve-order-in-strategic-merge-patch.md
-func mergePatchIntoOriginal(original, patch map[string]interface{}, t reflect.Type, mergeOptions MergeOptions) error {
+// Ref: https://git.k8s.io/community/contributors/design-proposals/cli/preserve-order-in-strategic-merge-patch.md
+func mergePatchIntoOriginal(original, patch map[string]interface{}, schema LookupPatchMeta, mergeOptions MergeOptions) error {
for key, patchV := range patch {
// Do nothing if there is no ordering directive
if !strings.HasPrefix(key, setElementOrderDirectivePrefix) {
@@ -1082,9 +1112,9 @@ func mergePatchIntoOriginal(original, patch map[string]interface{}, t reflect.Ty
var (
ok bool
originalFieldValue, patchFieldValue, merged []interface{}
- patchStrategy, mergeKey string
- patchStrategies []string
- fieldType reflect.Type
+ patchStrategy string
+ patchMeta PatchMeta
+ subschema LookupPatchMeta
)
typedSetElementOrderList, ok := setElementOrderInPatch.([]interface{})
if !ok {
@@ -1110,16 +1140,16 @@ func mergePatchIntoOriginal(original, patch map[string]interface{}, t reflect.Ty
return mergepatch.ErrBadArgType(patchFieldValue, patchList)
}
}
- fieldType, patchStrategies, mergeKey, err = forkedjson.LookupPatchMetadata(t, originalKey)
+ subschema, patchMeta, err = schema.LookupPatchMetadataForSlice(originalKey)
if err != nil {
return err
}
- _, patchStrategy, err = extractRetainKeysPatchStrategy(patchStrategies)
+ _, patchStrategy, err = extractRetainKeysPatchStrategy(patchMeta.GetPatchStrategies())
if err != nil {
return err
}
// Check for consistency between the element order list and the field it applies to
- err = validatePatchWithSetOrderList(patchFieldValue, typedSetElementOrderList, mergeKey)
+ err = validatePatchWithSetOrderList(patchFieldValue, typedSetElementOrderList, patchMeta.GetPatchMergeKey())
if err != nil {
return err
}
@@ -1132,8 +1162,8 @@ func mergePatchIntoOriginal(original, patch map[string]interface{}, t reflect.Ty
// list was added
merged = patchFieldValue
case foundOriginal && foundPatch:
- merged, err = mergeSliceHandler(originalList, patchList, fieldType,
- patchStrategy, mergeKey, false, mergeOptions)
+ merged, err = mergeSliceHandler(originalList, patchList, subschema,
+ patchStrategy, patchMeta.GetPatchMergeKey(), false, mergeOptions)
if err != nil {
return err
}
@@ -1143,13 +1173,13 @@ func mergePatchIntoOriginal(original, patch map[string]interface{}, t reflect.Ty
// Split all items into patch items and server-only items and then enforce the order.
var patchItems, serverOnlyItems []interface{}
- if len(mergeKey) == 0 {
+ if len(patchMeta.GetPatchMergeKey()) == 0 {
// Primitives doesn't need merge key to do partitioning.
patchItems, serverOnlyItems = partitionPrimitivesByPresentInList(merged, typedSetElementOrderList)
} else {
// Maps need merge key to do partitioning.
- patchItems, serverOnlyItems, err = partitionMapsByPresentInList(merged, typedSetElementOrderList, mergeKey)
+ patchItems, serverOnlyItems, err = partitionMapsByPresentInList(merged, typedSetElementOrderList, patchMeta.GetPatchMergeKey())
if err != nil {
return err
}
@@ -1163,7 +1193,7 @@ func mergePatchIntoOriginal(original, patch map[string]interface{}, t reflect.Ty
// normalize merged list
// typedSetElementOrderList contains all the relative order in typedPatchList,
// so don't need to use typedPatchList
- both, err := normalizeElementOrder(patchItems, serverOnlyItems, typedSetElementOrderList, originalFieldValue, mergeKey, kind)
+ both, err := normalizeElementOrder(patchItems, serverOnlyItems, typedSetElementOrderList, originalFieldValue, patchMeta.GetPatchMergeKey(), kind)
if err != nil {
return err
}
@@ -1225,7 +1255,7 @@ func partitionMapsByPresentInList(original, partitionBy []interface{}, mergeKey
// If patch contains any null field (e.g. field_1: null) that is not
// present in original, then to propagate it to the end result use
// mergeOptions.IgnoreUnmatchedNulls == false.
-func mergeMap(original, patch map[string]interface{}, t reflect.Type, mergeOptions MergeOptions) (map[string]interface{}, error) {
+func mergeMap(original, patch map[string]interface{}, schema LookupPatchMeta, mergeOptions MergeOptions) (map[string]interface{}, error) {
if v, ok := patch[directiveMarker]; ok {
return handleDirectiveInMergeMap(v, patch)
}
@@ -1245,7 +1275,7 @@ func mergeMap(original, patch map[string]interface{}, t reflect.Type, mergeOptio
// When not merging the directive, it will make sure $setElementOrder list exist only in original.
// When merging the directive, it will process $setElementOrder and its patch list together.
// This function will delete the merged elements from patch so they will not be reprocessed
- err = mergePatchIntoOriginal(original, patch, t, mergeOptions)
+ err = mergePatchIntoOriginal(original, patch, schema, mergeOptions)
if err != nil {
return nil, err
}
@@ -1283,11 +1313,6 @@ func mergeMap(original, patch map[string]interface{}, t reflect.Type, mergeOptio
continue
}
- // If the data type is a pointer, resolve the element.
- if t.Kind() == reflect.Ptr {
- t = t.Elem()
- }
-
originalType := reflect.TypeOf(original[k])
patchType := reflect.TypeOf(patchV)
if originalType != patchType {
@@ -1295,22 +1320,27 @@ func mergeMap(original, patch map[string]interface{}, t reflect.Type, mergeOptio
continue
}
// If they're both maps or lists, recurse into the value.
- // First find the fieldPatchStrategy and fieldPatchMergeKey.
- fieldType, fieldPatchStrategies, fieldPatchMergeKey, err := forkedjson.LookupPatchMetadata(t, k)
- if err != nil {
- return nil, err
- }
- _, patchStrategy, err := extractRetainKeysPatchStrategy(fieldPatchStrategies)
- if err != nil {
- return nil, err
- }
-
switch originalType.Kind() {
case reflect.Map:
-
- original[k], err = mergeMapHandler(original[k], patchV, fieldType, patchStrategy, mergeOptions)
+ subschema, patchMeta, err2 := schema.LookupPatchMetadataForStruct(k)
+ if err2 != nil {
+ return nil, err2
+ }
+ _, patchStrategy, err2 := extractRetainKeysPatchStrategy(patchMeta.GetPatchStrategies())
+ if err2 != nil {
+ return nil, err2
+ }
+ original[k], err = mergeMapHandler(original[k], patchV, subschema, patchStrategy, mergeOptions)
case reflect.Slice:
- original[k], err = mergeSliceHandler(original[k], patchV, fieldType, patchStrategy, fieldPatchMergeKey, isDeleteList, mergeOptions)
+ subschema, patchMeta, err2 := schema.LookupPatchMetadataForSlice(k)
+ if err2 != nil {
+ return nil, err2
+ }
+ _, patchStrategy, err2 := extractRetainKeysPatchStrategy(patchMeta.GetPatchStrategies())
+ if err2 != nil {
+ return nil, err2
+ }
+ original[k], err = mergeSliceHandler(original[k], patchV, subschema, patchStrategy, patchMeta.GetPatchMergeKey(), isDeleteList, mergeOptions)
default:
original[k] = patchV
}
@@ -1323,7 +1353,7 @@ func mergeMap(original, patch map[string]interface{}, t reflect.Type, mergeOptio
// mergeMapHandler handles how to merge `patchV` whose key is `key` with `original` respecting
// fieldPatchStrategy and mergeOptions.
-func mergeMapHandler(original, patch interface{}, fieldType reflect.Type,
+func mergeMapHandler(original, patch interface{}, schema LookupPatchMeta,
fieldPatchStrategy string, mergeOptions MergeOptions) (map[string]interface{}, error) {
typedOriginal, typedPatch, err := mapTypeAssertion(original, patch)
if err != nil {
@@ -1331,7 +1361,7 @@ func mergeMapHandler(original, patch interface{}, fieldType reflect.Type,
}
if fieldPatchStrategy != replaceDirective {
- return mergeMap(typedOriginal, typedPatch, fieldType, mergeOptions)
+ return mergeMap(typedOriginal, typedPatch, schema, mergeOptions)
} else {
return typedPatch, nil
}
@@ -1339,7 +1369,7 @@ func mergeMapHandler(original, patch interface{}, fieldType reflect.Type,
// mergeSliceHandler handles how to merge `patchV` whose key is `key` with `original` respecting
// fieldPatchStrategy, fieldPatchMergeKey, isDeleteList and mergeOptions.
-func mergeSliceHandler(original, patch interface{}, fieldType reflect.Type,
+func mergeSliceHandler(original, patch interface{}, schema LookupPatchMeta,
fieldPatchStrategy, fieldPatchMergeKey string, isDeleteList bool, mergeOptions MergeOptions) ([]interface{}, error) {
typedOriginal, typedPatch, err := sliceTypeAssertion(original, patch)
if err != nil {
@@ -1347,8 +1377,7 @@ func mergeSliceHandler(original, patch interface{}, fieldType reflect.Type,
}
if fieldPatchStrategy == mergeDirective {
- elemType := fieldType.Elem()
- return mergeSlice(typedOriginal, typedPatch, elemType, fieldPatchMergeKey, mergeOptions, isDeleteList)
+ return mergeSlice(typedOriginal, typedPatch, schema, fieldPatchMergeKey, mergeOptions, isDeleteList)
} else {
return typedPatch, nil
}
@@ -1357,7 +1386,7 @@ func mergeSliceHandler(original, patch interface{}, fieldType reflect.Type,
// Merge two slices together. Note: This may modify both the original slice and
// the patch because getting a deep copy of a slice in golang is highly
// non-trivial.
-func mergeSlice(original, patch []interface{}, elemType reflect.Type, mergeKey string, mergeOptions MergeOptions, isDeleteList bool) ([]interface{}, error) {
+func mergeSlice(original, patch []interface{}, schema LookupPatchMeta, mergeKey string, mergeOptions MergeOptions, isDeleteList bool) ([]interface{}, error) {
if len(original) == 0 && len(patch) == 0 {
return original, nil
}
@@ -1382,7 +1411,7 @@ func mergeSlice(original, patch []interface{}, elemType reflect.Type, mergeKey s
} else {
if mergeKey == "" {
- return nil, fmt.Errorf("cannot merge lists without merge key for type %s", elemType.Kind().String())
+ return nil, fmt.Errorf("cannot merge lists without merge key for %s", schema.Name())
}
original, patch, err = mergeSliceWithSpecialElements(original, patch, mergeKey)
@@ -1390,7 +1419,7 @@ func mergeSlice(original, patch []interface{}, elemType reflect.Type, mergeKey s
return nil, err
}
- merged, err = mergeSliceWithoutSpecialElements(original, patch, mergeKey, elemType, mergeOptions)
+ merged, err = mergeSliceWithoutSpecialElements(original, patch, mergeKey, schema, mergeOptions)
if err != nil {
return nil, err
}
@@ -1468,7 +1497,7 @@ func deleteMatchingEntries(original []interface{}, mergeKey string, mergeValue i
// mergeSliceWithoutSpecialElements merges slices with non-special elements.
// original and patch must be slices of maps, they should be checked before calling this function.
-func mergeSliceWithoutSpecialElements(original, patch []interface{}, mergeKey string, elemType reflect.Type, mergeOptions MergeOptions) ([]interface{}, error) {
+func mergeSliceWithoutSpecialElements(original, patch []interface{}, mergeKey string, schema LookupPatchMeta, mergeOptions MergeOptions) ([]interface{}, error) {
for _, v := range patch {
typedV := v.(map[string]interface{})
mergeValue, ok := typedV[mergeKey]
@@ -1487,7 +1516,7 @@ func mergeSliceWithoutSpecialElements(original, patch []interface{}, mergeKey st
var mergedMaps interface{}
var err error
// Merge into original.
- mergedMaps, err = mergeMap(originalMap, typedV, elemType, mergeOptions)
+ mergedMaps, err = mergeMap(originalMap, typedV, schema, mergeOptions)
if err != nil {
return nil, err
}
@@ -1520,7 +1549,7 @@ func findMapInSliceBasedOnKeyValue(m []interface{}, key string, value interface{
for k, v := range m {
typedV, ok := v.(map[string]interface{})
if !ok {
- return nil, 0, false, fmt.Errorf("value for key %v is not a map.", k)
+ return nil, 0, false, fmt.Errorf("value for key %v is not a map", k)
}
valueToMatch, ok := typedV[key]
@@ -1536,14 +1565,14 @@ func findMapInSliceBasedOnKeyValue(m []interface{}, key string, value interface{
// by key. This is needed by tests because in JSON, list order is significant,
// but in Strategic Merge Patch, merge lists do not have significant order.
// Sorting the lists allows for order-insensitive comparison of patched maps.
-func sortMergeListsByName(mapJSON []byte, dataStruct interface{}) ([]byte, error) {
+func sortMergeListsByName(mapJSON []byte, schema LookupPatchMeta) ([]byte, error) {
var m map[string]interface{}
err := json.Unmarshal(mapJSON, &m)
if err != nil {
- return nil, err
+ return nil, mergepatch.ErrBadJSONDoc
}
- newM, err := sortMergeListsByNameMap(m, reflect.TypeOf(dataStruct))
+ newM, err := sortMergeListsByNameMap(m, schema)
if err != nil {
return nil, err
}
@@ -1552,7 +1581,7 @@ func sortMergeListsByName(mapJSON []byte, dataStruct interface{}) ([]byte, error
}
// Function sortMergeListsByNameMap recursively sorts the merge lists by its mergeKey in a map.
-func sortMergeListsByNameMap(s map[string]interface{}, t reflect.Type) (map[string]interface{}, error) {
+func sortMergeListsByNameMap(s map[string]interface{}, schema LookupPatchMeta) (map[string]interface{}, error) {
newS := map[string]interface{}{}
for k, v := range s {
if k == retainKeysDirective {
@@ -1573,26 +1602,29 @@ func sortMergeListsByNameMap(s map[string]interface{}, t reflect.Type) (map[stri
return nil, mergepatch.ErrBadPatchFormatForSetElementOrderList
}
} else if k != directiveMarker {
- fieldType, fieldPatchStrategies, fieldPatchMergeKey, err := forkedjson.LookupPatchMetadata(t, k)
- if err != nil {
- return nil, err
- }
- _, patchStrategy, err := extractRetainKeysPatchStrategy(fieldPatchStrategies)
- if err != nil {
- return nil, err
- }
-
- // If v is a map or a merge slice, recurse.
- if typedV, ok := v.(map[string]interface{}); ok {
- var err error
- v, err = sortMergeListsByNameMap(typedV, fieldType)
+ // recurse for map and slice.
+ switch typedV := v.(type) {
+ case map[string]interface{}:
+ subschema, _, err := schema.LookupPatchMetadataForStruct(k)
+ if err != nil {
+ return nil, err
+ }
+ v, err = sortMergeListsByNameMap(typedV, subschema)
+ if err != nil {
+ return nil, err
+ }
+ case []interface{}:
+ subschema, patchMeta, err := schema.LookupPatchMetadataForSlice(k)
+ if err != nil {
+ return nil, err
+ }
+ _, patchStrategy, err := extractRetainKeysPatchStrategy(patchMeta.GetPatchStrategies())
if err != nil {
return nil, err
}
- } else if typedV, ok := v.([]interface{}); ok {
if patchStrategy == mergeDirective {
var err error
- v, err = sortMergeListsByNameArray(typedV, fieldType.Elem(), fieldPatchMergeKey, true)
+ v, err = sortMergeListsByNameArray(typedV, subschema, patchMeta.GetPatchMergeKey(), true)
if err != nil {
return nil, err
}
@@ -1607,7 +1639,7 @@ func sortMergeListsByNameMap(s map[string]interface{}, t reflect.Type) (map[stri
}
// Function sortMergeListsByNameMap recursively sorts the merge lists by its mergeKey in an array.
-func sortMergeListsByNameArray(s []interface{}, elemType reflect.Type, mergeKey string, recurse bool) ([]interface{}, error) {
+func sortMergeListsByNameArray(s []interface{}, schema LookupPatchMeta, mergeKey string, recurse bool) ([]interface{}, error) {
if len(s) == 0 {
return s, nil
}
@@ -1630,7 +1662,7 @@ func sortMergeListsByNameArray(s []interface{}, elemType reflect.Type, mergeKey
for _, elem := range s {
if recurse {
typedElem := elem.(map[string]interface{})
- newElem, err := sortMergeListsByNameMap(typedElem, elemType)
+ newElem, err := sortMergeListsByNameMap(typedElem, schema)
if err != nil {
return nil, err
}
@@ -1776,18 +1808,13 @@ func sliceElementType(slices ...[]interface{}) (reflect.Type, error) {
// objects overlap with different values in any key. All keys are required to be
// strings. Since patches of the same Type have congruent keys, this is valid
// for multiple patch types. This method supports strategic merge patch semantics.
-func MergingMapsHaveConflicts(left, right map[string]interface{}, dataStruct interface{}) (bool, error) {
- t, err := getTagStructType(dataStruct)
- if err != nil {
- return true, err
- }
-
- return mergingMapFieldsHaveConflicts(left, right, t, "", "")
+func MergingMapsHaveConflicts(left, right map[string]interface{}, schema LookupPatchMeta) (bool, error) {
+ return mergingMapFieldsHaveConflicts(left, right, schema, "", "")
}
func mergingMapFieldsHaveConflicts(
left, right interface{},
- fieldType reflect.Type,
+ schema LookupPatchMeta,
fieldPatchStrategy, fieldPatchMergeKey string,
) (bool, error) {
switch leftType := left.(type) {
@@ -1818,15 +1845,14 @@ func mergingMapFieldsHaveConflicts(
return false, nil
}
// Check the individual keys.
- return mapsHaveConflicts(leftType, rightType, fieldType)
+ return mapsHaveConflicts(leftType, rightType, schema)
case []interface{}:
rightType, ok := right.([]interface{})
if !ok {
return true, nil
}
- return slicesHaveConflicts(leftType, rightType, fieldType, fieldPatchStrategy, fieldPatchMergeKey)
-
+ return slicesHaveConflicts(leftType, rightType, schema, fieldPatchStrategy, fieldPatchMergeKey)
case string, float64, bool, int, int64, nil:
return !reflect.DeepEqual(left, right), nil
default:
@@ -1834,21 +1860,37 @@ func mergingMapFieldsHaveConflicts(
}
}
-func mapsHaveConflicts(typedLeft, typedRight map[string]interface{}, structType reflect.Type) (bool, error) {
+func mapsHaveConflicts(typedLeft, typedRight map[string]interface{}, schema LookupPatchMeta) (bool, error) {
for key, leftValue := range typedLeft {
if key != directiveMarker && key != retainKeysDirective {
if rightValue, ok := typedRight[key]; ok {
- fieldType, fieldPatchStrategies, fieldPatchMergeKey, err := forkedjson.LookupPatchMetadata(structType, key)
- if err != nil {
- return true, err
- }
- _, patchStrategy, err := extractRetainKeysPatchStrategy(fieldPatchStrategies)
- if err != nil {
- return true, err
+ var subschema LookupPatchMeta
+ var patchMeta PatchMeta
+ var patchStrategy string
+ var err error
+ switch leftValue.(type) {
+ case []interface{}:
+ subschema, patchMeta, err = schema.LookupPatchMetadataForSlice(key)
+ if err != nil {
+ return true, err
+ }
+ _, patchStrategy, err = extractRetainKeysPatchStrategy(patchMeta.patchStrategies)
+ if err != nil {
+ return true, err
+ }
+ case map[string]interface{}:
+ subschema, patchMeta, err = schema.LookupPatchMetadataForStruct(key)
+ if err != nil {
+ return true, err
+ }
+ _, patchStrategy, err = extractRetainKeysPatchStrategy(patchMeta.patchStrategies)
+ if err != nil {
+ return true, err
+ }
}
if hasConflicts, err := mergingMapFieldsHaveConflicts(leftValue, rightValue,
- fieldType, patchStrategy, fieldPatchMergeKey); hasConflicts {
+ subschema, patchStrategy, patchMeta.GetPatchMergeKey()); hasConflicts {
return true, err
}
}
@@ -1860,7 +1902,7 @@ func mapsHaveConflicts(typedLeft, typedRight map[string]interface{}, structType
func slicesHaveConflicts(
typedLeft, typedRight []interface{},
- fieldType reflect.Type,
+ schema LookupPatchMeta,
fieldPatchStrategy, fieldPatchMergeKey string,
) (bool, error) {
elementType, err := sliceElementType(typedLeft, typedRight)
@@ -1868,7 +1910,6 @@ func slicesHaveConflicts(
return true, err
}
- valueType := fieldType.Elem()
if fieldPatchStrategy == mergeDirective {
// Merging lists of scalars have no conflicts by definition
// So we only need to check further if the elements are maps
@@ -1887,7 +1928,7 @@ func slicesHaveConflicts(
return true, err
}
- return mapsOfMapsHaveConflicts(leftMap, rightMap, valueType)
+ return mapsOfMapsHaveConflicts(leftMap, rightMap, schema)
}
// Either we don't have type information, or these are non-merging lists
@@ -1905,7 +1946,7 @@ func slicesHaveConflicts(
// Compare the slices element by element in order
// This test will fail if the slices are not sorted
for i := range typedLeft {
- if hasConflicts, err := mergingMapFieldsHaveConflicts(typedLeft[i], typedRight[i], valueType, "", ""); hasConflicts {
+ if hasConflicts, err := mergingMapFieldsHaveConflicts(typedLeft[i], typedRight[i], schema, "", ""); hasConflicts {
return true, err
}
}
@@ -1932,10 +1973,10 @@ func sliceOfMapsToMapOfMaps(slice []interface{}, mergeKey string) (map[string]in
return result, nil
}
-func mapsOfMapsHaveConflicts(typedLeft, typedRight map[string]interface{}, structType reflect.Type) (bool, error) {
+func mapsOfMapsHaveConflicts(typedLeft, typedRight map[string]interface{}, schema LookupPatchMeta) (bool, error) {
for key, leftValue := range typedLeft {
if rightValue, ok := typedRight[key]; ok {
- if hasConflicts, err := mergingMapFieldsHaveConflicts(leftValue, rightValue, structType, "", ""); hasConflicts {
+ if hasConflicts, err := mergingMapFieldsHaveConflicts(leftValue, rightValue, schema, "", ""); hasConflicts {
return true, err
}
}
@@ -1955,7 +1996,7 @@ func mapsOfMapsHaveConflicts(typedLeft, typedRight map[string]interface{}, struc
// in a way that is different from how it is changed in current (e.g., deleting it, changing its
// value). We also propagate values fields that do not exist in original but are explicitly
// defined in modified.
-func CreateThreeWayMergePatch(original, modified, current []byte, dataStruct interface{}, overwrite bool, fns ...mergepatch.PreconditionFunc) ([]byte, error) {
+func CreateThreeWayMergePatch(original, modified, current []byte, schema LookupPatchMeta, overwrite bool, fns ...mergepatch.PreconditionFunc) ([]byte, error) {
originalMap := map[string]interface{}{}
if len(original) > 0 {
if err := json.Unmarshal(original, &originalMap); err != nil {
@@ -1977,11 +2018,6 @@ func CreateThreeWayMergePatch(original, modified, current []byte, dataStruct int
}
}
- t, err := getTagStructType(dataStruct)
- if err != nil {
- return nil, err
- }
-
// The patch is the difference from current to modified without deletions, plus deletions
// from original to modified. To find it, we compute deletions, which are the deletions from
// original to modified, and delta, which is the difference from current to modified without
@@ -1990,7 +2026,7 @@ func CreateThreeWayMergePatch(original, modified, current []byte, dataStruct int
IgnoreDeletions: true,
SetElementOrder: true,
}
- deltaMap, err := diffMaps(currentMap, modifiedMap, t, deltaMapDiffOptions)
+ deltaMap, err := diffMaps(currentMap, modifiedMap, schema, deltaMapDiffOptions)
if err != nil {
return nil, err
}
@@ -1998,13 +2034,13 @@ func CreateThreeWayMergePatch(original, modified, current []byte, dataStruct int
SetElementOrder: true,
IgnoreChangesAndAdditions: true,
}
- deletionsMap, err := diffMaps(originalMap, modifiedMap, t, deletionsMapDiffOptions)
+ deletionsMap, err := diffMaps(originalMap, modifiedMap, schema, deletionsMapDiffOptions)
if err != nil {
return nil, err
}
mergeOptions := MergeOptions{}
- patchMap, err := mergeMap(deletionsMap, deltaMap, t, mergeOptions)
+ patchMap, err := mergeMap(deletionsMap, deltaMap, schema, mergeOptions)
if err != nil {
return nil, err
}
@@ -2020,12 +2056,12 @@ func CreateThreeWayMergePatch(original, modified, current []byte, dataStruct int
// then return a conflict error.
if !overwrite {
changeMapDiffOptions := DiffOptions{}
- changedMap, err := diffMaps(originalMap, currentMap, t, changeMapDiffOptions)
+ changedMap, err := diffMaps(originalMap, currentMap, schema, changeMapDiffOptions)
if err != nil {
return nil, err
}
- hasConflicts, err := MergingMapsHaveConflicts(patchMap, changedMap, dataStruct)
+ hasConflicts, err := MergingMapsHaveConflicts(patchMap, changedMap, schema)
if err != nil {
return nil, err
}
@@ -2073,7 +2109,7 @@ func sliceTypeAssertion(original, patch interface{}) ([]interface{}, []interface
}
// extractRetainKeysPatchStrategy process patch strategy, which is a string may contains multiple
-// patch strategies seperated by ",". It returns a boolean var indicating if it has
+// patch strategies separated by ",". It returns a boolean var indicating if it has
// retainKeys strategies and a string for the other strategy.
func extractRetainKeysPatchStrategy(strategies []string) (bool, string, error) {
switch len(strategies) {
diff --git a/vendor/k8s.io/apimachinery/pkg/util/strategicpatch/types.go b/vendor/k8s.io/apimachinery/pkg/util/strategicpatch/types.go
new file mode 100644
index 000000000..f84d65aac
--- /dev/null
+++ b/vendor/k8s.io/apimachinery/pkg/util/strategicpatch/types.go
@@ -0,0 +1,193 @@
+/*
+Copyright 2017 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package strategicpatch
+
+import (
+ "errors"
+ "strings"
+
+ "k8s.io/apimachinery/pkg/util/mergepatch"
+ openapi "k8s.io/kube-openapi/pkg/util/proto"
+)
+
+const (
+ patchStrategyOpenapiextensionKey = "x-kubernetes-patch-strategy"
+ patchMergeKeyOpenapiextensionKey = "x-kubernetes-patch-merge-key"
+)
+
+type LookupPatchItem interface {
+ openapi.SchemaVisitor
+
+ Error() error
+ Path() *openapi.Path
+}
+
+type kindItem struct {
+ key string
+ path *openapi.Path
+ err error
+ patchmeta PatchMeta
+ subschema openapi.Schema
+ hasVisitKind bool
+}
+
+func NewKindItem(key string, path *openapi.Path) *kindItem {
+ return &kindItem{
+ key: key,
+ path: path,
+ }
+}
+
+var _ LookupPatchItem = &kindItem{}
+
+func (item *kindItem) Error() error {
+ return item.err
+}
+
+func (item *kindItem) Path() *openapi.Path {
+ return item.path
+}
+
+func (item *kindItem) VisitPrimitive(schema *openapi.Primitive) {
+ item.err = errors.New("expected kind, but got primitive")
+}
+
+func (item *kindItem) VisitArray(schema *openapi.Array) {
+ item.err = errors.New("expected kind, but got slice")
+}
+
+func (item *kindItem) VisitMap(schema *openapi.Map) {
+ item.err = errors.New("expected kind, but got map")
+}
+
+func (item *kindItem) VisitReference(schema openapi.Reference) {
+ if !item.hasVisitKind {
+ schema.SubSchema().Accept(item)
+ }
+}
+
+func (item *kindItem) VisitKind(schema *openapi.Kind) {
+ subschema, ok := schema.Fields[item.key]
+ if !ok {
+ item.err = FieldNotFoundError{Path: schema.GetPath().String(), Field: item.key}
+ return
+ }
+
+ mergeKey, patchStrategies, err := parsePatchMetadata(subschema.GetExtensions())
+ if err != nil {
+ item.err = err
+ return
+ }
+ item.patchmeta = PatchMeta{
+ patchStrategies: patchStrategies,
+ patchMergeKey: mergeKey,
+ }
+ item.subschema = subschema
+}
+
+type sliceItem struct {
+ key string
+ path *openapi.Path
+ err error
+ patchmeta PatchMeta
+ subschema openapi.Schema
+ hasVisitKind bool
+}
+
+func NewSliceItem(key string, path *openapi.Path) *sliceItem {
+ return &sliceItem{
+ key: key,
+ path: path,
+ }
+}
+
+var _ LookupPatchItem = &sliceItem{}
+
+func (item *sliceItem) Error() error {
+ return item.err
+}
+
+func (item *sliceItem) Path() *openapi.Path {
+ return item.path
+}
+
+func (item *sliceItem) VisitPrimitive(schema *openapi.Primitive) {
+ item.err = errors.New("expected slice, but got primitive")
+}
+
+func (item *sliceItem) VisitArray(schema *openapi.Array) {
+ if !item.hasVisitKind {
+ item.err = errors.New("expected visit kind first, then visit array")
+ }
+ subschema := schema.SubType
+ item.subschema = subschema
+}
+
+func (item *sliceItem) VisitMap(schema *openapi.Map) {
+ item.err = errors.New("expected slice, but got map")
+}
+
+func (item *sliceItem) VisitReference(schema openapi.Reference) {
+ if !item.hasVisitKind {
+ schema.SubSchema().Accept(item)
+ } else {
+ item.subschema = schema.SubSchema()
+ }
+}
+
+func (item *sliceItem) VisitKind(schema *openapi.Kind) {
+ subschema, ok := schema.Fields[item.key]
+ if !ok {
+ item.err = FieldNotFoundError{Path: schema.GetPath().String(), Field: item.key}
+ return
+ }
+
+ mergeKey, patchStrategies, err := parsePatchMetadata(subschema.GetExtensions())
+ if err != nil {
+ item.err = err
+ return
+ }
+ item.patchmeta = PatchMeta{
+ patchStrategies: patchStrategies,
+ patchMergeKey: mergeKey,
+ }
+ item.hasVisitKind = true
+ subschema.Accept(item)
+}
+
+func parsePatchMetadata(extensions map[string]interface{}) (string, []string, error) {
+ ps, foundPS := extensions[patchStrategyOpenapiextensionKey]
+ var patchStrategies []string
+ var mergeKey, patchStrategy string
+ var ok bool
+ if foundPS {
+ patchStrategy, ok = ps.(string)
+ if ok {
+ patchStrategies = strings.Split(patchStrategy, ",")
+ } else {
+ return "", nil, mergepatch.ErrBadArgType(patchStrategy, ps)
+ }
+ }
+ mk, foundMK := extensions[patchMergeKeyOpenapiextensionKey]
+ if foundMK {
+ mergeKey, ok = mk.(string)
+ if !ok {
+ return "", nil, mergepatch.ErrBadArgType(mergeKey, mk)
+ }
+ }
+ return mergeKey, patchStrategies, nil
+}
diff --git a/vendor/k8s.io/apimachinery/pkg/util/validation/field/errors.go b/vendor/k8s.io/apimachinery/pkg/util/validation/field/errors.go
index 43c779a11..31705dee3 100644
--- a/vendor/k8s.io/apimachinery/pkg/util/validation/field/errors.go
+++ b/vendor/k8s.io/apimachinery/pkg/util/validation/field/errors.go
@@ -19,6 +19,7 @@ package field
import (
"fmt"
"reflect"
+ "strconv"
"strings"
utilerrors "k8s.io/apimachinery/pkg/util/errors"
@@ -175,7 +176,11 @@ func Invalid(field *Path, value interface{}, detail string) *Error {
func NotSupported(field *Path, value interface{}, validValues []string) *Error {
detail := ""
if validValues != nil && len(validValues) > 0 {
- detail = "supported values: " + strings.Join(validValues, ", ")
+ quotedValues := make([]string, len(validValues))
+ for i, v := range validValues {
+ quotedValues[i] = strconv.Quote(v)
+ }
+ detail = "supported values: " + strings.Join(quotedValues, ", ")
}
return &Error{ErrorTypeNotSupported, field.String(), value, detail}
}
diff --git a/vendor/k8s.io/apimachinery/pkg/util/validation/validation.go b/vendor/k8s.io/apimachinery/pkg/util/validation/validation.go
index b1fcc5708..7da6a17d9 100644
--- a/vendor/k8s.io/apimachinery/pkg/util/validation/validation.go
+++ b/vendor/k8s.io/apimachinery/pkg/util/validation/validation.go
@@ -22,6 +22,8 @@ import (
"net"
"regexp"
"strings"
+
+ "k8s.io/apimachinery/pkg/util/validation/field"
)
const qnameCharFmt string = "[A-Za-z0-9]"
@@ -67,6 +69,21 @@ func IsQualifiedName(value string) []string {
return errs
}
+// IsFullyQualifiedName checks if the name is fully qualified.
+func IsFullyQualifiedName(fldPath *field.Path, name string) field.ErrorList {
+ var allErrors field.ErrorList
+ if len(name) == 0 {
+ return append(allErrors, field.Required(fldPath, ""))
+ }
+ if errs := IsDNS1123Subdomain(name); len(errs) > 0 {
+ return append(allErrors, field.Invalid(fldPath, name, strings.Join(errs, ",")))
+ }
+ if len(strings.Split(name, ".")) < 3 {
+ return append(allErrors, field.Invalid(fldPath, name, "should be a domain with at least three segments separated by dots"))
+ }
+ return allErrors
+}
+
const labelValueFmt string = "(" + qualifiedNameFmt + ")?"
const labelValueErrMsg string = "a valid label must be an empty string or consist of alphanumeric characters, '-', '_' or '.', and must start and end with an alphanumeric character"
const LabelValueMaxLength int = 63
@@ -126,7 +143,7 @@ func IsDNS1123Subdomain(value string) []string {
}
const dns1035LabelFmt string = "[a-z]([-a-z0-9]*[a-z0-9])?"
-const dns1035LabelErrMsg string = "a DNS-1035 label must consist of lower case alphanumeric characters or '-', and must start and end with an alphanumeric character"
+const dns1035LabelErrMsg string = "a DNS-1035 label must consist of lower case alphanumeric characters or '-', start with an alphabetic character, and end with an alphanumeric character"
const DNS1035LabelMaxLength int = 63
var dns1035LabelRegexp = regexp.MustCompile("^" + dns1035LabelFmt + "$")
@@ -188,6 +205,14 @@ func IsValidPortNum(port int) []string {
return []string{InclusiveRangeError(1, 65535)}
}
+// IsInRange tests that the argument is in an inclusive range.
+func IsInRange(value int, min int, max int) []string {
+ if value >= min && value <= max {
+ return nil
+ }
+ return []string{InclusiveRangeError(min, max)}
+}
+
// Now in libcontainer UID/GID limits is 0 ~ 1<<31 - 1
// TODO: once we have a type for UID/GID we should make these that type.
const (
@@ -277,6 +302,22 @@ func IsHTTPHeaderName(value string) []string {
return nil
}
+const envVarNameFmt = "[-._a-zA-Z][-._a-zA-Z0-9]*"
+const envVarNameFmtErrMsg string = "a valid environment variable name must consist of alphabetic characters, digits, '_', '-', or '.', and must not start with a digit"
+
+var envVarNameRegexp = regexp.MustCompile("^" + envVarNameFmt + "$")
+
+// IsEnvVarName tests if a string is a valid environment variable name.
+func IsEnvVarName(value string) []string {
+ var errs []string
+ if !envVarNameRegexp.MatchString(value) {
+ errs = append(errs, RegexError(envVarNameFmtErrMsg, envVarNameFmt, "my.env-name", "MY_ENV.NAME", "MyEnvName1"))
+ }
+
+ errs = append(errs, hasChDirPrefix(value)...)
+ return errs
+}
+
const configMapKeyFmt = `[-._a-zA-Z0-9]+`
const configMapKeyErrMsg string = "a valid config key must consist of alphanumeric characters, '-', '_' or '.'"
@@ -291,13 +332,7 @@ func IsConfigMapKey(value string) []string {
if !configMapKeyRegexp.MatchString(value) {
errs = append(errs, RegexError(configMapKeyErrMsg, configMapKeyFmt, "key.name", "KEY_NAME", "key-name"))
}
- if value == "." {
- errs = append(errs, `must not be '.'`)
- } else if value == ".." {
- errs = append(errs, `must not be '..'`)
- } else if strings.HasPrefix(value, "..") {
- errs = append(errs, `must not start with '..'`)
- }
+ errs = append(errs, hasChDirPrefix(value)...)
return errs
}
@@ -341,3 +376,16 @@ func prefixEach(msgs []string, prefix string) []string {
func InclusiveRangeError(lo, hi int) string {
return fmt.Sprintf(`must be between %d and %d, inclusive`, lo, hi)
}
+
+func hasChDirPrefix(value string) []string {
+ var errs []string
+ switch {
+ case value == ".":
+ errs = append(errs, `must not be '.'`)
+ case value == "..":
+ errs = append(errs, `must not be '..'`)
+ case strings.HasPrefix(value, ".."):
+ errs = append(errs, `must not start with '..'`)
+ }
+ return errs
+}
diff --git a/vendor/k8s.io/apimachinery/pkg/util/wait/wait.go b/vendor/k8s.io/apimachinery/pkg/util/wait/wait.go
index badaa2159..0997de806 100644
--- a/vendor/k8s.io/apimachinery/pkg/util/wait/wait.go
+++ b/vendor/k8s.io/apimachinery/pkg/util/wait/wait.go
@@ -17,8 +17,10 @@ limitations under the License.
package wait
import (
+ "context"
"errors"
"math/rand"
+ "sync"
"time"
"k8s.io/apimachinery/pkg/util/runtime"
@@ -36,6 +38,40 @@ var ForeverTestTimeout = time.Second * 30
// NeverStop may be passed to Until to make it never stop.
var NeverStop <-chan struct{} = make(chan struct{})
+// Group allows to start a group of goroutines and wait for their completion.
+type Group struct {
+ wg sync.WaitGroup
+}
+
+func (g *Group) Wait() {
+ g.wg.Wait()
+}
+
+// StartWithChannel starts f in a new goroutine in the group.
+// stopCh is passed to f as an argument. f should stop when stopCh is available.
+func (g *Group) StartWithChannel(stopCh <-chan struct{}, f func(stopCh <-chan struct{})) {
+ g.Start(func() {
+ f(stopCh)
+ })
+}
+
+// StartWithContext starts f in a new goroutine in the group.
+// ctx is passed to f as an argument. f should stop when ctx.Done() is available.
+func (g *Group) StartWithContext(ctx context.Context, f func(context.Context)) {
+ g.Start(func() {
+ f(ctx)
+ })
+}
+
+// Start starts f in a new goroutine in the group.
+func (g *Group) Start(f func()) {
+ g.wg.Add(1)
+ go func() {
+ defer g.wg.Done()
+ f()
+ }()
+}
+
// Forever calls f every period for ever.
//
// Forever is syntactic sugar on top of Until.
diff --git a/vendor/k8s.io/apimachinery/pkg/util/yaml/decoder.go b/vendor/k8s.io/apimachinery/pkg/util/yaml/decoder.go
index 6ebfaea70..3cd85515d 100644
--- a/vendor/k8s.io/apimachinery/pkg/util/yaml/decoder.go
+++ b/vendor/k8s.io/apimachinery/pkg/util/yaml/decoder.go
@@ -122,12 +122,12 @@ func (d *YAMLDecoder) Read(data []byte) (n int, err error) {
if left <= len(data) {
copy(data, d.remaining)
d.remaining = nil
- return len(d.remaining), nil
+ return left, nil
}
// caller will need to reread
- copy(data, d.remaining[:left])
- d.remaining = d.remaining[left:]
+ copy(data, d.remaining[:len(data)])
+ d.remaining = d.remaining[len(data):]
return len(data), io.ErrShortBuffer
}