summaryrefslogtreecommitdiff
path: root/vendor/k8s.io/apimachinery/pkg/api
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/k8s.io/apimachinery/pkg/api')
-rw-r--r--vendor/k8s.io/apimachinery/pkg/api/equality/semantic.go46
-rw-r--r--vendor/k8s.io/apimachinery/pkg/api/errors/doc.go18
-rw-r--r--vendor/k8s.io/apimachinery/pkg/api/errors/errors.go478
-rw-r--r--vendor/k8s.io/apimachinery/pkg/api/meta/default.go51
-rw-r--r--vendor/k8s.io/apimachinery/pkg/api/meta/doc.go19
-rw-r--r--vendor/k8s.io/apimachinery/pkg/api/meta/errors.go105
-rw-r--r--vendor/k8s.io/apimachinery/pkg/api/meta/firsthit_restmapper.go97
-rw-r--r--vendor/k8s.io/apimachinery/pkg/api/meta/help.go202
-rw-r--r--vendor/k8s.io/apimachinery/pkg/api/meta/interfaces.go146
-rw-r--r--vendor/k8s.io/apimachinery/pkg/api/meta/meta.go610
-rw-r--r--vendor/k8s.io/apimachinery/pkg/api/meta/multirestmapper.go210
-rw-r--r--vendor/k8s.io/apimachinery/pkg/api/meta/priority.go222
-rw-r--r--vendor/k8s.io/apimachinery/pkg/api/meta/restmapper.go545
-rw-r--r--vendor/k8s.io/apimachinery/pkg/api/meta/unstructured.go31
-rw-r--r--vendor/k8s.io/apimachinery/pkg/api/resource/amount.go299
-rw-r--r--vendor/k8s.io/apimachinery/pkg/api/resource/generated.pb.go77
-rw-r--r--vendor/k8s.io/apimachinery/pkg/api/resource/generated.proto94
-rw-r--r--vendor/k8s.io/apimachinery/pkg/api/resource/math.go314
-rw-r--r--vendor/k8s.io/apimachinery/pkg/api/resource/quantity.go792
-rw-r--r--vendor/k8s.io/apimachinery/pkg/api/resource/quantity_proto.go284
-rw-r--r--vendor/k8s.io/apimachinery/pkg/api/resource/scale_int.go95
-rw-r--r--vendor/k8s.io/apimachinery/pkg/api/resource/suffix.go198
-rw-r--r--vendor/k8s.io/apimachinery/pkg/api/validation/doc.go18
-rw-r--r--vendor/k8s.io/apimachinery/pkg/api/validation/generic.go85
-rw-r--r--vendor/k8s.io/apimachinery/pkg/api/validation/objectmeta.go348
25 files changed, 5384 insertions, 0 deletions
diff --git a/vendor/k8s.io/apimachinery/pkg/api/equality/semantic.go b/vendor/k8s.io/apimachinery/pkg/api/equality/semantic.go
new file mode 100644
index 000000000..3ebd8c719
--- /dev/null
+++ b/vendor/k8s.io/apimachinery/pkg/api/equality/semantic.go
@@ -0,0 +1,46 @@
+/*
+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 equality
+
+import (
+ "k8s.io/apimachinery/pkg/api/resource"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/conversion"
+ "k8s.io/apimachinery/pkg/fields"
+ "k8s.io/apimachinery/pkg/labels"
+)
+
+// Semantic can do semantic deep equality checks for api objects.
+// Example: apiequality.Semantic.DeepEqual(aPod, aPodWithNonNilButEmptyMaps) == true
+var Semantic = conversion.EqualitiesOrDie(
+ func(a, b resource.Quantity) bool {
+ // Ignore formatting, only care that numeric value stayed the same.
+ // TODO: if we decide it's important, it should be safe to start comparing the format.
+ //
+ // Uninitialized quantities are equivalent to 0 quantities.
+ return a.Cmp(b) == 0
+ },
+ func(a, b metav1.Time) bool {
+ return a.UTC() == b.UTC()
+ },
+ func(a, b labels.Selector) bool {
+ return a.String() == b.String()
+ },
+ func(a, b fields.Selector) bool {
+ return a.String() == b.String()
+ },
+)
diff --git a/vendor/k8s.io/apimachinery/pkg/api/errors/doc.go b/vendor/k8s.io/apimachinery/pkg/api/errors/doc.go
new file mode 100644
index 000000000..167baf680
--- /dev/null
+++ b/vendor/k8s.io/apimachinery/pkg/api/errors/doc.go
@@ -0,0 +1,18 @@
+/*
+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 errors provides detailed error types for api field validation.
+package errors // import "k8s.io/apimachinery/pkg/api/errors"
diff --git a/vendor/k8s.io/apimachinery/pkg/api/errors/errors.go b/vendor/k8s.io/apimachinery/pkg/api/errors/errors.go
new file mode 100644
index 000000000..560c889b9
--- /dev/null
+++ b/vendor/k8s.io/apimachinery/pkg/api/errors/errors.go
@@ -0,0 +1,478 @@
+/*
+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 errors
+
+import (
+ "encoding/json"
+ "fmt"
+ "net/http"
+ "strings"
+
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/apimachinery/pkg/util/validation/field"
+)
+
+// HTTP Status codes not in the golang http package.
+const (
+ StatusUnprocessableEntity = 422
+ StatusTooManyRequests = 429
+ // StatusServerTimeout is an indication that a transient server error has
+ // occurred and the client *should* retry, with an optional Retry-After
+ // header to specify the back off window.
+ StatusServerTimeout = 504
+)
+
+// StatusError is an error intended for consumption by a REST API server; it can also be
+// reconstructed by clients from a REST response. Public to allow easy type switches.
+type StatusError struct {
+ ErrStatus metav1.Status
+}
+
+// APIStatus is exposed by errors that can be converted to an api.Status object
+// for finer grained details.
+type APIStatus interface {
+ Status() metav1.Status
+}
+
+var _ error = &StatusError{}
+
+// Error implements the Error interface.
+func (e *StatusError) Error() string {
+ return e.ErrStatus.Message
+}
+
+// Status allows access to e's status without having to know the detailed workings
+// of StatusError.
+func (e *StatusError) Status() metav1.Status {
+ return e.ErrStatus
+}
+
+// DebugError reports extended info about the error to debug output.
+func (e *StatusError) DebugError() (string, []interface{}) {
+ if out, err := json.MarshalIndent(e.ErrStatus, "", " "); err == nil {
+ return "server response object: %s", []interface{}{string(out)}
+ }
+ return "server response object: %#v", []interface{}{e.ErrStatus}
+}
+
+// UnexpectedObjectError can be returned by FromObject if it's passed a non-status object.
+type UnexpectedObjectError struct {
+ Object runtime.Object
+}
+
+// Error returns an error message describing 'u'.
+func (u *UnexpectedObjectError) Error() string {
+ return fmt.Sprintf("unexpected object: %v", u.Object)
+}
+
+// FromObject generates an StatusError from an metav1.Status, if that is the type of obj; otherwise,
+// returns an UnexpecteObjectError.
+func FromObject(obj runtime.Object) error {
+ switch t := obj.(type) {
+ case *metav1.Status:
+ return &StatusError{*t}
+ }
+ return &UnexpectedObjectError{obj}
+}
+
+// NewNotFound returns a new error which indicates that the resource of the kind and the name was not found.
+func NewNotFound(qualifiedResource schema.GroupResource, name string) *StatusError {
+ return &StatusError{metav1.Status{
+ Status: metav1.StatusFailure,
+ Code: http.StatusNotFound,
+ Reason: metav1.StatusReasonNotFound,
+ Details: &metav1.StatusDetails{
+ Group: qualifiedResource.Group,
+ Kind: qualifiedResource.Resource,
+ Name: name,
+ },
+ Message: fmt.Sprintf("%s %q not found", qualifiedResource.String(), name),
+ }}
+}
+
+// NewAlreadyExists returns an error indicating the item requested exists by that identifier.
+func NewAlreadyExists(qualifiedResource schema.GroupResource, name string) *StatusError {
+ return &StatusError{metav1.Status{
+ Status: metav1.StatusFailure,
+ Code: http.StatusConflict,
+ Reason: metav1.StatusReasonAlreadyExists,
+ Details: &metav1.StatusDetails{
+ Group: qualifiedResource.Group,
+ Kind: qualifiedResource.Resource,
+ Name: name,
+ },
+ Message: fmt.Sprintf("%s %q already exists", qualifiedResource.String(), name),
+ }}
+}
+
+// NewUnauthorized returns an error indicating the client is not authorized to perform the requested
+// action.
+func NewUnauthorized(reason string) *StatusError {
+ message := reason
+ if len(message) == 0 {
+ message = "not authorized"
+ }
+ return &StatusError{metav1.Status{
+ Status: metav1.StatusFailure,
+ Code: http.StatusUnauthorized,
+ Reason: metav1.StatusReasonUnauthorized,
+ Message: message,
+ }}
+}
+
+// NewForbidden returns an error indicating the requested action was forbidden
+func NewForbidden(qualifiedResource schema.GroupResource, name string, err error) *StatusError {
+ return &StatusError{metav1.Status{
+ Status: metav1.StatusFailure,
+ Code: http.StatusForbidden,
+ Reason: metav1.StatusReasonForbidden,
+ Details: &metav1.StatusDetails{
+ Group: qualifiedResource.Group,
+ Kind: qualifiedResource.Resource,
+ Name: name,
+ },
+ Message: fmt.Sprintf("%s %q is forbidden: %v", qualifiedResource.String(), name, err),
+ }}
+}
+
+// NewConflict returns an error indicating the item can't be updated as provided.
+func NewConflict(qualifiedResource schema.GroupResource, name string, err error) *StatusError {
+ return &StatusError{metav1.Status{
+ Status: metav1.StatusFailure,
+ Code: http.StatusConflict,
+ Reason: metav1.StatusReasonConflict,
+ Details: &metav1.StatusDetails{
+ Group: qualifiedResource.Group,
+ Kind: qualifiedResource.Resource,
+ Name: name,
+ },
+ Message: fmt.Sprintf("Operation cannot be fulfilled on %s %q: %v", qualifiedResource.String(), name, err),
+ }}
+}
+
+// NewGone returns an error indicating the item no longer available at the server and no forwarding address is known.
+func NewGone(message string) *StatusError {
+ return &StatusError{metav1.Status{
+ Status: metav1.StatusFailure,
+ Code: http.StatusGone,
+ Reason: metav1.StatusReasonGone,
+ Message: message,
+ }}
+}
+
+// NewInvalid returns an error indicating the item is invalid and cannot be processed.
+func NewInvalid(qualifiedKind schema.GroupKind, name string, errs field.ErrorList) *StatusError {
+ causes := make([]metav1.StatusCause, 0, len(errs))
+ for i := range errs {
+ err := errs[i]
+ causes = append(causes, metav1.StatusCause{
+ Type: metav1.CauseType(err.Type),
+ Message: err.ErrorBody(),
+ Field: err.Field,
+ })
+ }
+ return &StatusError{metav1.Status{
+ Status: metav1.StatusFailure,
+ Code: StatusUnprocessableEntity, // RFC 4918: StatusUnprocessableEntity
+ Reason: metav1.StatusReasonInvalid,
+ Details: &metav1.StatusDetails{
+ Group: qualifiedKind.Group,
+ Kind: qualifiedKind.Kind,
+ Name: name,
+ Causes: causes,
+ },
+ Message: fmt.Sprintf("%s %q is invalid: %v", qualifiedKind.String(), name, errs.ToAggregate()),
+ }}
+}
+
+// NewBadRequest creates an error that indicates that the request is invalid and can not be processed.
+func NewBadRequest(reason string) *StatusError {
+ return &StatusError{metav1.Status{
+ Status: metav1.StatusFailure,
+ Code: http.StatusBadRequest,
+ Reason: metav1.StatusReasonBadRequest,
+ Message: reason,
+ }}
+}
+
+// NewServiceUnavailable creates an error that indicates that the requested service is unavailable.
+func NewServiceUnavailable(reason string) *StatusError {
+ return &StatusError{metav1.Status{
+ Status: metav1.StatusFailure,
+ Code: http.StatusServiceUnavailable,
+ Reason: metav1.StatusReasonServiceUnavailable,
+ Message: reason,
+ }}
+}
+
+// NewMethodNotSupported returns an error indicating the requested action is not supported on this kind.
+func NewMethodNotSupported(qualifiedResource schema.GroupResource, action string) *StatusError {
+ return &StatusError{metav1.Status{
+ Status: metav1.StatusFailure,
+ Code: http.StatusMethodNotAllowed,
+ Reason: metav1.StatusReasonMethodNotAllowed,
+ Details: &metav1.StatusDetails{
+ Group: qualifiedResource.Group,
+ Kind: qualifiedResource.Resource,
+ },
+ Message: fmt.Sprintf("%s is not supported on resources of kind %q", action, qualifiedResource.String()),
+ }}
+}
+
+// NewServerTimeout returns an error indicating the requested action could not be completed due to a
+// transient error, and the client should try again.
+func NewServerTimeout(qualifiedResource schema.GroupResource, operation string, retryAfterSeconds int) *StatusError {
+ return &StatusError{metav1.Status{
+ Status: metav1.StatusFailure,
+ Code: http.StatusInternalServerError,
+ Reason: metav1.StatusReasonServerTimeout,
+ Details: &metav1.StatusDetails{
+ Group: qualifiedResource.Group,
+ Kind: qualifiedResource.Resource,
+ Name: operation,
+ RetryAfterSeconds: int32(retryAfterSeconds),
+ },
+ Message: fmt.Sprintf("The %s operation against %s could not be completed at this time, please try again.", operation, qualifiedResource.String()),
+ }}
+}
+
+// NewServerTimeoutForKind should not exist. Server timeouts happen when accessing resources, the Kind is just what we
+// happened to be looking at when the request failed. This delegates to keep code sane, but we should work towards removing this.
+func NewServerTimeoutForKind(qualifiedKind schema.GroupKind, operation string, retryAfterSeconds int) *StatusError {
+ return NewServerTimeout(schema.GroupResource{Group: qualifiedKind.Group, Resource: qualifiedKind.Kind}, operation, retryAfterSeconds)
+}
+
+// NewInternalError returns an error indicating the item is invalid and cannot be processed.
+func NewInternalError(err error) *StatusError {
+ return &StatusError{metav1.Status{
+ Status: metav1.StatusFailure,
+ Code: http.StatusInternalServerError,
+ Reason: metav1.StatusReasonInternalError,
+ Details: &metav1.StatusDetails{
+ Causes: []metav1.StatusCause{{Message: err.Error()}},
+ },
+ Message: fmt.Sprintf("Internal error occurred: %v", err),
+ }}
+}
+
+// NewTimeoutError returns an error indicating that a timeout occurred before the request
+// could be completed. Clients may retry, but the operation may still complete.
+func NewTimeoutError(message string, retryAfterSeconds int) *StatusError {
+ return &StatusError{metav1.Status{
+ Status: metav1.StatusFailure,
+ Code: StatusServerTimeout,
+ Reason: metav1.StatusReasonTimeout,
+ Message: fmt.Sprintf("Timeout: %s", message),
+ Details: &metav1.StatusDetails{
+ RetryAfterSeconds: int32(retryAfterSeconds),
+ },
+ }}
+}
+
+// NewGenericServerResponse returns a new error for server responses that are not in a recognizable form.
+func NewGenericServerResponse(code int, verb string, qualifiedResource schema.GroupResource, name, serverMessage string, retryAfterSeconds int, isUnexpectedResponse bool) *StatusError {
+ reason := metav1.StatusReasonUnknown
+ message := fmt.Sprintf("the server responded with the status code %d but did not return more information", code)
+ switch code {
+ case http.StatusConflict:
+ if verb == "POST" {
+ reason = metav1.StatusReasonAlreadyExists
+ } else {
+ reason = metav1.StatusReasonConflict
+ }
+ message = "the server reported a conflict"
+ case http.StatusNotFound:
+ reason = metav1.StatusReasonNotFound
+ message = "the server could not find the requested resource"
+ case http.StatusBadRequest:
+ reason = metav1.StatusReasonBadRequest
+ message = "the server rejected our request for an unknown reason"
+ case http.StatusUnauthorized:
+ reason = metav1.StatusReasonUnauthorized
+ message = "the server has asked for the client to provide credentials"
+ case http.StatusForbidden:
+ reason = metav1.StatusReasonForbidden
+ // the server message has details about who is trying to perform what action. Keep its message.
+ message = serverMessage
+ case http.StatusMethodNotAllowed:
+ reason = metav1.StatusReasonMethodNotAllowed
+ message = "the server does not allow this method on the requested resource"
+ case StatusUnprocessableEntity:
+ reason = metav1.StatusReasonInvalid
+ message = "the server rejected our request due to an error in our request"
+ case StatusServerTimeout:
+ reason = metav1.StatusReasonServerTimeout
+ message = "the server cannot complete the requested operation at this time, try again later"
+ case StatusTooManyRequests:
+ reason = metav1.StatusReasonTimeout
+ message = "the server has received too many requests and has asked us to try again later"
+ default:
+ if code >= 500 {
+ reason = metav1.StatusReasonInternalError
+ message = fmt.Sprintf("an error on the server (%q) has prevented the request from succeeding", serverMessage)
+ }
+ }
+ switch {
+ case !qualifiedResource.Empty() && len(name) > 0:
+ message = fmt.Sprintf("%s (%s %s %s)", message, strings.ToLower(verb), qualifiedResource.String(), name)
+ case !qualifiedResource.Empty():
+ message = fmt.Sprintf("%s (%s %s)", message, strings.ToLower(verb), qualifiedResource.String())
+ }
+ var causes []metav1.StatusCause
+ if isUnexpectedResponse {
+ causes = []metav1.StatusCause{
+ {
+ Type: metav1.CauseTypeUnexpectedServerResponse,
+ Message: serverMessage,
+ },
+ }
+ } else {
+ causes = nil
+ }
+ return &StatusError{metav1.Status{
+ Status: metav1.StatusFailure,
+ Code: int32(code),
+ Reason: reason,
+ Details: &metav1.StatusDetails{
+ Group: qualifiedResource.Group,
+ Kind: qualifiedResource.Resource,
+ Name: name,
+
+ Causes: causes,
+ RetryAfterSeconds: int32(retryAfterSeconds),
+ },
+ Message: message,
+ }}
+}
+
+// IsNotFound returns true if the specified error was created by NewNotFound.
+func IsNotFound(err error) bool {
+ return reasonForError(err) == metav1.StatusReasonNotFound
+}
+
+// IsAlreadyExists determines if the err is an error which indicates that a specified resource already exists.
+func IsAlreadyExists(err error) bool {
+ return reasonForError(err) == metav1.StatusReasonAlreadyExists
+}
+
+// IsConflict determines if the err is an error which indicates the provided update conflicts.
+func IsConflict(err error) bool {
+ return reasonForError(err) == metav1.StatusReasonConflict
+}
+
+// IsInvalid determines if the err is an error which indicates the provided resource is not valid.
+func IsInvalid(err error) bool {
+ return reasonForError(err) == metav1.StatusReasonInvalid
+}
+
+// IsMethodNotSupported determines if the err is an error which indicates the provided action could not
+// be performed because it is not supported by the server.
+func IsMethodNotSupported(err error) bool {
+ return reasonForError(err) == metav1.StatusReasonMethodNotAllowed
+}
+
+// IsBadRequest determines if err is an error which indicates that the request is invalid.
+func IsBadRequest(err error) bool {
+ return reasonForError(err) == metav1.StatusReasonBadRequest
+}
+
+// IsUnauthorized determines if err is an error which indicates that the request is unauthorized and
+// requires authentication by the user.
+func IsUnauthorized(err error) bool {
+ return reasonForError(err) == metav1.StatusReasonUnauthorized
+}
+
+// IsForbidden determines if err is an error which indicates that the request is forbidden and cannot
+// be completed as requested.
+func IsForbidden(err error) bool {
+ return reasonForError(err) == metav1.StatusReasonForbidden
+}
+
+// IsTimeout determines if err is an error which indicates that request times out due to long
+// processing.
+func IsTimeout(err error) bool {
+ return reasonForError(err) == metav1.StatusReasonTimeout
+}
+
+// IsServerTimeout determines if err is an error which indicates that the request needs to be retried
+// by the client.
+func IsServerTimeout(err error) bool {
+ return reasonForError(err) == metav1.StatusReasonServerTimeout
+}
+
+// IsInternalError determines if err is an error which indicates an internal server error.
+func IsInternalError(err error) bool {
+ return reasonForError(err) == metav1.StatusReasonInternalError
+}
+
+// IsTooManyRequests determines if err is an error which indicates that there are too many requests
+// that the server cannot handle.
+// TODO: update IsTooManyRequests() when the TooManyRequests(429) error returned from the API server has a non-empty Reason field
+func IsTooManyRequests(err error) bool {
+ switch t := err.(type) {
+ case APIStatus:
+ return t.Status().Code == StatusTooManyRequests
+ }
+ return false
+}
+
+// IsUnexpectedServerError returns true if the server response was not in the expected API format,
+// and may be the result of another HTTP actor.
+func IsUnexpectedServerError(err error) bool {
+ switch t := err.(type) {
+ case APIStatus:
+ if d := t.Status().Details; d != nil {
+ for _, cause := range d.Causes {
+ if cause.Type == metav1.CauseTypeUnexpectedServerResponse {
+ return true
+ }
+ }
+ }
+ }
+ return false
+}
+
+// IsUnexpectedObjectError determines if err is due to an unexpected object from the master.
+func IsUnexpectedObjectError(err error) bool {
+ _, ok := err.(*UnexpectedObjectError)
+ return err != nil && ok
+}
+
+// SuggestsClientDelay returns true if this error suggests a client delay as well as the
+// suggested seconds to wait, or false if the error does not imply a wait.
+func SuggestsClientDelay(err error) (int, bool) {
+ switch t := err.(type) {
+ case APIStatus:
+ if t.Status().Details != nil {
+ switch t.Status().Reason {
+ case metav1.StatusReasonServerTimeout, metav1.StatusReasonTimeout:
+ return int(t.Status().Details.RetryAfterSeconds), true
+ }
+ }
+ }
+ return 0, false
+}
+
+func reasonForError(err error) metav1.StatusReason {
+ switch t := err.(type) {
+ case APIStatus:
+ return t.Status().Reason
+ }
+ return metav1.StatusReasonUnknown
+}
diff --git a/vendor/k8s.io/apimachinery/pkg/api/meta/default.go b/vendor/k8s.io/apimachinery/pkg/api/meta/default.go
new file mode 100644
index 000000000..5ea906a2a
--- /dev/null
+++ b/vendor/k8s.io/apimachinery/pkg/api/meta/default.go
@@ -0,0 +1,51 @@
+/*
+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 meta
+
+import (
+ "strings"
+
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/apimachinery/pkg/util/sets"
+)
+
+// NewDefaultRESTMapperFromScheme instantiates a DefaultRESTMapper based on types registered in the given scheme.
+func NewDefaultRESTMapperFromScheme(defaultGroupVersions []schema.GroupVersion, interfacesFunc VersionInterfacesFunc,
+ importPathPrefix string, ignoredKinds, rootScoped sets.String, scheme *runtime.Scheme) *DefaultRESTMapper {
+
+ mapper := NewDefaultRESTMapper(defaultGroupVersions, interfacesFunc)
+ // enumerate all supported versions, get the kinds, and register with the mapper how to address
+ // our resources.
+ for _, gv := range defaultGroupVersions {
+ for kind, oType := range scheme.KnownTypes(gv) {
+ gvk := gv.WithKind(kind)
+ // TODO: Remove import path check.
+ // We check the import path because we currently stuff both "api" and "extensions" objects
+ // into the same group within Scheme since Scheme has no notion of groups yet.
+ if !strings.Contains(oType.PkgPath(), importPathPrefix) || ignoredKinds.Has(kind) {
+ continue
+ }
+ scope := RESTScopeNamespace
+ if rootScoped.Has(kind) {
+ scope = RESTScopeRoot
+ }
+ mapper.Add(gvk, scope)
+ }
+ }
+ return mapper
+}
diff --git a/vendor/k8s.io/apimachinery/pkg/api/meta/doc.go b/vendor/k8s.io/apimachinery/pkg/api/meta/doc.go
new file mode 100644
index 000000000..b6d42acf8
--- /dev/null
+++ b/vendor/k8s.io/apimachinery/pkg/api/meta/doc.go
@@ -0,0 +1,19 @@
+/*
+Copyright 2014 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+// Package meta provides functions for retrieving API metadata from objects
+// belonging to the Kubernetes API
+package meta // import "k8s.io/apimachinery/pkg/api/meta"
diff --git a/vendor/k8s.io/apimachinery/pkg/api/meta/errors.go b/vendor/k8s.io/apimachinery/pkg/api/meta/errors.go
new file mode 100644
index 000000000..1503bd6d8
--- /dev/null
+++ b/vendor/k8s.io/apimachinery/pkg/api/meta/errors.go
@@ -0,0 +1,105 @@
+/*
+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 meta
+
+import (
+ "fmt"
+
+ "k8s.io/apimachinery/pkg/runtime/schema"
+)
+
+// AmbiguousResourceError is returned if the RESTMapper finds multiple matches for a resource
+type AmbiguousResourceError struct {
+ PartialResource schema.GroupVersionResource
+
+ MatchingResources []schema.GroupVersionResource
+ MatchingKinds []schema.GroupVersionKind
+}
+
+func (e *AmbiguousResourceError) Error() string {
+ switch {
+ case len(e.MatchingKinds) > 0 && len(e.MatchingResources) > 0:
+ return fmt.Sprintf("%v matches multiple resources %v and kinds %v", e.PartialResource, e.MatchingResources, e.MatchingKinds)
+ case len(e.MatchingKinds) > 0:
+ return fmt.Sprintf("%v matches multiple kinds %v", e.PartialResource, e.MatchingKinds)
+ case len(e.MatchingResources) > 0:
+ return fmt.Sprintf("%v matches multiple resources %v", e.PartialResource, e.MatchingResources)
+ }
+ return fmt.Sprintf("%v matches multiple resources or kinds", e.PartialResource)
+}
+
+// AmbiguousKindError is returned if the RESTMapper finds multiple matches for a kind
+type AmbiguousKindError struct {
+ PartialKind schema.GroupVersionKind
+
+ MatchingResources []schema.GroupVersionResource
+ MatchingKinds []schema.GroupVersionKind
+}
+
+func (e *AmbiguousKindError) Error() string {
+ switch {
+ case len(e.MatchingKinds) > 0 && len(e.MatchingResources) > 0:
+ return fmt.Sprintf("%v matches multiple resources %v and kinds %v", e.PartialKind, e.MatchingResources, e.MatchingKinds)
+ case len(e.MatchingKinds) > 0:
+ return fmt.Sprintf("%v matches multiple kinds %v", e.PartialKind, e.MatchingKinds)
+ case len(e.MatchingResources) > 0:
+ return fmt.Sprintf("%v matches multiple resources %v", e.PartialKind, e.MatchingResources)
+ }
+ return fmt.Sprintf("%v matches multiple resources or kinds", e.PartialKind)
+}
+
+func IsAmbiguousError(err error) bool {
+ if err == nil {
+ return false
+ }
+ switch err.(type) {
+ case *AmbiguousResourceError, *AmbiguousKindError:
+ return true
+ default:
+ return false
+ }
+}
+
+// NoResourceMatchError is returned if the RESTMapper can't find any match for a resource
+type NoResourceMatchError struct {
+ PartialResource schema.GroupVersionResource
+}
+
+func (e *NoResourceMatchError) Error() string {
+ return fmt.Sprintf("no matches for %v", e.PartialResource)
+}
+
+// NoKindMatchError is returned if the RESTMapper can't find any match for a kind
+type NoKindMatchError struct {
+ PartialKind schema.GroupVersionKind
+}
+
+func (e *NoKindMatchError) Error() string {
+ return fmt.Sprintf("no matches for %v", e.PartialKind)
+}
+
+func IsNoMatchError(err error) bool {
+ if err == nil {
+ return false
+ }
+ switch err.(type) {
+ case *NoResourceMatchError, *NoKindMatchError:
+ return true
+ default:
+ return false
+ }
+}
diff --git a/vendor/k8s.io/apimachinery/pkg/api/meta/firsthit_restmapper.go b/vendor/k8s.io/apimachinery/pkg/api/meta/firsthit_restmapper.go
new file mode 100644
index 000000000..fd2210022
--- /dev/null
+++ b/vendor/k8s.io/apimachinery/pkg/api/meta/firsthit_restmapper.go
@@ -0,0 +1,97 @@
+/*
+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 meta
+
+import (
+ "fmt"
+
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ utilerrors "k8s.io/apimachinery/pkg/util/errors"
+)
+
+// FirstHitRESTMapper is a wrapper for multiple RESTMappers which returns the
+// first successful result for the singular requests
+type FirstHitRESTMapper struct {
+ MultiRESTMapper
+}
+
+func (m FirstHitRESTMapper) String() string {
+ return fmt.Sprintf("FirstHitRESTMapper{\n\t%v\n}", m.MultiRESTMapper)
+}
+
+func (m FirstHitRESTMapper) ResourceFor(resource schema.GroupVersionResource) (schema.GroupVersionResource, error) {
+ errors := []error{}
+ for _, t := range m.MultiRESTMapper {
+ ret, err := t.ResourceFor(resource)
+ if err == nil {
+ return ret, nil
+ }
+ errors = append(errors, err)
+ }
+
+ return schema.GroupVersionResource{}, collapseAggregateErrors(errors)
+}
+
+func (m FirstHitRESTMapper) KindFor(resource schema.GroupVersionResource) (schema.GroupVersionKind, error) {
+ errors := []error{}
+ for _, t := range m.MultiRESTMapper {
+ ret, err := t.KindFor(resource)
+ if err == nil {
+ return ret, nil
+ }
+ errors = append(errors, err)
+ }
+
+ return schema.GroupVersionKind{}, collapseAggregateErrors(errors)
+}
+
+// RESTMapping provides the REST mapping for the resource based on the
+// kind and version. This implementation supports multiple REST schemas and
+// return the first match.
+func (m FirstHitRESTMapper) RESTMapping(gk schema.GroupKind, versions ...string) (*RESTMapping, error) {
+ errors := []error{}
+ for _, t := range m.MultiRESTMapper {
+ ret, err := t.RESTMapping(gk, versions...)
+ if err == nil {
+ return ret, nil
+ }
+ errors = append(errors, err)
+ }
+
+ return nil, collapseAggregateErrors(errors)
+}
+
+// collapseAggregateErrors returns the minimal errors. it handles empty as nil, handles one item in a list
+// by returning the item, and collapses all NoMatchErrors to a single one (since they should all be the same)
+func collapseAggregateErrors(errors []error) error {
+ if len(errors) == 0 {
+ return nil
+ }
+ if len(errors) == 1 {
+ return errors[0]
+ }
+
+ allNoMatchErrors := true
+ for _, err := range errors {
+ allNoMatchErrors = allNoMatchErrors && IsNoMatchError(err)
+ }
+ if allNoMatchErrors {
+ return errors[0]
+ }
+
+ return utilerrors.NewAggregate(errors)
+}
diff --git a/vendor/k8s.io/apimachinery/pkg/api/meta/help.go b/vendor/k8s.io/apimachinery/pkg/api/meta/help.go
new file mode 100644
index 000000000..9e0fb152a
--- /dev/null
+++ b/vendor/k8s.io/apimachinery/pkg/api/meta/help.go
@@ -0,0 +1,202 @@
+/*
+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 meta
+
+import (
+ "fmt"
+ "reflect"
+
+ "k8s.io/apimachinery/pkg/conversion"
+ "k8s.io/apimachinery/pkg/runtime"
+)
+
+// IsListType returns true if the provided Object has a slice called Items
+func IsListType(obj runtime.Object) bool {
+ // if we're a runtime.Unstructured, check whether this is a list.
+ // TODO: refactor GetItemsPtr to use an interface that returns []runtime.Object
+ if unstructured, ok := obj.(runtime.Unstructured); ok {
+ return unstructured.IsList()
+ }
+
+ _, err := GetItemsPtr(obj)
+ return err == nil
+}
+
+// GetItemsPtr returns a pointer to the list object's Items member.
+// If 'list' doesn't have an Items member, it's not really a list type
+// and an error will be returned.
+// This function will either return a pointer to a slice, or an error, but not both.
+func GetItemsPtr(list runtime.Object) (interface{}, error) {
+ v, err := conversion.EnforcePtr(list)
+ if err != nil {
+ return nil, err
+ }
+
+ items := v.FieldByName("Items")
+ if !items.IsValid() {
+ return nil, fmt.Errorf("no Items field in %#v", list)
+ }
+ switch items.Kind() {
+ case reflect.Interface, reflect.Ptr:
+ target := reflect.TypeOf(items.Interface()).Elem()
+ if target.Kind() != reflect.Slice {
+ return nil, fmt.Errorf("items: Expected slice, got %s", target.Kind())
+ }
+ return items.Interface(), nil
+ case reflect.Slice:
+ return items.Addr().Interface(), nil
+ default:
+ return nil, fmt.Errorf("items: Expected slice, got %s", items.Kind())
+ }
+}
+
+// EachListItem invokes fn on each runtime.Object in the list. Any error immediately terminates
+// the loop.
+func EachListItem(obj runtime.Object, fn func(runtime.Object) error) error {
+ // TODO: Change to an interface call?
+ itemsPtr, err := GetItemsPtr(obj)
+ if err != nil {
+ return err
+ }
+ items, err := conversion.EnforcePtr(itemsPtr)
+ if err != nil {
+ return err
+ }
+ len := items.Len()
+ if len == 0 {
+ return nil
+ }
+ takeAddr := false
+ if elemType := items.Type().Elem(); elemType.Kind() != reflect.Ptr && elemType.Kind() != reflect.Interface {
+ if !items.Index(0).CanAddr() {
+ return fmt.Errorf("unable to take address of items in %T for EachListItem", obj)
+ }
+ takeAddr = true
+ }
+
+ for i := 0; i < len; i++ {
+ raw := items.Index(i)
+ if takeAddr {
+ raw = raw.Addr()
+ }
+ switch item := raw.Interface().(type) {
+ case *runtime.RawExtension:
+ if err := fn(item.Object); err != nil {
+ return err
+ }
+ case runtime.Object:
+ if err := fn(item); err != nil {
+ return err
+ }
+ default:
+ obj, ok := item.(runtime.Object)
+ if !ok {
+ return fmt.Errorf("%v: item[%v]: Expected object, got %#v(%s)", obj, i, raw.Interface(), raw.Kind())
+ }
+ if err := fn(obj); err != nil {
+ return err
+ }
+ }
+ }
+ return nil
+}
+
+// ExtractList returns obj's Items element as an array of runtime.Objects.
+// Returns an error if obj is not a List type (does not have an Items member).
+func ExtractList(obj runtime.Object) ([]runtime.Object, error) {
+ itemsPtr, err := GetItemsPtr(obj)
+ if err != nil {
+ return nil, err
+ }
+ items, err := conversion.EnforcePtr(itemsPtr)
+ if err != nil {
+ return nil, err
+ }
+ list := make([]runtime.Object, items.Len())
+ for i := range list {
+ raw := items.Index(i)
+ switch item := raw.Interface().(type) {
+ case runtime.RawExtension:
+ switch {
+ case item.Object != nil:
+ list[i] = item.Object
+ case item.Raw != nil:
+ // TODO: Set ContentEncoding and ContentType correctly.
+ list[i] = &runtime.Unknown{Raw: item.Raw}
+ default:
+ list[i] = nil
+ }
+ case runtime.Object:
+ list[i] = item
+ default:
+ var found bool
+ if list[i], found = raw.Addr().Interface().(runtime.Object); !found {
+ return nil, fmt.Errorf("%v: item[%v]: Expected object, got %#v(%s)", obj, i, raw.Interface(), raw.Kind())
+ }
+ }
+ }
+ return list, nil
+}
+
+// objectSliceType is the type of a slice of Objects
+var objectSliceType = reflect.TypeOf([]runtime.Object{})
+
+// SetList sets the given list object's Items member have the elements given in
+// objects.
+// Returns an error if list is not a List type (does not have an Items member),
+// or if any of the objects are not of the right type.
+func SetList(list runtime.Object, objects []runtime.Object) error {
+ itemsPtr, err := GetItemsPtr(list)
+ if err != nil {
+ return err
+ }
+ items, err := conversion.EnforcePtr(itemsPtr)
+ if err != nil {
+ return err
+ }
+ if items.Type() == objectSliceType {
+ items.Set(reflect.ValueOf(objects))
+ return nil
+ }
+ slice := reflect.MakeSlice(items.Type(), len(objects), len(objects))
+ for i := range objects {
+ dest := slice.Index(i)
+ if dest.Type() == reflect.TypeOf(runtime.RawExtension{}) {
+ dest = dest.FieldByName("Object")
+ }
+
+ // check to see if you're directly assignable
+ if reflect.TypeOf(objects[i]).AssignableTo(dest.Type()) {
+ dest.Set(reflect.ValueOf(objects[i]))
+ continue
+ }
+
+ src, err := conversion.EnforcePtr(objects[i])
+ if err != nil {
+ return err
+ }
+ if src.Type().AssignableTo(dest.Type()) {
+ dest.Set(src)
+ } else if src.Type().ConvertibleTo(dest.Type()) {
+ dest.Set(src.Convert(dest.Type()))
+ } else {
+ return fmt.Errorf("item[%d]: can't assign or convert %v into %v", i, src.Type(), dest.Type())
+ }
+ }
+ items.Set(slice)
+ return nil
+}
diff --git a/vendor/k8s.io/apimachinery/pkg/api/meta/interfaces.go b/vendor/k8s.io/apimachinery/pkg/api/meta/interfaces.go
new file mode 100644
index 000000000..b2c8c97b1
--- /dev/null
+++ b/vendor/k8s.io/apimachinery/pkg/api/meta/interfaces.go
@@ -0,0 +1,146 @@
+/*
+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 meta
+
+import (
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/apimachinery/pkg/types"
+)
+
+// VersionInterfaces contains the interfaces one should use for dealing with types of a particular version.
+type VersionInterfaces struct {
+ runtime.ObjectConvertor
+ MetadataAccessor
+}
+
+type ListMetaAccessor interface {
+ GetListMeta() List
+}
+
+// List lets you work with list metadata from any of the versioned or
+// internal API objects. Attempting to set or retrieve a field on an object that does
+// not support that field will be a no-op and return a default value.
+type List metav1.List
+
+// Type exposes the type and APIVersion of versioned or internal API objects.
+type Type metav1.Type
+
+// MetadataAccessor lets you work with object and list metadata from any of the versioned or
+// internal API objects. Attempting to set or retrieve a field on an object that does
+// not support that field (Name, UID, Namespace on lists) will be a no-op and return
+// a default value.
+//
+// MetadataAccessor exposes Interface in a way that can be used with multiple objects.
+type MetadataAccessor interface {
+ APIVersion(obj runtime.Object) (string, error)
+ SetAPIVersion(obj runtime.Object, version string) error
+
+ Kind(obj runtime.Object) (string, error)
+ SetKind(obj runtime.Object, kind string) error
+
+ Namespace(obj runtime.Object) (string, error)
+ SetNamespace(obj runtime.Object, namespace string) error
+
+ Name(obj runtime.Object) (string, error)
+ SetName(obj runtime.Object, name string) error
+
+ GenerateName(obj runtime.Object) (string, error)
+ SetGenerateName(obj runtime.Object, name string) error
+
+ UID(obj runtime.Object) (types.UID, error)
+ SetUID(obj runtime.Object, uid types.UID) error
+
+ SelfLink(obj runtime.Object) (string, error)
+ SetSelfLink(obj runtime.Object, selfLink string) error
+
+ Labels(obj runtime.Object) (map[string]string, error)
+ SetLabels(obj runtime.Object, labels map[string]string) error
+
+ Annotations(obj runtime.Object) (map[string]string, error)
+ SetAnnotations(obj runtime.Object, annotations map[string]string) error
+
+ runtime.ResourceVersioner
+}
+
+type RESTScopeName string
+
+const (
+ RESTScopeNameNamespace RESTScopeName = "namespace"
+ RESTScopeNameRoot RESTScopeName = "root"
+)
+
+// RESTScope contains the information needed to deal with REST resources that are in a resource hierarchy
+type RESTScope interface {
+ // Name of the scope
+ Name() RESTScopeName
+ // ParamName is the optional name of the parameter that should be inserted in the resource url
+ // If empty, no param will be inserted
+ ParamName() string
+ // ArgumentName is the optional name that should be used for the variable holding the value.
+ ArgumentName() string
+ // ParamDescription is the optional description to use to document the parameter in api documentation
+ ParamDescription() string
+}
+
+// RESTMapping contains the information needed to deal with objects of a specific
+// resource and kind in a RESTful manner.
+type RESTMapping struct {
+ // Resource is a string representing the name of this resource as a REST client would see it
+ Resource string
+
+ GroupVersionKind schema.GroupVersionKind
+
+ // Scope contains the information needed to deal with REST Resources that are in a resource hierarchy
+ Scope RESTScope
+
+ runtime.ObjectConvertor
+ MetadataAccessor
+}
+
+// RESTMapper allows clients to map resources to kind, and map kind and version
+// to interfaces for manipulating those objects. It is primarily intended for
+// consumers of Kubernetes compatible REST APIs as defined in docs/devel/api-conventions.md.
+//
+// The Kubernetes API provides versioned resources and object kinds which are scoped
+// to API groups. In other words, kinds and resources should not be assumed to be
+// unique across groups.
+//
+// TODO: split into sub-interfaces
+type RESTMapper interface {
+ // KindFor takes a partial resource and returns the single match. Returns an error if there are multiple matches
+ KindFor(resource schema.GroupVersionResource) (schema.GroupVersionKind, error)
+
+ // KindsFor takes a partial resource and returns the list of potential kinds in priority order
+ KindsFor(resource schema.GroupVersionResource) ([]schema.GroupVersionKind, error)
+
+ // ResourceFor takes a partial resource and returns the single match. Returns an error if there are multiple matches
+ ResourceFor(input schema.GroupVersionResource) (schema.GroupVersionResource, error)
+
+ // ResourcesFor takes a partial resource and returns the list of potential resource in priority order
+ ResourcesFor(input schema.GroupVersionResource) ([]schema.GroupVersionResource, error)
+
+ // RESTMapping identifies a preferred resource mapping for the provided group kind.
+ RESTMapping(gk schema.GroupKind, versions ...string) (*RESTMapping, error)
+ // RESTMappings returns all resource mappings for the provided group kind if no
+ // version search is provided. Otherwise identifies a preferred resource mapping for
+ // the provided version(s).
+ RESTMappings(gk schema.GroupKind, versions ...string) ([]*RESTMapping, error)
+
+ ResourceSingularizer(resource string) (singular string, err error)
+}
diff --git a/vendor/k8s.io/apimachinery/pkg/api/meta/meta.go b/vendor/k8s.io/apimachinery/pkg/api/meta/meta.go
new file mode 100644
index 000000000..45d850ea8
--- /dev/null
+++ b/vendor/k8s.io/apimachinery/pkg/api/meta/meta.go
@@ -0,0 +1,610 @@
+/*
+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 meta
+
+import (
+ "fmt"
+ "reflect"
+
+ "github.com/golang/glog"
+
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ metav1alpha1 "k8s.io/apimachinery/pkg/apis/meta/v1alpha1"
+ "k8s.io/apimachinery/pkg/conversion"
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/apimachinery/pkg/types"
+)
+
+// errNotList is returned when an object implements the Object style interfaces but not the List style
+// interfaces.
+var errNotList = fmt.Errorf("object does not implement the List interfaces")
+
+// ListAccessor returns a List interface for the provided object or an error if the object does
+// not provide List.
+// IMPORTANT: Objects are a superset of lists, so all Objects return List metadata. Do not use this
+// check to determine whether an object *is* a List.
+// TODO: return bool instead of error
+func ListAccessor(obj interface{}) (List, error) {
+ switch t := obj.(type) {
+ case List:
+ return t, nil
+ case metav1.List:
+ return t, nil
+ case ListMetaAccessor:
+ if m := t.GetListMeta(); m != nil {
+ return m, nil
+ }
+ return nil, errNotList
+ case metav1.ListMetaAccessor:
+ if m := t.GetListMeta(); m != nil {
+ return m, nil
+ }
+ return nil, errNotList
+ case metav1.Object:
+ return t, nil
+ case metav1.ObjectMetaAccessor:
+ if m := t.GetObjectMeta(); m != nil {
+ return m, nil
+ }
+ return nil, errNotList
+ default:
+ return nil, errNotList
+ }
+}
+
+// errNotObject is returned when an object implements the List style interfaces but not the Object style
+// interfaces.
+var errNotObject = fmt.Errorf("object does not implement the Object interfaces")
+
+// Accessor takes an arbitrary object pointer and returns meta.Interface.
+// obj must be a pointer to an API type. An error is returned if the minimum
+// required fields are missing. Fields that are not required return the default
+// value and are a no-op if set.
+// TODO: return bool instead of error
+func Accessor(obj interface{}) (metav1.Object, error) {
+ switch t := obj.(type) {
+ case metav1.Object:
+ return t, nil
+ case metav1.ObjectMetaAccessor:
+ if m := t.GetObjectMeta(); m != nil {
+ return m, nil
+ }
+ return nil, errNotObject
+ default:
+ return nil, errNotObject
+ }
+}
+
+// AsPartialObjectMetadata takes the metav1 interface and returns a partial object.
+// TODO: consider making this solely a conversion action.
+func AsPartialObjectMetadata(m metav1.Object) *metav1alpha1.PartialObjectMetadata {
+ switch t := m.(type) {
+ case *metav1.ObjectMeta:
+ return &metav1alpha1.PartialObjectMetadata{ObjectMeta: *t}
+ default:
+ return &metav1alpha1.PartialObjectMetadata{
+ ObjectMeta: metav1.ObjectMeta{
+ Name: m.GetName(),
+ GenerateName: m.GetGenerateName(),
+ Namespace: m.GetNamespace(),
+ SelfLink: m.GetSelfLink(),
+ UID: m.GetUID(),
+ ResourceVersion: m.GetResourceVersion(),
+ Generation: m.GetGeneration(),
+ CreationTimestamp: m.GetCreationTimestamp(),
+ DeletionTimestamp: m.GetDeletionTimestamp(),
+ DeletionGracePeriodSeconds: m.GetDeletionGracePeriodSeconds(),
+ Labels: m.GetLabels(),
+ Annotations: m.GetAnnotations(),
+ OwnerReferences: m.GetOwnerReferences(),
+ Finalizers: m.GetFinalizers(),
+ ClusterName: m.GetClusterName(),
+ Initializers: m.GetInitializers(),
+ },
+ }
+ }
+}
+
+// TypeAccessor returns an interface that allows retrieving and modifying the APIVersion
+// and Kind of an in-memory internal object.
+// TODO: this interface is used to test code that does not have ObjectMeta or ListMeta
+// in round tripping (objects which can use apiVersion/kind, but do not fit the Kube
+// api conventions).
+func TypeAccessor(obj interface{}) (Type, error) {
+ if typed, ok := obj.(runtime.Object); ok {
+ return objectAccessor{typed}, nil
+ }
+ v, err := conversion.EnforcePtr(obj)
+ if err != nil {
+ return nil, err
+ }
+ t := v.Type()
+ if v.Kind() != reflect.Struct {
+ return nil, fmt.Errorf("expected struct, but got %v: %v (%#v)", v.Kind(), t, v.Interface())
+ }
+
+ typeMeta := v.FieldByName("TypeMeta")
+ if !typeMeta.IsValid() {
+ return nil, fmt.Errorf("struct %v lacks embedded TypeMeta type", t)
+ }
+ a := &genericAccessor{}
+ if err := extractFromTypeMeta(typeMeta, a); err != nil {
+ return nil, fmt.Errorf("unable to find type fields on %#v: %v", typeMeta, err)
+ }
+ return a, nil
+}
+
+type objectAccessor struct {
+ runtime.Object
+}
+
+func (obj objectAccessor) GetKind() string {
+ return obj.GetObjectKind().GroupVersionKind().Kind
+}
+
+func (obj objectAccessor) SetKind(kind string) {
+ gvk := obj.GetObjectKind().GroupVersionKind()
+ gvk.Kind = kind
+ obj.GetObjectKind().SetGroupVersionKind(gvk)
+}
+
+func (obj objectAccessor) GetAPIVersion() string {
+ return obj.GetObjectKind().GroupVersionKind().GroupVersion().String()
+}
+
+func (obj objectAccessor) SetAPIVersion(version string) {
+ gvk := obj.GetObjectKind().GroupVersionKind()
+ gv, err := schema.ParseGroupVersion(version)
+ if err != nil {
+ gv = schema.GroupVersion{Version: version}
+ }
+ gvk.Group, gvk.Version = gv.Group, gv.Version
+ obj.GetObjectKind().SetGroupVersionKind(gvk)
+}
+
+// NewAccessor returns a MetadataAccessor that can retrieve
+// or manipulate resource version on objects derived from core API
+// metadata concepts.
+func NewAccessor() MetadataAccessor {
+ return resourceAccessor{}
+}
+
+// resourceAccessor implements ResourceVersioner and SelfLinker.
+type resourceAccessor struct{}
+
+func (resourceAccessor) Kind(obj runtime.Object) (string, error) {
+ return objectAccessor{obj}.GetKind(), nil
+}
+
+func (resourceAccessor) SetKind(obj runtime.Object, kind string) error {
+ objectAccessor{obj}.SetKind(kind)
+ return nil
+}
+
+func (resourceAccessor) APIVersion(obj runtime.Object) (string, error) {
+ return objectAccessor{obj}.GetAPIVersion(), nil
+}
+
+func (resourceAccessor) SetAPIVersion(obj runtime.Object, version string) error {
+ objectAccessor{obj}.SetAPIVersion(version)
+ return nil
+}
+
+func (resourceAccessor) Namespace(obj runtime.Object) (string, error) {
+ accessor, err := Accessor(obj)
+ if err != nil {
+ return "", err
+ }
+ return accessor.GetNamespace(), nil
+}
+
+func (resourceAccessor) SetNamespace(obj runtime.Object, namespace string) error {
+ accessor, err := Accessor(obj)
+ if err != nil {
+ return err
+ }
+ accessor.SetNamespace(namespace)
+ return nil
+}
+
+func (resourceAccessor) Name(obj runtime.Object) (string, error) {
+ accessor, err := Accessor(obj)
+ if err != nil {
+ return "", err
+ }
+ return accessor.GetName(), nil
+}
+
+func (resourceAccessor) SetName(obj runtime.Object, name string) error {
+ accessor, err := Accessor(obj)
+ if err != nil {
+ return err
+ }
+ accessor.SetName(name)
+ return nil
+}
+
+func (resourceAccessor) GenerateName(obj runtime.Object) (string, error) {
+ accessor, err := Accessor(obj)
+ if err != nil {
+ return "", err
+ }
+ return accessor.GetGenerateName(), nil
+}
+
+func (resourceAccessor) SetGenerateName(obj runtime.Object, name string) error {
+ accessor, err := Accessor(obj)
+ if err != nil {
+ return err
+ }
+ accessor.SetGenerateName(name)
+ return nil
+}
+
+func (resourceAccessor) UID(obj runtime.Object) (types.UID, error) {
+ accessor, err := Accessor(obj)
+ if err != nil {
+ return "", err
+ }
+ return accessor.GetUID(), nil
+}
+
+func (resourceAccessor) SetUID(obj runtime.Object, uid types.UID) error {
+ accessor, err := Accessor(obj)
+ if err != nil {
+ return err
+ }
+ accessor.SetUID(uid)
+ return nil
+}
+
+func (resourceAccessor) SelfLink(obj runtime.Object) (string, error) {
+ accessor, err := ListAccessor(obj)
+ if err != nil {
+ return "", err
+ }
+ return accessor.GetSelfLink(), nil
+}
+
+func (resourceAccessor) SetSelfLink(obj runtime.Object, selfLink string) error {
+ accessor, err := ListAccessor(obj)
+ if err != nil {
+ return err
+ }
+ accessor.SetSelfLink(selfLink)
+ return nil
+}
+
+func (resourceAccessor) Labels(obj runtime.Object) (map[string]string, error) {
+ accessor, err := Accessor(obj)
+ if err != nil {
+ return nil, err
+ }
+ return accessor.GetLabels(), nil
+}
+
+func (resourceAccessor) SetLabels(obj runtime.Object, labels map[string]string) error {
+ accessor, err := Accessor(obj)
+ if err != nil {
+ return err
+ }
+ accessor.SetLabels(labels)
+ return nil
+}
+
+func (resourceAccessor) Annotations(obj runtime.Object) (map[string]string, error) {
+ accessor, err := Accessor(obj)
+ if err != nil {
+ return nil, err
+ }
+ return accessor.GetAnnotations(), nil
+}
+
+func (resourceAccessor) SetAnnotations(obj runtime.Object, annotations map[string]string) error {
+ accessor, err := Accessor(obj)
+ if err != nil {
+ return err
+ }
+ accessor.SetAnnotations(annotations)
+ return nil
+}
+
+func (resourceAccessor) ResourceVersion(obj runtime.Object) (string, error) {
+ accessor, err := ListAccessor(obj)
+ if err != nil {
+ return "", err
+ }
+ return accessor.GetResourceVersion(), nil
+}
+
+func (resourceAccessor) SetResourceVersion(obj runtime.Object, version string) error {
+ accessor, err := ListAccessor(obj)
+ if err != nil {
+ return err
+ }
+ accessor.SetResourceVersion(version)
+ return nil
+}
+
+// extractFromOwnerReference extracts v to o. v is the OwnerReferences field of an object.
+func extractFromOwnerReference(v reflect.Value, o *metav1.OwnerReference) error {
+ if err := runtime.Field(v, "APIVersion", &o.APIVersion); err != nil {
+ return err
+ }
+ if err := runtime.Field(v, "Kind", &o.Kind); err != nil {
+ return err
+ }
+ if err := runtime.Field(v, "Name", &o.Name); err != nil {
+ return err
+ }
+ if err := runtime.Field(v, "UID", &o.UID); err != nil {
+ return err
+ }
+ var controllerPtr *bool
+ if err := runtime.Field(v, "Controller", &controllerPtr); err != nil {
+ return err
+ }
+ if controllerPtr != nil {
+ controller := *controllerPtr
+ o.Controller = &controller
+ }
+ var blockOwnerDeletionPtr *bool
+ if err := runtime.Field(v, "BlockOwnerDeletion", &blockOwnerDeletionPtr); err != nil {
+ return err
+ }
+ if blockOwnerDeletionPtr != nil {
+ block := *blockOwnerDeletionPtr
+ o.BlockOwnerDeletion = &block
+ }
+ return nil
+}
+
+// setOwnerReference sets v to o. v is the OwnerReferences field of an object.
+func setOwnerReference(v reflect.Value, o *metav1.OwnerReference) error {
+ if err := runtime.SetField(o.APIVersion, v, "APIVersion"); err != nil {
+ return err
+ }
+ if err := runtime.SetField(o.Kind, v, "Kind"); err != nil {
+ return err
+ }
+ if err := runtime.SetField(o.Name, v, "Name"); err != nil {
+ return err
+ }
+ if err := runtime.SetField(o.UID, v, "UID"); err != nil {
+ return err
+ }
+ if o.Controller != nil {
+ controller := *(o.Controller)
+ if err := runtime.SetField(&controller, v, "Controller"); err != nil {
+ return err
+ }
+ }
+ if o.BlockOwnerDeletion != nil {
+ block := *(o.BlockOwnerDeletion)
+ if err := runtime.SetField(&block, v, "BlockOwnerDeletion"); err != nil {
+ return err
+ }
+ }
+ return nil
+}
+
+// genericAccessor contains pointers to strings that can modify an arbitrary
+// struct and implements the Accessor interface.
+type genericAccessor struct {
+ namespace *string
+ name *string
+ generateName *string
+ uid *types.UID
+ apiVersion *string
+ kind *string
+ resourceVersion *string
+ selfLink *string
+ creationTimestamp *metav1.Time
+ deletionTimestamp **metav1.Time
+ labels *map[string]string
+ annotations *map[string]string
+ ownerReferences reflect.Value
+ finalizers *[]string
+}
+
+func (a genericAccessor) GetNamespace() string {
+ if a.namespace == nil {
+ return ""
+ }
+ return *a.namespace
+}
+
+func (a genericAccessor) SetNamespace(namespace string) {
+ if a.namespace == nil {
+ return
+ }
+ *a.namespace = namespace
+}
+
+func (a genericAccessor) GetName() string {
+ if a.name == nil {
+ return ""
+ }
+ return *a.name
+}
+
+func (a genericAccessor) SetName(name string) {
+ if a.name == nil {
+ return
+ }
+ *a.name = name
+}
+
+func (a genericAccessor) GetGenerateName() string {
+ if a.generateName == nil {
+ return ""
+ }
+ return *a.generateName
+}
+
+func (a genericAccessor) SetGenerateName(generateName string) {
+ if a.generateName == nil {
+ return
+ }
+ *a.generateName = generateName
+}
+
+func (a genericAccessor) GetUID() types.UID {
+ if a.uid == nil {
+ return ""
+ }
+ return *a.uid
+}
+
+func (a genericAccessor) SetUID(uid types.UID) {
+ if a.uid == nil {
+ return
+ }
+ *a.uid = uid
+}
+
+func (a genericAccessor) GetAPIVersion() string {
+ return *a.apiVersion
+}
+
+func (a genericAccessor) SetAPIVersion(version string) {
+ *a.apiVersion = version
+}
+
+func (a genericAccessor) GetKind() string {
+ return *a.kind
+}
+
+func (a genericAccessor) SetKind(kind string) {
+ *a.kind = kind
+}
+
+func (a genericAccessor) GetResourceVersion() string {
+ return *a.resourceVersion
+}
+
+func (a genericAccessor) SetResourceVersion(version string) {
+ *a.resourceVersion = version
+}
+
+func (a genericAccessor) GetSelfLink() string {
+ return *a.selfLink
+}
+
+func (a genericAccessor) SetSelfLink(selfLink string) {
+ *a.selfLink = selfLink
+}
+
+func (a genericAccessor) GetCreationTimestamp() metav1.Time {
+ return *a.creationTimestamp
+}
+
+func (a genericAccessor) SetCreationTimestamp(timestamp metav1.Time) {
+ *a.creationTimestamp = timestamp
+}
+
+func (a genericAccessor) GetDeletionTimestamp() *metav1.Time {
+ return *a.deletionTimestamp
+}
+
+func (a genericAccessor) SetDeletionTimestamp(timestamp *metav1.Time) {
+ *a.deletionTimestamp = timestamp
+}
+
+func (a genericAccessor) GetLabels() map[string]string {
+ if a.labels == nil {
+ return nil
+ }
+ return *a.labels
+}
+
+func (a genericAccessor) SetLabels(labels map[string]string) {
+ *a.labels = labels
+}
+
+func (a genericAccessor) GetAnnotations() map[string]string {
+ if a.annotations == nil {
+ return nil
+ }
+ return *a.annotations
+}
+
+func (a genericAccessor) SetAnnotations(annotations map[string]string) {
+ if a.annotations == nil {
+ emptyAnnotations := make(map[string]string)
+ a.annotations = &emptyAnnotations
+ }
+ *a.annotations = annotations
+}
+
+func (a genericAccessor) GetFinalizers() []string {
+ if a.finalizers == nil {
+ return nil
+ }
+ return *a.finalizers
+}
+
+func (a genericAccessor) SetFinalizers(finalizers []string) {
+ *a.finalizers = finalizers
+}
+
+func (a genericAccessor) GetOwnerReferences() []metav1.OwnerReference {
+ var ret []metav1.OwnerReference
+ s := a.ownerReferences
+ if s.Kind() != reflect.Ptr || s.Elem().Kind() != reflect.Slice {
+ glog.Errorf("expect %v to be a pointer to slice", s)
+ return ret
+ }
+ s = s.Elem()
+ // Set the capacity to one element greater to avoid copy if the caller later append an element.
+ ret = make([]metav1.OwnerReference, s.Len(), s.Len()+1)
+ for i := 0; i < s.Len(); i++ {
+ if err := extractFromOwnerReference(s.Index(i), &ret[i]); err != nil {
+ glog.Errorf("extractFromOwnerReference failed: %v", err)
+ return ret
+ }
+ }
+ return ret
+}
+
+func (a genericAccessor) SetOwnerReferences(references []metav1.OwnerReference) {
+ s := a.ownerReferences
+ if s.Kind() != reflect.Ptr || s.Elem().Kind() != reflect.Slice {
+ glog.Errorf("expect %v to be a pointer to slice", s)
+ }
+ s = s.Elem()
+ newReferences := reflect.MakeSlice(s.Type(), len(references), len(references))
+ for i := 0; i < len(references); i++ {
+ if err := setOwnerReference(newReferences.Index(i), &references[i]); err != nil {
+ glog.Errorf("setOwnerReference failed: %v", err)
+ return
+ }
+ }
+ s.Set(newReferences)
+}
+
+// extractFromTypeMeta extracts pointers to version and kind fields from an object
+func extractFromTypeMeta(v reflect.Value, a *genericAccessor) error {
+ if err := runtime.FieldPtr(v, "APIVersion", &a.apiVersion); err != nil {
+ return err
+ }
+ if err := runtime.FieldPtr(v, "Kind", &a.kind); err != nil {
+ return err
+ }
+ return nil
+}
diff --git a/vendor/k8s.io/apimachinery/pkg/api/meta/multirestmapper.go b/vendor/k8s.io/apimachinery/pkg/api/meta/multirestmapper.go
new file mode 100644
index 000000000..679098fe5
--- /dev/null
+++ b/vendor/k8s.io/apimachinery/pkg/api/meta/multirestmapper.go
@@ -0,0 +1,210 @@
+/*
+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 meta
+
+import (
+ "fmt"
+ "strings"
+
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ utilerrors "k8s.io/apimachinery/pkg/util/errors"
+)
+
+// MultiRESTMapper is a wrapper for multiple RESTMappers.
+type MultiRESTMapper []RESTMapper
+
+func (m MultiRESTMapper) String() string {
+ nested := []string{}
+ for _, t := range m {
+ currString := fmt.Sprintf("%v", t)
+ splitStrings := strings.Split(currString, "\n")
+ nested = append(nested, strings.Join(splitStrings, "\n\t"))
+ }
+
+ return fmt.Sprintf("MultiRESTMapper{\n\t%s\n}", strings.Join(nested, "\n\t"))
+}
+
+// ResourceSingularizer converts a REST resource name from plural to singular (e.g., from pods to pod)
+// This implementation supports multiple REST schemas and return the first match.
+func (m MultiRESTMapper) ResourceSingularizer(resource string) (singular string, err error) {
+ for _, t := range m {
+ singular, err = t.ResourceSingularizer(resource)
+ if err == nil {
+ return
+ }
+ }
+ return
+}
+
+func (m MultiRESTMapper) ResourcesFor(resource schema.GroupVersionResource) ([]schema.GroupVersionResource, error) {
+ allGVRs := []schema.GroupVersionResource{}
+ for _, t := range m {
+ gvrs, err := t.ResourcesFor(resource)
+ // ignore "no match" errors, but any other error percolates back up
+ if IsNoMatchError(err) {
+ continue
+ }
+ if err != nil {
+ return nil, err
+ }
+
+ // walk the existing values to de-dup
+ for _, curr := range gvrs {
+ found := false
+ for _, existing := range allGVRs {
+ if curr == existing {
+ found = true
+ break
+ }
+ }
+
+ if !found {
+ allGVRs = append(allGVRs, curr)
+ }
+ }
+ }
+
+ if len(allGVRs) == 0 {
+ return nil, &NoResourceMatchError{PartialResource: resource}
+ }
+
+ return allGVRs, nil
+}
+
+func (m MultiRESTMapper) KindsFor(resource schema.GroupVersionResource) (gvk []schema.GroupVersionKind, err error) {
+ allGVKs := []schema.GroupVersionKind{}
+ for _, t := range m {
+ gvks, err := t.KindsFor(resource)
+ // ignore "no match" errors, but any other error percolates back up
+ if IsNoMatchError(err) {
+ continue
+ }
+ if err != nil {
+ return nil, err
+ }
+
+ // walk the existing values to de-dup
+ for _, curr := range gvks {
+ found := false
+ for _, existing := range allGVKs {
+ if curr == existing {
+ found = true
+ break
+ }
+ }
+
+ if !found {
+ allGVKs = append(allGVKs, curr)
+ }
+ }
+ }
+
+ if len(allGVKs) == 0 {
+ return nil, &NoResourceMatchError{PartialResource: resource}
+ }
+
+ return allGVKs, nil
+}
+
+func (m MultiRESTMapper) ResourceFor(resource schema.GroupVersionResource) (schema.GroupVersionResource, error) {
+ resources, err := m.ResourcesFor(resource)
+ if err != nil {
+ return schema.GroupVersionResource{}, err
+ }
+ if len(resources) == 1 {
+ return resources[0], nil
+ }
+
+ return schema.GroupVersionResource{}, &AmbiguousResourceError{PartialResource: resource, MatchingResources: resources}
+}
+
+func (m MultiRESTMapper) KindFor(resource schema.GroupVersionResource) (schema.GroupVersionKind, error) {
+ kinds, err := m.KindsFor(resource)
+ if err != nil {
+ return schema.GroupVersionKind{}, err
+ }
+ if len(kinds) == 1 {
+ return kinds[0], nil
+ }
+
+ return schema.GroupVersionKind{}, &AmbiguousResourceError{PartialResource: resource, MatchingKinds: kinds}
+}
+
+// RESTMapping provides the REST mapping for the resource based on the
+// kind and version. This implementation supports multiple REST schemas and
+// return the first match.
+func (m MultiRESTMapper) RESTMapping(gk schema.GroupKind, versions ...string) (*RESTMapping, error) {
+ allMappings := []*RESTMapping{}
+ errors := []error{}
+
+ for _, t := range m {
+ currMapping, err := t.RESTMapping(gk, versions...)
+ // ignore "no match" errors, but any other error percolates back up
+ if IsNoMatchError(err) {
+ continue
+ }
+ if err != nil {
+ errors = append(errors, err)
+ continue
+ }
+
+ allMappings = append(allMappings, currMapping)
+ }
+
+ // if we got exactly one mapping, then use it even if other requested failed
+ if len(allMappings) == 1 {
+ return allMappings[0], nil
+ }
+ if len(allMappings) > 1 {
+ var kinds []schema.GroupVersionKind
+ for _, m := range allMappings {
+ kinds = append(kinds, m.GroupVersionKind)
+ }
+ return nil, &AmbiguousKindError{PartialKind: gk.WithVersion(""), MatchingKinds: kinds}
+ }
+ if len(errors) > 0 {
+ return nil, utilerrors.NewAggregate(errors)
+ }
+ return nil, &NoKindMatchError{PartialKind: gk.WithVersion("")}
+}
+
+// RESTMappings returns all possible RESTMappings for the provided group kind, or an error
+// if the type is not recognized.
+func (m MultiRESTMapper) RESTMappings(gk schema.GroupKind, versions ...string) ([]*RESTMapping, error) {
+ var allMappings []*RESTMapping
+ var errors []error
+
+ for _, t := range m {
+ currMappings, err := t.RESTMappings(gk, versions...)
+ // ignore "no match" errors, but any other error percolates back up
+ if IsNoMatchError(err) {
+ continue
+ }
+ if err != nil {
+ errors = append(errors, err)
+ continue
+ }
+ allMappings = append(allMappings, currMappings...)
+ }
+ if len(errors) > 0 {
+ return nil, utilerrors.NewAggregate(errors)
+ }
+ if len(allMappings) == 0 {
+ return nil, &NoKindMatchError{PartialKind: gk.WithVersion("")}
+ }
+ return allMappings, nil
+}
diff --git a/vendor/k8s.io/apimachinery/pkg/api/meta/priority.go b/vendor/k8s.io/apimachinery/pkg/api/meta/priority.go
new file mode 100644
index 000000000..2a14aa7ab
--- /dev/null
+++ b/vendor/k8s.io/apimachinery/pkg/api/meta/priority.go
@@ -0,0 +1,222 @@
+/*
+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 meta
+
+import (
+ "fmt"
+
+ "k8s.io/apimachinery/pkg/runtime/schema"
+)
+
+const (
+ AnyGroup = "*"
+ AnyVersion = "*"
+ AnyResource = "*"
+ AnyKind = "*"
+)
+
+// PriorityRESTMapper is a wrapper for automatically choosing a particular Resource or Kind
+// when multiple matches are possible
+type PriorityRESTMapper struct {
+ // Delegate is the RESTMapper to use to locate all the Kind and Resource matches
+ Delegate RESTMapper
+
+ // ResourcePriority is a list of priority patterns to apply to matching resources.
+ // The list of all matching resources is narrowed based on the patterns until only one remains.
+ // A pattern with no matches is skipped. A pattern with more than one match uses its
+ // matches as the list to continue matching against.
+ ResourcePriority []schema.GroupVersionResource
+
+ // KindPriority is a list of priority patterns to apply to matching kinds.
+ // The list of all matching kinds is narrowed based on the patterns until only one remains.
+ // A pattern with no matches is skipped. A pattern with more than one match uses its
+ // matches as the list to continue matching against.
+ KindPriority []schema.GroupVersionKind
+}
+
+func (m PriorityRESTMapper) String() string {
+ return fmt.Sprintf("PriorityRESTMapper{\n\t%v\n\t%v\n\t%v\n}", m.ResourcePriority, m.KindPriority, m.Delegate)
+}
+
+// ResourceFor finds all resources, then passes them through the ResourcePriority patterns to find a single matching hit.
+func (m PriorityRESTMapper) ResourceFor(partiallySpecifiedResource schema.GroupVersionResource) (schema.GroupVersionResource, error) {
+ originalGVRs, err := m.Delegate.ResourcesFor(partiallySpecifiedResource)
+ if err != nil {
+ return schema.GroupVersionResource{}, err
+ }
+ if len(originalGVRs) == 1 {
+ return originalGVRs[0], nil
+ }
+
+ remainingGVRs := append([]schema.GroupVersionResource{}, originalGVRs...)
+ for _, pattern := range m.ResourcePriority {
+ matchedGVRs := []schema.GroupVersionResource{}
+ for _, gvr := range remainingGVRs {
+ if resourceMatches(pattern, gvr) {
+ matchedGVRs = append(matchedGVRs, gvr)
+ }
+ }
+
+ switch len(matchedGVRs) {
+ case 0:
+ // if you have no matches, then nothing matched this pattern just move to the next
+ continue
+ case 1:
+ // one match, return
+ return matchedGVRs[0], nil
+ default:
+ // more than one match, use the matched hits as the list moving to the next pattern.
+ // this way you can have a series of selection criteria
+ remainingGVRs = matchedGVRs
+ }
+ }
+
+ return schema.GroupVersionResource{}, &AmbiguousResourceError{PartialResource: partiallySpecifiedResource, MatchingResources: originalGVRs}
+}
+
+// KindFor finds all kinds, then passes them through the KindPriority patterns to find a single matching hit.
+func (m PriorityRESTMapper) KindFor(partiallySpecifiedResource schema.GroupVersionResource) (schema.GroupVersionKind, error) {
+ originalGVKs, err := m.Delegate.KindsFor(partiallySpecifiedResource)
+ if err != nil {
+ return schema.GroupVersionKind{}, err
+ }
+ if len(originalGVKs) == 1 {
+ return originalGVKs[0], nil
+ }
+
+ remainingGVKs := append([]schema.GroupVersionKind{}, originalGVKs...)
+ for _, pattern := range m.KindPriority {
+ matchedGVKs := []schema.GroupVersionKind{}
+ for _, gvr := range remainingGVKs {
+ if kindMatches(pattern, gvr) {
+ matchedGVKs = append(matchedGVKs, gvr)
+ }
+ }
+
+ switch len(matchedGVKs) {
+ case 0:
+ // if you have no matches, then nothing matched this pattern just move to the next
+ continue
+ case 1:
+ // one match, return
+ return matchedGVKs[0], nil
+ default:
+ // more than one match, use the matched hits as the list moving to the next pattern.
+ // this way you can have a series of selection criteria
+ remainingGVKs = matchedGVKs
+ }
+ }
+
+ return schema.GroupVersionKind{}, &AmbiguousResourceError{PartialResource: partiallySpecifiedResource, MatchingKinds: originalGVKs}
+}
+
+func resourceMatches(pattern schema.GroupVersionResource, resource schema.GroupVersionResource) bool {
+ if pattern.Group != AnyGroup && pattern.Group != resource.Group {
+ return false
+ }
+ if pattern.Version != AnyVersion && pattern.Version != resource.Version {
+ return false
+ }
+ if pattern.Resource != AnyResource && pattern.Resource != resource.Resource {
+ return false
+ }
+
+ return true
+}
+
+func kindMatches(pattern schema.GroupVersionKind, kind schema.GroupVersionKind) bool {
+ if pattern.Group != AnyGroup && pattern.Group != kind.Group {
+ return false
+ }
+ if pattern.Version != AnyVersion && pattern.Version != kind.Version {
+ return false
+ }
+ if pattern.Kind != AnyKind && pattern.Kind != kind.Kind {
+ return false
+ }
+
+ return true
+}
+
+func (m PriorityRESTMapper) RESTMapping(gk schema.GroupKind, versions ...string) (mapping *RESTMapping, err error) {
+ mappings, err := m.Delegate.RESTMappings(gk)
+ if err != nil {
+ return nil, err
+ }
+
+ // any versions the user provides take priority
+ priorities := m.KindPriority
+ if len(versions) > 0 {
+ priorities = make([]schema.GroupVersionKind, 0, len(m.KindPriority)+len(versions))
+ for _, version := range versions {
+ gv := schema.GroupVersion{
+ Version: version,
+ Group: gk.Group,
+ }
+ priorities = append(priorities, gv.WithKind(AnyKind))
+ }
+ priorities = append(priorities, m.KindPriority...)
+ }
+
+ remaining := append([]*RESTMapping{}, mappings...)
+ for _, pattern := range priorities {
+ var matching []*RESTMapping
+ for _, m := range remaining {
+ if kindMatches(pattern, m.GroupVersionKind) {
+ matching = append(matching, m)
+ }
+ }
+
+ switch len(matching) {
+ case 0:
+ // if you have no matches, then nothing matched this pattern just move to the next
+ continue
+ case 1:
+ // one match, return
+ return matching[0], nil
+ default:
+ // more than one match, use the matched hits as the list moving to the next pattern.
+ // this way you can have a series of selection criteria
+ remaining = matching
+ }
+ }
+ if len(remaining) == 1 {
+ return remaining[0], nil
+ }
+
+ var kinds []schema.GroupVersionKind
+ for _, m := range mappings {
+ kinds = append(kinds, m.GroupVersionKind)
+ }
+ return nil, &AmbiguousKindError{PartialKind: gk.WithVersion(""), MatchingKinds: kinds}
+}
+
+func (m PriorityRESTMapper) RESTMappings(gk schema.GroupKind, versions ...string) ([]*RESTMapping, error) {
+ return m.Delegate.RESTMappings(gk, versions...)
+}
+
+func (m PriorityRESTMapper) ResourceSingularizer(resource string) (singular string, err error) {
+ return m.Delegate.ResourceSingularizer(resource)
+}
+
+func (m PriorityRESTMapper) ResourcesFor(partiallySpecifiedResource schema.GroupVersionResource) ([]schema.GroupVersionResource, error) {
+ return m.Delegate.ResourcesFor(partiallySpecifiedResource)
+}
+
+func (m PriorityRESTMapper) KindsFor(partiallySpecifiedResource schema.GroupVersionResource) (gvk []schema.GroupVersionKind, err error) {
+ return m.Delegate.KindsFor(partiallySpecifiedResource)
+}
diff --git a/vendor/k8s.io/apimachinery/pkg/api/meta/restmapper.go b/vendor/k8s.io/apimachinery/pkg/api/meta/restmapper.go
new file mode 100644
index 000000000..c6b2597dd
--- /dev/null
+++ b/vendor/k8s.io/apimachinery/pkg/api/meta/restmapper.go
@@ -0,0 +1,545 @@
+/*
+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.
+*/
+
+// TODO: move everything in this file to pkg/api/rest
+package meta
+
+import (
+ "fmt"
+ "sort"
+ "strings"
+
+ "k8s.io/apimachinery/pkg/runtime"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+)
+
+// Implements RESTScope interface
+type restScope struct {
+ name RESTScopeName
+ paramName string
+ argumentName string
+ paramDescription string
+}
+
+func (r *restScope) Name() RESTScopeName {
+ return r.name
+}
+func (r *restScope) ParamName() string {
+ return r.paramName
+}
+func (r *restScope) ArgumentName() string {
+ return r.argumentName
+}
+func (r *restScope) ParamDescription() string {
+ return r.paramDescription
+}
+
+var RESTScopeNamespace = &restScope{
+ name: RESTScopeNameNamespace,
+ paramName: "namespaces",
+ argumentName: "namespace",
+ paramDescription: "object name and auth scope, such as for teams and projects",
+}
+
+var RESTScopeRoot = &restScope{
+ name: RESTScopeNameRoot,
+}
+
+// DefaultRESTMapper exposes mappings between the types defined in a
+// runtime.Scheme. It assumes that all types defined the provided scheme
+// can be mapped with the provided MetadataAccessor and Codec interfaces.
+//
+// The resource name of a Kind is defined as the lowercase,
+// English-plural version of the Kind string.
+// When converting from resource to Kind, the singular version of the
+// resource name is also accepted for convenience.
+//
+// TODO: Only accept plural for some operations for increased control?
+// (`get pod bar` vs `get pods bar`)
+type DefaultRESTMapper struct {
+ defaultGroupVersions []schema.GroupVersion
+
+ resourceToKind map[schema.GroupVersionResource]schema.GroupVersionKind
+ kindToPluralResource map[schema.GroupVersionKind]schema.GroupVersionResource
+ kindToScope map[schema.GroupVersionKind]RESTScope
+ singularToPlural map[schema.GroupVersionResource]schema.GroupVersionResource
+ pluralToSingular map[schema.GroupVersionResource]schema.GroupVersionResource
+
+ interfacesFunc VersionInterfacesFunc
+}
+
+func (m *DefaultRESTMapper) String() string {
+ return fmt.Sprintf("DefaultRESTMapper{kindToPluralResource=%v}", m.kindToPluralResource)
+}
+
+var _ RESTMapper = &DefaultRESTMapper{}
+
+// VersionInterfacesFunc returns the appropriate typer, and metadata accessor for a
+// given api version, or an error if no such api version exists.
+type VersionInterfacesFunc func(version schema.GroupVersion) (*VersionInterfaces, error)
+
+// NewDefaultRESTMapper initializes a mapping between Kind and APIVersion
+// to a resource name and back based on the objects in a runtime.Scheme
+// and the Kubernetes API conventions. Takes a group name, a priority list of the versions
+// to search when an object has no default version (set empty to return an error),
+// and a function that retrieves the correct metadata for a given version.
+func NewDefaultRESTMapper(defaultGroupVersions []schema.GroupVersion, f VersionInterfacesFunc) *DefaultRESTMapper {
+ resourceToKind := make(map[schema.GroupVersionResource]schema.GroupVersionKind)
+ kindToPluralResource := make(map[schema.GroupVersionKind]schema.GroupVersionResource)
+ kindToScope := make(map[schema.GroupVersionKind]RESTScope)
+ singularToPlural := make(map[schema.GroupVersionResource]schema.GroupVersionResource)
+ pluralToSingular := make(map[schema.GroupVersionResource]schema.GroupVersionResource)
+ // TODO: verify name mappings work correctly when versions differ
+
+ return &DefaultRESTMapper{
+ resourceToKind: resourceToKind,
+ kindToPluralResource: kindToPluralResource,
+ kindToScope: kindToScope,
+ defaultGroupVersions: defaultGroupVersions,
+ singularToPlural: singularToPlural,
+ pluralToSingular: pluralToSingular,
+ interfacesFunc: f,
+ }
+}
+
+func (m *DefaultRESTMapper) Add(kind schema.GroupVersionKind, scope RESTScope) {
+ plural, singular := UnsafeGuessKindToResource(kind)
+
+ m.singularToPlural[singular] = plural
+ m.pluralToSingular[plural] = singular
+
+ m.resourceToKind[singular] = kind
+ m.resourceToKind[plural] = kind
+
+ m.kindToPluralResource[kind] = plural
+ m.kindToScope[kind] = scope
+}
+
+// unpluralizedSuffixes is a list of resource suffixes that are the same plural and singular
+// This is only is only necessary because some bits of code are lazy and don't actually use the RESTMapper like they should.
+// TODO eliminate this so that different callers can correctly map to resources. This probably means updating all
+// callers to use the RESTMapper they mean.
+var unpluralizedSuffixes = []string{
+ "endpoints",
+}
+
+// UnsafeGuessKindToResource converts Kind to a resource name.
+// Broken. This method only "sort of" works when used outside of this package. It assumes that Kinds and Resources match
+// and they aren't guaranteed to do so.
+func UnsafeGuessKindToResource(kind schema.GroupVersionKind) ( /*plural*/ schema.GroupVersionResource /*singular*/, schema.GroupVersionResource) {
+ kindName := kind.Kind
+ if len(kindName) == 0 {
+ return schema.GroupVersionResource{}, schema.GroupVersionResource{}
+ }
+ singularName := strings.ToLower(kindName)
+ singular := kind.GroupVersion().WithResource(singularName)
+
+ for _, skip := range unpluralizedSuffixes {
+ if strings.HasSuffix(singularName, skip) {
+ return singular, singular
+ }
+ }
+
+ switch string(singularName[len(singularName)-1]) {
+ case "s":
+ return kind.GroupVersion().WithResource(singularName + "es"), singular
+ case "y":
+ return kind.GroupVersion().WithResource(strings.TrimSuffix(singularName, "y") + "ies"), singular
+ }
+
+ return kind.GroupVersion().WithResource(singularName + "s"), singular
+}
+
+// ResourceSingularizer implements RESTMapper
+// It converts a resource name from plural to singular (e.g., from pods to pod)
+func (m *DefaultRESTMapper) ResourceSingularizer(resourceType string) (string, error) {
+ partialResource := schema.GroupVersionResource{Resource: resourceType}
+ resources, err := m.ResourcesFor(partialResource)
+ if err != nil {
+ return resourceType, err
+ }
+
+ singular := schema.GroupVersionResource{}
+ for _, curr := range resources {
+ currSingular, ok := m.pluralToSingular[curr]
+ if !ok {
+ continue
+ }
+ if singular.Empty() {
+ singular = currSingular
+ continue
+ }
+
+ if currSingular.Resource != singular.Resource {
+ return resourceType, fmt.Errorf("multiple possible singular resources (%v) found for %v", resources, resourceType)
+ }
+ }
+
+ if singular.Empty() {
+ return resourceType, fmt.Errorf("no singular of resource %v has been defined", resourceType)
+ }
+
+ return singular.Resource, nil
+}
+
+// coerceResourceForMatching makes the resource lower case and converts internal versions to unspecified (legacy behavior)
+func coerceResourceForMatching(resource schema.GroupVersionResource) schema.GroupVersionResource {
+ resource.Resource = strings.ToLower(resource.Resource)
+ if resource.Version == runtime.APIVersionInternal {
+ resource.Version = ""
+ }
+
+ return resource
+}
+
+func (m *DefaultRESTMapper) ResourcesFor(input schema.GroupVersionResource) ([]schema.GroupVersionResource, error) {
+ resource := coerceResourceForMatching(input)
+
+ hasResource := len(resource.Resource) > 0
+ hasGroup := len(resource.Group) > 0
+ hasVersion := len(resource.Version) > 0
+
+ if !hasResource {
+ return nil, fmt.Errorf("a resource must be present, got: %v", resource)
+ }
+
+ ret := []schema.GroupVersionResource{}
+ switch {
+ case hasGroup && hasVersion:
+ // fully qualified. Find the exact match
+ for plural, singular := range m.pluralToSingular {
+ if singular == resource {
+ ret = append(ret, plural)
+ break
+ }
+ if plural == resource {
+ ret = append(ret, plural)
+ break
+ }
+ }
+
+ case hasGroup:
+ // given a group, prefer an exact match. If you don't find one, resort to a prefix match on group
+ foundExactMatch := false
+ requestedGroupResource := resource.GroupResource()
+ for plural, singular := range m.pluralToSingular {
+ if singular.GroupResource() == requestedGroupResource {
+ foundExactMatch = true
+ ret = append(ret, plural)
+ }
+ if plural.GroupResource() == requestedGroupResource {
+ foundExactMatch = true
+ ret = append(ret, plural)
+ }
+ }
+
+ // if you didn't find an exact match, match on group prefixing. This allows storageclass.storage to match
+ // storageclass.storage.k8s.io
+ if !foundExactMatch {
+ for plural, singular := range m.pluralToSingular {
+ if !strings.HasPrefix(plural.Group, requestedGroupResource.Group) {
+ continue
+ }
+ if singular.Resource == requestedGroupResource.Resource {
+ ret = append(ret, plural)
+ }
+ if plural.Resource == requestedGroupResource.Resource {
+ ret = append(ret, plural)
+ }
+ }
+
+ }
+
+ case hasVersion:
+ for plural, singular := range m.pluralToSingular {
+ if singular.Version == resource.Version && singular.Resource == resource.Resource {
+ ret = append(ret, plural)
+ }
+ if plural.Version == resource.Version && plural.Resource == resource.Resource {
+ ret = append(ret, plural)
+ }
+ }
+
+ default:
+ for plural, singular := range m.pluralToSingular {
+ if singular.Resource == resource.Resource {
+ ret = append(ret, plural)
+ }
+ if plural.Resource == resource.Resource {
+ ret = append(ret, plural)
+ }
+ }
+ }
+
+ if len(ret) == 0 {
+ return nil, &NoResourceMatchError{PartialResource: resource}
+ }
+
+ sort.Sort(resourceByPreferredGroupVersion{ret, m.defaultGroupVersions})
+ return ret, nil
+}
+
+func (m *DefaultRESTMapper) ResourceFor(resource schema.GroupVersionResource) (schema.GroupVersionResource, error) {
+ resources, err := m.ResourcesFor(resource)
+ if err != nil {
+ return schema.GroupVersionResource{}, err
+ }
+ if len(resources) == 1 {
+ return resources[0], nil
+ }
+
+ return schema.GroupVersionResource{}, &AmbiguousResourceError{PartialResource: resource, MatchingResources: resources}
+}
+
+func (m *DefaultRESTMapper) KindsFor(input schema.GroupVersionResource) ([]schema.GroupVersionKind, error) {
+ resource := coerceResourceForMatching(input)
+
+ hasResource := len(resource.Resource) > 0
+ hasGroup := len(resource.Group) > 0
+ hasVersion := len(resource.Version) > 0
+
+ if !hasResource {
+ return nil, fmt.Errorf("a resource must be present, got: %v", resource)
+ }
+
+ ret := []schema.GroupVersionKind{}
+ switch {
+ // fully qualified. Find the exact match
+ case hasGroup && hasVersion:
+ kind, exists := m.resourceToKind[resource]
+ if exists {
+ ret = append(ret, kind)
+ }
+
+ case hasGroup:
+ foundExactMatch := false
+ requestedGroupResource := resource.GroupResource()
+ for currResource, currKind := range m.resourceToKind {
+ if currResource.GroupResource() == requestedGroupResource {
+ foundExactMatch = true
+ ret = append(ret, currKind)
+ }
+ }
+
+ // if you didn't find an exact match, match on group prefixing. This allows storageclass.storage to match
+ // storageclass.storage.k8s.io
+ if !foundExactMatch {
+ for currResource, currKind := range m.resourceToKind {
+ if !strings.HasPrefix(currResource.Group, requestedGroupResource.Group) {
+ continue
+ }
+ if currResource.Resource == requestedGroupResource.Resource {
+ ret = append(ret, currKind)
+ }
+ }
+
+ }
+
+ case hasVersion:
+ for currResource, currKind := range m.resourceToKind {
+ if currResource.Version == resource.Version && currResource.Resource == resource.Resource {
+ ret = append(ret, currKind)
+ }
+ }
+
+ default:
+ for currResource, currKind := range m.resourceToKind {
+ if currResource.Resource == resource.Resource {
+ ret = append(ret, currKind)
+ }
+ }
+ }
+
+ if len(ret) == 0 {
+ return nil, &NoResourceMatchError{PartialResource: input}
+ }
+
+ sort.Sort(kindByPreferredGroupVersion{ret, m.defaultGroupVersions})
+ return ret, nil
+}
+
+func (m *DefaultRESTMapper) KindFor(resource schema.GroupVersionResource) (schema.GroupVersionKind, error) {
+ kinds, err := m.KindsFor(resource)
+ if err != nil {
+ return schema.GroupVersionKind{}, err
+ }
+ if len(kinds) == 1 {
+ return kinds[0], nil
+ }
+
+ return schema.GroupVersionKind{}, &AmbiguousResourceError{PartialResource: resource, MatchingKinds: kinds}
+}
+
+type kindByPreferredGroupVersion struct {
+ list []schema.GroupVersionKind
+ sortOrder []schema.GroupVersion
+}
+
+func (o kindByPreferredGroupVersion) Len() int { return len(o.list) }
+func (o kindByPreferredGroupVersion) Swap(i, j int) { o.list[i], o.list[j] = o.list[j], o.list[i] }
+func (o kindByPreferredGroupVersion) Less(i, j int) bool {
+ lhs := o.list[i]
+ rhs := o.list[j]
+ if lhs == rhs {
+ return false
+ }
+
+ if lhs.GroupVersion() == rhs.GroupVersion() {
+ return lhs.Kind < rhs.Kind
+ }
+
+ // otherwise, the difference is in the GroupVersion, so we need to sort with respect to the preferred order
+ lhsIndex := -1
+ rhsIndex := -1
+
+ for i := range o.sortOrder {
+ if o.sortOrder[i] == lhs.GroupVersion() {
+ lhsIndex = i
+ }
+ if o.sortOrder[i] == rhs.GroupVersion() {
+ rhsIndex = i
+ }
+ }
+
+ if rhsIndex == -1 {
+ return true
+ }
+
+ return lhsIndex < rhsIndex
+}
+
+type resourceByPreferredGroupVersion struct {
+ list []schema.GroupVersionResource
+ sortOrder []schema.GroupVersion
+}
+
+func (o resourceByPreferredGroupVersion) Len() int { return len(o.list) }
+func (o resourceByPreferredGroupVersion) Swap(i, j int) { o.list[i], o.list[j] = o.list[j], o.list[i] }
+func (o resourceByPreferredGroupVersion) Less(i, j int) bool {
+ lhs := o.list[i]
+ rhs := o.list[j]
+ if lhs == rhs {
+ return false
+ }
+
+ if lhs.GroupVersion() == rhs.GroupVersion() {
+ return lhs.Resource < rhs.Resource
+ }
+
+ // otherwise, the difference is in the GroupVersion, so we need to sort with respect to the preferred order
+ lhsIndex := -1
+ rhsIndex := -1
+
+ for i := range o.sortOrder {
+ if o.sortOrder[i] == lhs.GroupVersion() {
+ lhsIndex = i
+ }
+ if o.sortOrder[i] == rhs.GroupVersion() {
+ rhsIndex = i
+ }
+ }
+
+ if rhsIndex == -1 {
+ return true
+ }
+
+ return lhsIndex < rhsIndex
+}
+
+// RESTMapping returns a struct representing the resource path and conversion interfaces a
+// RESTClient should use to operate on the provided group/kind in order of versions. If a version search
+// order is not provided, the search order provided to DefaultRESTMapper will be used to resolve which
+// version should be used to access the named group/kind.
+func (m *DefaultRESTMapper) RESTMapping(gk schema.GroupKind, versions ...string) (*RESTMapping, error) {
+ mappings, err := m.RESTMappings(gk, versions...)
+ if err != nil {
+ return nil, err
+ }
+ if len(mappings) == 0 {
+ return nil, &NoKindMatchError{PartialKind: gk.WithVersion("")}
+ }
+ // since we rely on RESTMappings method
+ // take the first match and return to the caller
+ // as this was the existing behavior.
+ return mappings[0], nil
+}
+
+// RESTMappings returns the RESTMappings for the provided group kind. If a version search order
+// is not provided, the search order provided to DefaultRESTMapper will be used.
+func (m *DefaultRESTMapper) RESTMappings(gk schema.GroupKind, versions ...string) ([]*RESTMapping, error) {
+ mappings := make([]*RESTMapping, 0)
+ potentialGVK := make([]schema.GroupVersionKind, 0)
+ hadVersion := false
+
+ // Pick an appropriate version
+ for _, version := range versions {
+ if len(version) == 0 || version == runtime.APIVersionInternal {
+ continue
+ }
+ currGVK := gk.WithVersion(version)
+ hadVersion = true
+ if _, ok := m.kindToPluralResource[currGVK]; ok {
+ potentialGVK = append(potentialGVK, currGVK)
+ break
+ }
+ }
+ // Use the default preferred versions
+ if !hadVersion && len(potentialGVK) == 0 {
+ for _, gv := range m.defaultGroupVersions {
+ if gv.Group != gk.Group {
+ continue
+ }
+ potentialGVK = append(potentialGVK, gk.WithVersion(gv.Version))
+ }
+ }
+
+ if len(potentialGVK) == 0 {
+ return nil, &NoKindMatchError{PartialKind: gk.WithVersion("")}
+ }
+
+ for _, gvk := range potentialGVK {
+ //Ensure we have a REST mapping
+ res, ok := m.kindToPluralResource[gvk]
+ if !ok {
+ continue
+ }
+
+ // Ensure we have a REST scope
+ scope, ok := m.kindToScope[gvk]
+ if !ok {
+ return nil, fmt.Errorf("the provided version %q and kind %q cannot be mapped to a supported scope", gvk.GroupVersion(), gvk.Kind)
+ }
+
+ interfaces, err := m.interfacesFunc(gvk.GroupVersion())
+ if err != nil {
+ return nil, fmt.Errorf("the provided version %q has no relevant versions: %v", gvk.GroupVersion().String(), err)
+ }
+
+ mappings = append(mappings, &RESTMapping{
+ Resource: res.Resource,
+ GroupVersionKind: gvk,
+ Scope: scope,
+
+ ObjectConvertor: interfaces.ObjectConvertor,
+ MetadataAccessor: interfaces.MetadataAccessor,
+ })
+ }
+
+ if len(mappings) == 0 {
+ return nil, &NoResourceMatchError{PartialResource: schema.GroupVersionResource{Group: gk.Group, Resource: gk.Kind}}
+ }
+ return mappings, nil
+}
diff --git a/vendor/k8s.io/apimachinery/pkg/api/meta/unstructured.go b/vendor/k8s.io/apimachinery/pkg/api/meta/unstructured.go
new file mode 100644
index 000000000..3ebf24815
--- /dev/null
+++ b/vendor/k8s.io/apimachinery/pkg/api/meta/unstructured.go
@@ -0,0 +1,31 @@
+/*
+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 meta
+
+import (
+ "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+)
+
+// InterfacesForUnstructured returns VersionInterfaces suitable for
+// dealing with unstructured.Unstructured objects.
+func InterfacesForUnstructured(schema.GroupVersion) (*VersionInterfaces, error) {
+ return &VersionInterfaces{
+ ObjectConvertor: &unstructured.UnstructuredObjectConverter{},
+ MetadataAccessor: NewAccessor(),
+ }, nil
+}
diff --git a/vendor/k8s.io/apimachinery/pkg/api/resource/amount.go b/vendor/k8s.io/apimachinery/pkg/api/resource/amount.go
new file mode 100644
index 000000000..a8866a43e
--- /dev/null
+++ b/vendor/k8s.io/apimachinery/pkg/api/resource/amount.go
@@ -0,0 +1,299 @@
+/*
+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 resource
+
+import (
+ "math/big"
+ "strconv"
+
+ inf "gopkg.in/inf.v0"
+)
+
+// Scale is used for getting and setting the base-10 scaled value.
+// Base-2 scales are omitted for mathematical simplicity.
+// See Quantity.ScaledValue for more details.
+type Scale int32
+
+// infScale adapts a Scale value to an inf.Scale value.
+func (s Scale) infScale() inf.Scale {
+ return inf.Scale(-s) // inf.Scale is upside-down
+}
+
+const (
+ Nano Scale = -9
+ Micro Scale = -6
+ Milli Scale = -3
+ Kilo Scale = 3
+ Mega Scale = 6
+ Giga Scale = 9
+ Tera Scale = 12
+ Peta Scale = 15
+ Exa Scale = 18
+)
+
+var (
+ Zero = int64Amount{}
+
+ // Used by quantity strings - treat as read only
+ zeroBytes = []byte("0")
+)
+
+// int64Amount represents a fixed precision numerator and arbitrary scale exponent. It is faster
+// than operations on inf.Dec for values that can be represented as int64.
+// +k8s:openapi-gen=true
+type int64Amount struct {
+ value int64
+ scale Scale
+}
+
+// Sign returns 0 if the value is zero, -1 if it is less than 0, or 1 if it is greater than 0.
+func (a int64Amount) Sign() int {
+ switch {
+ case a.value == 0:
+ return 0
+ case a.value > 0:
+ return 1
+ default:
+ return -1
+ }
+}
+
+// AsInt64 returns the current amount as an int64 at scale 0, or false if the value cannot be
+// represented in an int64 OR would result in a loss of precision. This method is intended as
+// an optimization to avoid calling AsDec.
+func (a int64Amount) AsInt64() (int64, bool) {
+ if a.scale == 0 {
+ return a.value, true
+ }
+ if a.scale < 0 {
+ // TODO: attempt to reduce factors, although it is assumed that factors are reduced prior
+ // to the int64Amount being created.
+ return 0, false
+ }
+ return positiveScaleInt64(a.value, a.scale)
+}
+
+// AsScaledInt64 returns an int64 representing the value of this amount at the specified scale,
+// rounding up, or false if that would result in overflow. (1e20).AsScaledInt64(1) would result
+// in overflow because 1e19 is not representable as an int64. Note that setting a scale larger
+// than the current value may result in loss of precision - i.e. (1e-6).AsScaledInt64(0) would
+// return 1, because 0.000001 is rounded up to 1.
+func (a int64Amount) AsScaledInt64(scale Scale) (result int64, ok bool) {
+ if a.scale < scale {
+ result, _ = negativeScaleInt64(a.value, scale-a.scale)
+ return result, true
+ }
+ return positiveScaleInt64(a.value, a.scale-scale)
+}
+
+// AsDec returns an inf.Dec representation of this value.
+func (a int64Amount) AsDec() *inf.Dec {
+ var base inf.Dec
+ base.SetUnscaled(a.value)
+ base.SetScale(inf.Scale(-a.scale))
+ return &base
+}
+
+// Cmp returns 0 if a and b are equal, 1 if a is greater than b, or -1 if a is less than b.
+func (a int64Amount) Cmp(b int64Amount) int {
+ switch {
+ case a.scale == b.scale:
+ // compare only the unscaled portion
+ case a.scale > b.scale:
+ result, remainder, exact := divideByScaleInt64(b.value, a.scale-b.scale)
+ if !exact {
+ return a.AsDec().Cmp(b.AsDec())
+ }
+ if result == a.value {
+ switch {
+ case remainder == 0:
+ return 0
+ case remainder > 0:
+ return -1
+ default:
+ return 1
+ }
+ }
+ b.value = result
+ default:
+ result, remainder, exact := divideByScaleInt64(a.value, b.scale-a.scale)
+ if !exact {
+ return a.AsDec().Cmp(b.AsDec())
+ }
+ if result == b.value {
+ switch {
+ case remainder == 0:
+ return 0
+ case remainder > 0:
+ return 1
+ default:
+ return -1
+ }
+ }
+ a.value = result
+ }
+
+ switch {
+ case a.value == b.value:
+ return 0
+ case a.value < b.value:
+ return -1
+ default:
+ return 1
+ }
+}
+
+// Add adds two int64Amounts together, matching scales. It will return false and not mutate
+// a if overflow or underflow would result.
+func (a *int64Amount) Add(b int64Amount) bool {
+ switch {
+ case b.value == 0:
+ return true
+ case a.value == 0:
+ a.value = b.value
+ a.scale = b.scale
+ return true
+ case a.scale == b.scale:
+ c, ok := int64Add(a.value, b.value)
+ if !ok {
+ return false
+ }
+ a.value = c
+ case a.scale > b.scale:
+ c, ok := positiveScaleInt64(a.value, a.scale-b.scale)
+ if !ok {
+ return false
+ }
+ c, ok = int64Add(c, b.value)
+ if !ok {
+ return false
+ }
+ a.scale = b.scale
+ a.value = c
+ default:
+ c, ok := positiveScaleInt64(b.value, b.scale-a.scale)
+ if !ok {
+ return false
+ }
+ c, ok = int64Add(a.value, c)
+ if !ok {
+ return false
+ }
+ a.value = c
+ }
+ return true
+}
+
+// Sub removes the value of b from the current amount, or returns false if underflow would result.
+func (a *int64Amount) Sub(b int64Amount) bool {
+ return a.Add(int64Amount{value: -b.value, scale: b.scale})
+}
+
+// AsScale adjusts this amount to set a minimum scale, rounding up, and returns true iff no precision
+// was lost. (1.1e5).AsScale(5) would return 1.1e5, but (1.1e5).AsScale(6) would return 1e6.
+func (a int64Amount) AsScale(scale Scale) (int64Amount, bool) {
+ if a.scale >= scale {
+ return a, true
+ }
+ result, exact := negativeScaleInt64(a.value, scale-a.scale)
+ return int64Amount{value: result, scale: scale}, exact
+}
+
+// AsCanonicalBytes accepts a buffer to write the base-10 string value of this field to, and returns
+// either that buffer or a larger buffer and the current exponent of the value. The value is adjusted
+// until the exponent is a multiple of 3 - i.e. 1.1e5 would return "110", 3.
+func (a int64Amount) AsCanonicalBytes(out []byte) (result []byte, exponent int32) {
+ mantissa := a.value
+ exponent = int32(a.scale)
+
+ amount, times := removeInt64Factors(mantissa, 10)
+ exponent += int32(times)
+
+ // make sure exponent is a multiple of 3
+ var ok bool
+ switch exponent % 3 {
+ case 1, -2:
+ amount, ok = int64MultiplyScale10(amount)
+ if !ok {
+ return infDecAmount{a.AsDec()}.AsCanonicalBytes(out)
+ }
+ exponent = exponent - 1
+ case 2, -1:
+ amount, ok = int64MultiplyScale100(amount)
+ if !ok {
+ return infDecAmount{a.AsDec()}.AsCanonicalBytes(out)
+ }
+ exponent = exponent - 2
+ }
+ return strconv.AppendInt(out, amount, 10), exponent
+}
+
+// AsCanonicalBase1024Bytes accepts a buffer to write the base-1024 string value of this field to, and returns
+// either that buffer or a larger buffer and the current exponent of the value. 2048 is 2 * 1024 ^ 1 and would
+// return []byte("2048"), 1.
+func (a int64Amount) AsCanonicalBase1024Bytes(out []byte) (result []byte, exponent int32) {
+ value, ok := a.AsScaledInt64(0)
+ if !ok {
+ return infDecAmount{a.AsDec()}.AsCanonicalBase1024Bytes(out)
+ }
+ amount, exponent := removeInt64Factors(value, 1024)
+ return strconv.AppendInt(out, amount, 10), exponent
+}
+
+// infDecAmount implements common operations over an inf.Dec that are specific to the quantity
+// representation.
+type infDecAmount struct {
+ *inf.Dec
+}
+
+// AsScale adjusts this amount to set a minimum scale, rounding up, and returns true iff no precision
+// was lost. (1.1e5).AsScale(5) would return 1.1e5, but (1.1e5).AsScale(6) would return 1e6.
+func (a infDecAmount) AsScale(scale Scale) (infDecAmount, bool) {
+ tmp := &inf.Dec{}
+ tmp.Round(a.Dec, scale.infScale(), inf.RoundUp)
+ return infDecAmount{tmp}, tmp.Cmp(a.Dec) == 0
+}
+
+// AsCanonicalBytes accepts a buffer to write the base-10 string value of this field to, and returns
+// either that buffer or a larger buffer and the current exponent of the value. The value is adjusted
+// until the exponent is a multiple of 3 - i.e. 1.1e5 would return "110", 3.
+func (a infDecAmount) AsCanonicalBytes(out []byte) (result []byte, exponent int32) {
+ mantissa := a.Dec.UnscaledBig()
+ exponent = int32(-a.Dec.Scale())
+ amount := big.NewInt(0).Set(mantissa)
+ // move all factors of 10 into the exponent for easy reasoning
+ amount, times := removeBigIntFactors(amount, bigTen)
+ exponent += times
+
+ // make sure exponent is a multiple of 3
+ for exponent%3 != 0 {
+ amount.Mul(amount, bigTen)
+ exponent--
+ }
+
+ return append(out, amount.String()...), exponent
+}
+
+// AsCanonicalBase1024Bytes accepts a buffer to write the base-1024 string value of this field to, and returns
+// either that buffer or a larger buffer and the current exponent of the value. 2048 is 2 * 1024 ^ 1 and would
+// return []byte("2048"), 1.
+func (a infDecAmount) AsCanonicalBase1024Bytes(out []byte) (result []byte, exponent int32) {
+ tmp := &inf.Dec{}
+ tmp.Round(a.Dec, 0, inf.RoundUp)
+ amount, exponent := removeBigIntFactors(tmp.UnscaledBig(), big1024)
+ return append(out, amount.String()...), exponent
+}
diff --git a/vendor/k8s.io/apimachinery/pkg/api/resource/generated.pb.go b/vendor/k8s.io/apimachinery/pkg/api/resource/generated.pb.go
new file mode 100644
index 000000000..8b2e338a7
--- /dev/null
+++ b/vendor/k8s.io/apimachinery/pkg/api/resource/generated.pb.go
@@ -0,0 +1,77 @@
+/*
+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.
+*/
+
+// Code generated by protoc-gen-gogo.
+// source: k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/api/resource/generated.proto
+// DO NOT EDIT!
+
+/*
+ Package resource is a generated protocol buffer package.
+
+ It is generated from these files:
+ k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/api/resource/generated.proto
+
+ It has these top-level messages:
+ Quantity
+*/
+package resource
+
+import proto "github.com/gogo/protobuf/proto"
+import fmt "fmt"
+import math "math"
+
+// Reference imports to suppress errors if they are not otherwise used.
+var _ = proto.Marshal
+var _ = fmt.Errorf
+var _ = math.Inf
+
+// This is a compile-time assertion to ensure that this generated file
+// is compatible with the proto package it is being compiled against.
+// A compilation error at this line likely means your copy of the
+// proto package needs to be updated.
+const _ = proto.GoGoProtoPackageIsVersion2 // please upgrade the proto package
+
+func (m *Quantity) Reset() { *m = Quantity{} }
+func (*Quantity) ProtoMessage() {}
+func (*Quantity) Descriptor() ([]byte, []int) { return fileDescriptorGenerated, []int{0} }
+
+func init() {
+ proto.RegisterType((*Quantity)(nil), "k8s.io.apimachinery.pkg.api.resource.Quantity")
+}
+
+func init() {
+ proto.RegisterFile("k8s.io/kubernetes/vendor/k8s.io/apimachinery/pkg/api/resource/generated.proto", fileDescriptorGenerated)
+}
+
+var fileDescriptorGenerated = []byte{
+ // 255 bytes of a gzipped FileDescriptorProto
+ 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x74, 0x8f, 0xa1, 0x4e, 0x03, 0x41,
+ 0x10, 0x86, 0x77, 0x0d, 0x29, 0x95, 0x0d, 0x21, 0xa4, 0x62, 0xaf, 0x21, 0x08, 0x0c, 0x3b, 0x02,
+ 0xd3, 0x20, 0xf1, 0x08, 0x90, 0xb8, 0xbb, 0xeb, 0xb0, 0xdd, 0x1c, 0xdd, 0xbd, 0xcc, 0xce, 0x92,
+ 0xd4, 0x55, 0x22, 0x2b, 0x91, 0xbd, 0xb7, 0xa9, 0xac, 0xac, 0x40, 0x70, 0xcb, 0x8b, 0x90, 0x5e,
+ 0xdb, 0x84, 0x90, 0xe0, 0xe6, 0xfb, 0x27, 0xdf, 0xe4, 0x9f, 0xfe, 0x43, 0x35, 0x0e, 0xda, 0x7a,
+ 0xa8, 0x62, 0x81, 0xe4, 0x90, 0x31, 0xc0, 0x1b, 0xba, 0x89, 0x27, 0x38, 0x2c, 0xf2, 0xda, 0xce,
+ 0xf2, 0x72, 0x6a, 0x1d, 0xd2, 0x1c, 0xea, 0xca, 0xec, 0x02, 0x20, 0x0c, 0x3e, 0x52, 0x89, 0x60,
+ 0xd0, 0x21, 0xe5, 0x8c, 0x13, 0x5d, 0x93, 0x67, 0x3f, 0xb8, 0xda, 0x5b, 0xfa, 0xb7, 0xa5, 0xeb,
+ 0xca, 0xec, 0x02, 0x7d, 0xb4, 0x86, 0x37, 0xc6, 0xf2, 0x34, 0x16, 0xba, 0xf4, 0x33, 0x30, 0xde,
+ 0x78, 0xe8, 0xe4, 0x22, 0xbe, 0x74, 0xd4, 0x41, 0x37, 0xed, 0x8f, 0x0e, 0x6f, 0xff, 0xab, 0x12,
+ 0xd9, 0xbe, 0x82, 0x75, 0x1c, 0x98, 0xfe, 0x36, 0xb9, 0x1c, 0xf7, 0x7b, 0x8f, 0x31, 0x77, 0x6c,
+ 0x79, 0x3e, 0x38, 0xef, 0x9f, 0x04, 0x26, 0xeb, 0xcc, 0x85, 0x1c, 0xc9, 0xeb, 0xd3, 0xa7, 0x03,
+ 0xdd, 0x9d, 0x7d, 0xac, 0x32, 0xf1, 0xde, 0x64, 0x62, 0xd9, 0x64, 0x62, 0xd5, 0x64, 0x62, 0xf1,
+ 0x39, 0x12, 0xf7, 0x7a, 0xdd, 0x2a, 0xb1, 0x69, 0x95, 0xd8, 0xb6, 0x4a, 0x2c, 0x92, 0x92, 0xeb,
+ 0xa4, 0xe4, 0x26, 0x29, 0xb9, 0x4d, 0x4a, 0x7e, 0x25, 0x25, 0x97, 0xdf, 0x4a, 0x3c, 0xf7, 0x8e,
+ 0xdf, 0xfc, 0x04, 0x00, 0x00, 0xff, 0xff, 0x5f, 0x5e, 0xda, 0xf9, 0x43, 0x01, 0x00, 0x00,
+}
diff --git a/vendor/k8s.io/apimachinery/pkg/api/resource/generated.proto b/vendor/k8s.io/apimachinery/pkg/api/resource/generated.proto
new file mode 100644
index 000000000..608299da4
--- /dev/null
+++ b/vendor/k8s.io/apimachinery/pkg/api/resource/generated.proto
@@ -0,0 +1,94 @@
+/*
+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.
+*/
+
+
+// This file was autogenerated by go-to-protobuf. Do not edit it manually!
+
+syntax = 'proto2';
+
+package k8s.io.apimachinery.pkg.api.resource;
+
+import "k8s.io/apimachinery/pkg/util/intstr/generated.proto";
+
+// Package-wide variables from generator "generated".
+option go_package = "resource";
+
+// Quantity is a fixed-point representation of a number.
+// It provides convenient marshaling/unmarshaling in JSON and YAML,
+// in addition to String() and Int64() accessors.
+//
+// The serialization format is:
+//
+// <quantity> ::= <signedNumber><suffix>
+// (Note that <suffix> may be empty, from the "" case in <decimalSI>.)
+// <digit> ::= 0 | 1 | ... | 9
+// <digits> ::= <digit> | <digit><digits>
+// <number> ::= <digits> | <digits>.<digits> | <digits>. | .<digits>
+// <sign> ::= "+" | "-"
+// <signedNumber> ::= <number> | <sign><number>
+// <suffix> ::= <binarySI> | <decimalExponent> | <decimalSI>
+// <binarySI> ::= Ki | Mi | Gi | Ti | Pi | Ei
+// (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)
+// <decimalSI> ::= m | "" | k | M | G | T | P | E
+// (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)
+// <decimalExponent> ::= "e" <signedNumber> | "E" <signedNumber>
+//
+// No matter which of the three exponent forms is used, no quantity may represent
+// a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal
+// places. Numbers larger or more precise will be capped or rounded up.
+// (E.g.: 0.1m will rounded up to 1m.)
+// This may be extended in the future if we require larger or smaller quantities.
+//
+// When a Quantity is parsed from a string, it will remember the type of suffix
+// it had, and will use the same type again when it is serialized.
+//
+// Before serializing, Quantity will be put in "canonical form".
+// This means that Exponent/suffix will be adjusted up or down (with a
+// corresponding increase or decrease in Mantissa) such that:
+// a. No precision is lost
+// b. No fractional digits will be emitted
+// c. The exponent (or suffix) is as large as possible.
+// The sign will be omitted unless the number is negative.
+//
+// Examples:
+// 1.5 will be serialized as "1500m"
+// 1.5Gi will be serialized as "1536Mi"
+//
+// NOTE: We reserve the right to amend this canonical format, perhaps to
+// allow 1.5 to be canonical.
+// TODO: Remove above disclaimer after all bikeshedding about format is over,
+// or after March 2015.
+//
+// Note that the quantity will NEVER be internally represented by a
+// floating point number. That is the whole point of this exercise.
+//
+// Non-canonical values will still parse as long as they are well formed,
+// but will be re-emitted in their canonical form. (So always use canonical
+// form, or don't diff.)
+//
+// This format is intended to make it difficult to use these numbers without
+// writing some sort of special handling code in the hopes that that will
+// cause implementors to also use a fixed point implementation.
+//
+// +protobuf=true
+// +protobuf.embed=string
+// +protobuf.options.marshal=false
+// +protobuf.options.(gogoproto.goproto_stringer)=false
+// +k8s:openapi-gen=true
+message Quantity {
+ optional string string = 1;
+}
+
diff --git a/vendor/k8s.io/apimachinery/pkg/api/resource/math.go b/vendor/k8s.io/apimachinery/pkg/api/resource/math.go
new file mode 100644
index 000000000..72d3880c0
--- /dev/null
+++ b/vendor/k8s.io/apimachinery/pkg/api/resource/math.go
@@ -0,0 +1,314 @@
+/*
+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 resource
+
+import (
+ "math/big"
+
+ inf "gopkg.in/inf.v0"
+)
+
+const (
+ // maxInt64Factors is the highest value that will be checked when removing factors of 10 from an int64.
+ // It is also the maximum decimal digits that can be represented with an int64.
+ maxInt64Factors = 18
+)
+
+var (
+ // Commonly needed big.Int values-- treat as read only!
+ bigTen = big.NewInt(10)
+ bigZero = big.NewInt(0)
+ bigOne = big.NewInt(1)
+ bigThousand = big.NewInt(1000)
+ big1024 = big.NewInt(1024)
+
+ // Commonly needed inf.Dec values-- treat as read only!
+ decZero = inf.NewDec(0, 0)
+ decOne = inf.NewDec(1, 0)
+ decMinusOne = inf.NewDec(-1, 0)
+ decThousand = inf.NewDec(1000, 0)
+ dec1024 = inf.NewDec(1024, 0)
+ decMinus1024 = inf.NewDec(-1024, 0)
+
+ // Largest (in magnitude) number allowed.
+ maxAllowed = infDecAmount{inf.NewDec((1<<63)-1, 0)} // == max int64
+
+ // The maximum value we can represent milli-units for.
+ // Compare with the return value of Quantity.Value() to
+ // see if it's safe to use Quantity.MilliValue().
+ MaxMilliValue = int64(((1 << 63) - 1) / 1000)
+)
+
+const mostNegative = -(mostPositive + 1)
+const mostPositive = 1<<63 - 1
+
+// int64Add returns a+b, or false if that would overflow int64.
+func int64Add(a, b int64) (int64, bool) {
+ c := a + b
+ switch {
+ case a > 0 && b > 0:
+ if c < 0 {
+ return 0, false
+ }
+ case a < 0 && b < 0:
+ if c > 0 {
+ return 0, false
+ }
+ if a == mostNegative && b == mostNegative {
+ return 0, false
+ }
+ }
+ return c, true
+}
+
+// int64Multiply returns a*b, or false if that would overflow or underflow int64.
+func int64Multiply(a, b int64) (int64, bool) {
+ if a == 0 || b == 0 || a == 1 || b == 1 {
+ return a * b, true
+ }
+ if a == mostNegative || b == mostNegative {
+ return 0, false
+ }
+ c := a * b
+ return c, c/b == a
+}
+
+// int64MultiplyScale returns a*b, assuming b is greater than one, or false if that would overflow or underflow int64.
+// Use when b is known to be greater than one.
+func int64MultiplyScale(a int64, b int64) (int64, bool) {
+ if a == 0 || a == 1 {
+ return a * b, true
+ }
+ if a == mostNegative && b != 1 {
+ return 0, false
+ }
+ c := a * b
+ return c, c/b == a
+}
+
+// int64MultiplyScale10 multiplies a by 10, or returns false if that would overflow. This method is faster than
+// int64Multiply(a, 10) because the compiler can optimize constant factor multiplication.
+func int64MultiplyScale10(a int64) (int64, bool) {
+ if a == 0 || a == 1 {
+ return a * 10, true
+ }
+ if a == mostNegative {
+ return 0, false
+ }
+ c := a * 10
+ return c, c/10 == a
+}
+
+// int64MultiplyScale100 multiplies a by 100, or returns false if that would overflow. This method is faster than
+// int64Multiply(a, 100) because the compiler can optimize constant factor multiplication.
+func int64MultiplyScale100(a int64) (int64, bool) {
+ if a == 0 || a == 1 {
+ return a * 100, true
+ }
+ if a == mostNegative {
+ return 0, false
+ }
+ c := a * 100
+ return c, c/100 == a
+}
+
+// int64MultiplyScale1000 multiplies a by 1000, or returns false if that would overflow. This method is faster than
+// int64Multiply(a, 1000) because the compiler can optimize constant factor multiplication.
+func int64MultiplyScale1000(a int64) (int64, bool) {
+ if a == 0 || a == 1 {
+ return a * 1000, true
+ }
+ if a == mostNegative {
+ return 0, false
+ }
+ c := a * 1000
+ return c, c/1000 == a
+}
+
+// positiveScaleInt64 multiplies base by 10^scale, returning false if the
+// value overflows. Passing a negative scale is undefined.
+func positiveScaleInt64(base int64, scale Scale) (int64, bool) {
+ switch scale {
+ case 0:
+ return base, true
+ case 1:
+ return int64MultiplyScale10(base)
+ case 2:
+ return int64MultiplyScale100(base)
+ case 3:
+ return int64MultiplyScale1000(base)
+ case 6:
+ return int64MultiplyScale(base, 1000000)
+ case 9:
+ return int64MultiplyScale(base, 1000000000)
+ default:
+ value := base
+ var ok bool
+ for i := Scale(0); i < scale; i++ {
+ if value, ok = int64MultiplyScale(value, 10); !ok {
+ return 0, false
+ }
+ }
+ return value, true
+ }
+}
+
+// negativeScaleInt64 reduces base by the provided scale, rounding up, until the
+// value is zero or the scale is reached. Passing a negative scale is undefined.
+// The value returned, if not exact, is rounded away from zero.
+func negativeScaleInt64(base int64, scale Scale) (result int64, exact bool) {
+ if scale == 0 {
+ return base, true
+ }
+
+ value := base
+ var fraction bool
+ for i := Scale(0); i < scale; i++ {
+ if !fraction && value%10 != 0 {
+ fraction = true
+ }
+ value = value / 10
+ if value == 0 {
+ if fraction {
+ if base > 0 {
+ return 1, false
+ }
+ return -1, false
+ }
+ return 0, true
+ }
+ }
+ if fraction {
+ if base > 0 {
+ value += 1
+ } else {
+ value += -1
+ }
+ }
+ return value, !fraction
+}
+
+func pow10Int64(b int64) int64 {
+ switch b {
+ case 0:
+ return 1
+ case 1:
+ return 10
+ case 2:
+ return 100
+ case 3:
+ return 1000
+ case 4:
+ return 10000
+ case 5:
+ return 100000
+ case 6:
+ return 1000000
+ case 7:
+ return 10000000
+ case 8:
+ return 100000000
+ case 9:
+ return 1000000000
+ case 10:
+ return 10000000000
+ case 11:
+ return 100000000000
+ case 12:
+ return 1000000000000
+ case 13:
+ return 10000000000000
+ case 14:
+ return 100000000000000
+ case 15:
+ return 1000000000000000
+ case 16:
+ return 10000000000000000
+ case 17:
+ return 100000000000000000
+ case 18:
+ return 1000000000000000000
+ default:
+ return 0
+ }
+}
+
+// negativeScaleInt64 returns the result of dividing base by scale * 10 and the remainder, or
+// false if no such division is possible. Dividing by negative scales is undefined.
+func divideByScaleInt64(base int64, scale Scale) (result, remainder int64, exact bool) {
+ if scale == 0 {
+ return base, 0, true
+ }
+ // the max scale representable in base 10 in an int64 is 18 decimal places
+ if scale >= 18 {
+ return 0, base, false
+ }
+ divisor := pow10Int64(int64(scale))
+ return base / divisor, base % divisor, true
+}
+
+// removeInt64Factors divides in a loop; the return values have the property that
+// value == result * base ^ scale
+func removeInt64Factors(value int64, base int64) (result int64, times int32) {
+ times = 0
+ result = value
+ negative := result < 0
+ if negative {
+ result = -result
+ }
+ switch base {
+ // allow the compiler to optimize the common cases
+ case 10:
+ for result >= 10 && result%10 == 0 {
+ times++
+ result = result / 10
+ }
+ // allow the compiler to optimize the common cases
+ case 1024:
+ for result >= 1024 && result%1024 == 0 {
+ times++
+ result = result / 1024
+ }
+ default:
+ for result >= base && result%base == 0 {
+ times++
+ result = result / base
+ }
+ }
+ if negative {
+ result = -result
+ }
+ return result, times
+}
+
+// removeBigIntFactors divides in a loop; the return values have the property that
+// d == result * factor ^ times
+// d may be modified in place.
+// If d == 0, then the return values will be (0, 0)
+func removeBigIntFactors(d, factor *big.Int) (result *big.Int, times int32) {
+ q := big.NewInt(0)
+ m := big.NewInt(0)
+ for d.Cmp(bigZero) != 0 {
+ q.DivMod(d, factor, m)
+ if m.Cmp(bigZero) != 0 {
+ break
+ }
+ times++
+ d, q = q, d
+ }
+ return d, times
+}
diff --git a/vendor/k8s.io/apimachinery/pkg/api/resource/quantity.go b/vendor/k8s.io/apimachinery/pkg/api/resource/quantity.go
new file mode 100644
index 000000000..3a9560882
--- /dev/null
+++ b/vendor/k8s.io/apimachinery/pkg/api/resource/quantity.go
@@ -0,0 +1,792 @@
+/*
+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 resource
+
+import (
+ "bytes"
+ "errors"
+ "fmt"
+ "math/big"
+ "regexp"
+ "strconv"
+ "strings"
+
+ flag "github.com/spf13/pflag"
+
+ "github.com/go-openapi/spec"
+ inf "gopkg.in/inf.v0"
+ "k8s.io/apimachinery/pkg/openapi"
+)
+
+// Quantity is a fixed-point representation of a number.
+// It provides convenient marshaling/unmarshaling in JSON and YAML,
+// in addition to String() and Int64() accessors.
+//
+// The serialization format is:
+//
+// <quantity> ::= <signedNumber><suffix>
+// (Note that <suffix> may be empty, from the "" case in <decimalSI>.)
+// <digit> ::= 0 | 1 | ... | 9
+// <digits> ::= <digit> | <digit><digits>
+// <number> ::= <digits> | <digits>.<digits> | <digits>. | .<digits>
+// <sign> ::= "+" | "-"
+// <signedNumber> ::= <number> | <sign><number>
+// <suffix> ::= <binarySI> | <decimalExponent> | <decimalSI>
+// <binarySI> ::= Ki | Mi | Gi | Ti | Pi | Ei
+// (International System of units; See: http://physics.nist.gov/cuu/Units/binary.html)
+// <decimalSI> ::= m | "" | k | M | G | T | P | E
+// (Note that 1024 = 1Ki but 1000 = 1k; I didn't choose the capitalization.)
+// <decimalExponent> ::= "e" <signedNumber> | "E" <signedNumber>
+//
+// No matter which of the three exponent forms is used, no quantity may represent
+// a number greater than 2^63-1 in magnitude, nor may it have more than 3 decimal
+// places. Numbers larger or more precise will be capped or rounded up.
+// (E.g.: 0.1m will rounded up to 1m.)
+// This may be extended in the future if we require larger or smaller quantities.
+//
+// When a Quantity is parsed from a string, it will remember the type of suffix
+// it had, and will use the same type again when it is serialized.
+//
+// Before serializing, Quantity will be put in "canonical form".
+// This means that Exponent/suffix will be adjusted up or down (with a
+// corresponding increase or decrease in Mantissa) such that:
+// a. No precision is lost
+// b. No fractional digits will be emitted
+// c. The exponent (or suffix) is as large as possible.
+// The sign will be omitted unless the number is negative.
+//
+// Examples:
+// 1.5 will be serialized as "1500m"
+// 1.5Gi will be serialized as "1536Mi"
+//
+// NOTE: We reserve the right to amend this canonical format, perhaps to
+// allow 1.5 to be canonical.
+// TODO: Remove above disclaimer after all bikeshedding about format is over,
+// or after March 2015.
+//
+// Note that the quantity will NEVER be internally represented by a
+// floating point number. That is the whole point of this exercise.
+//
+// Non-canonical values will still parse as long as they are well formed,
+// but will be re-emitted in their canonical form. (So always use canonical
+// form, or don't diff.)
+//
+// This format is intended to make it difficult to use these numbers without
+// writing some sort of special handling code in the hopes that that will
+// cause implementors to also use a fixed point implementation.
+//
+// +protobuf=true
+// +protobuf.embed=string
+// +protobuf.options.marshal=false
+// +protobuf.options.(gogoproto.goproto_stringer)=false
+// +k8s:openapi-gen=true
+type Quantity struct {
+ // i is the quantity in int64 scaled form, if d.Dec == nil
+ i int64Amount
+ // d is the quantity in inf.Dec form if d.Dec != nil
+ d infDecAmount
+ // s is the generated value of this quantity to avoid recalculation
+ s string
+
+ // Change Format at will. See the comment for Canonicalize for
+ // more details.
+ Format
+}
+
+// CanonicalValue allows a quantity amount to be converted to a string.
+type CanonicalValue interface {
+ // AsCanonicalBytes returns a byte array representing the string representation
+ // of the value mantissa and an int32 representing its exponent in base-10. Callers may
+ // pass a byte slice to the method to avoid allocations.
+ AsCanonicalBytes(out []byte) ([]byte, int32)
+ // AsCanonicalBase1024Bytes returns a byte array representing the string representation
+ // of the value mantissa and an int32 representing its exponent in base-1024. Callers
+ // may pass a byte slice to the method to avoid allocations.
+ AsCanonicalBase1024Bytes(out []byte) ([]byte, int32)
+}
+
+// Format lists the three possible formattings of a quantity.
+type Format string
+
+const (
+ DecimalExponent = Format("DecimalExponent") // e.g., 12e6
+ BinarySI = Format("BinarySI") // e.g., 12Mi (12 * 2^20)
+ DecimalSI = Format("DecimalSI") // e.g., 12M (12 * 10^6)
+)
+
+// MustParse turns the given string into a quantity or panics; for tests
+// or others cases where you know the string is valid.
+func MustParse(str string) Quantity {
+ q, err := ParseQuantity(str)
+ if err != nil {
+ panic(fmt.Errorf("cannot parse '%v': %v", str, err))
+ }
+ return q
+}
+
+const (
+ // splitREString is used to separate a number from its suffix; as such,
+ // this is overly permissive, but that's OK-- it will be checked later.
+ splitREString = "^([+-]?[0-9.]+)([eEinumkKMGTP]*[-+]?[0-9]*)$"
+)
+
+var (
+ // splitRE is used to get the various parts of a number.
+ splitRE = regexp.MustCompile(splitREString)
+
+ // Errors that could happen while parsing a string.
+ ErrFormatWrong = errors.New("quantities must match the regular expression '" + splitREString + "'")
+ ErrNumeric = errors.New("unable to parse numeric part of quantity")
+ ErrSuffix = errors.New("unable to parse quantity's suffix")
+)
+
+// parseQuantityString is a fast scanner for quantity values.
+func parseQuantityString(str string) (positive bool, value, num, denom, suffix string, err error) {
+ positive = true
+ pos := 0
+ end := len(str)
+
+ // handle leading sign
+ if pos < end {
+ switch str[0] {
+ case '-':
+ positive = false
+ pos++
+ case '+':
+ pos++
+ }
+ }
+
+ // strip leading zeros
+Zeroes:
+ for i := pos; ; i++ {
+ if i >= end {
+ num = "0"
+ value = num
+ return
+ }
+ switch str[i] {
+ case '0':
+ pos++
+ default:
+ break Zeroes
+ }
+ }
+
+ // extract the numerator
+Num:
+ for i := pos; ; i++ {
+ if i >= end {
+ num = str[pos:end]
+ value = str[0:end]
+ return
+ }
+ switch str[i] {
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ default:
+ num = str[pos:i]
+ pos = i
+ break Num
+ }
+ }
+
+ // if we stripped all numerator positions, always return 0
+ if len(num) == 0 {
+ num = "0"
+ }
+
+ // handle a denominator
+ if pos < end && str[pos] == '.' {
+ pos++
+ Denom:
+ for i := pos; ; i++ {
+ if i >= end {
+ denom = str[pos:end]
+ value = str[0:end]
+ return
+ }
+ switch str[i] {
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ default:
+ denom = str[pos:i]
+ pos = i
+ break Denom
+ }
+ }
+ // TODO: we currently allow 1.G, but we may not want to in the future.
+ // if len(denom) == 0 {
+ // err = ErrFormatWrong
+ // return
+ // }
+ }
+ value = str[0:pos]
+
+ // grab the elements of the suffix
+ suffixStart := pos
+ for i := pos; ; i++ {
+ if i >= end {
+ suffix = str[suffixStart:end]
+ return
+ }
+ if !strings.ContainsAny(str[i:i+1], "eEinumkKMGTP") {
+ pos = i
+ break
+ }
+ }
+ if pos < end {
+ switch str[pos] {
+ case '-', '+':
+ pos++
+ }
+ }
+Suffix:
+ for i := pos; ; i++ {
+ if i >= end {
+ suffix = str[suffixStart:end]
+ return
+ }
+ switch str[i] {
+ case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
+ default:
+ break Suffix
+ }
+ }
+ // we encountered a non decimal in the Suffix loop, but the last character
+ // was not a valid exponent
+ err = ErrFormatWrong
+ return
+}
+
+// ParseQuantity turns str into a Quantity, or returns an error.
+func ParseQuantity(str string) (Quantity, error) {
+ if len(str) == 0 {
+ return Quantity{}, ErrFormatWrong
+ }
+ if str == "0" {
+ return Quantity{Format: DecimalSI, s: str}, nil
+ }
+
+ positive, value, num, denom, suf, err := parseQuantityString(str)
+ if err != nil {
+ return Quantity{}, err
+ }
+
+ base, exponent, format, ok := quantitySuffixer.interpret(suffix(suf))
+ if !ok {
+ return Quantity{}, ErrSuffix
+ }
+
+ precision := int32(0)
+ scale := int32(0)
+ mantissa := int64(1)
+ switch format {
+ case DecimalExponent, DecimalSI:
+ scale = exponent
+ precision = maxInt64Factors - int32(len(num)+len(denom))
+ case BinarySI:
+ scale = 0
+ switch {
+ case exponent >= 0 && len(denom) == 0:
+ // only handle positive binary numbers with the fast path
+ mantissa = int64(int64(mantissa) << uint64(exponent))
+ // 1Mi (2^20) has ~6 digits of decimal precision, so exponent*3/10 -1 is roughly the precision
+ precision = 15 - int32(len(num)) - int32(float32(exponent)*3/10) - 1
+ default:
+ precision = -1
+ }
+ }
+
+ if precision >= 0 {
+ // if we have a denominator, shift the entire value to the left by the number of places in the
+ // denominator
+ scale -= int32(len(denom))
+ if scale >= int32(Nano) {
+ shifted := num + denom
+
+ var value int64
+ value, err := strconv.ParseInt(shifted, 10, 64)
+ if err != nil {
+ return Quantity{}, ErrNumeric
+ }
+ if result, ok := int64Multiply(value, int64(mantissa)); ok {
+ if !positive {
+ result = -result
+ }
+ // if the number is in canonical form, reuse the string
+ switch format {
+ case BinarySI:
+ if exponent%10 == 0 && (value&0x07 != 0) {
+ return Quantity{i: int64Amount{value: result, scale: Scale(scale)}, Format: format, s: str}, nil
+ }
+ default:
+ if scale%3 == 0 && !strings.HasSuffix(shifted, "000") && shifted[0] != '0' {
+ return Quantity{i: int64Amount{value: result, scale: Scale(scale)}, Format: format, s: str}, nil
+ }
+ }
+ return Quantity{i: int64Amount{value: result, scale: Scale(scale)}, Format: format}, nil
+ }
+ }
+ }
+
+ amount := new(inf.Dec)
+ if _, ok := amount.SetString(value); !ok {
+ return Quantity{}, ErrNumeric
+ }
+
+ // So that no one but us has to think about suffixes, remove it.
+ if base == 10 {
+ amount.SetScale(amount.Scale() + Scale(exponent).infScale())
+ } else if base == 2 {
+ // numericSuffix = 2 ** exponent
+ numericSuffix := big.NewInt(1).Lsh(bigOne, uint(exponent))
+ ub := amount.UnscaledBig()
+ amount.SetUnscaledBig(ub.Mul(ub, numericSuffix))
+ }
+
+ // Cap at min/max bounds.
+ sign := amount.Sign()
+ if sign == -1 {
+ amount.Neg(amount)
+ }
+
+ // This rounds non-zero values up to the minimum representable value, under the theory that
+ // if you want some resources, you should get some resources, even if you asked for way too small
+ // of an amount. Arguably, this should be inf.RoundHalfUp (normal rounding), but that would have
+ // the side effect of rounding values < .5n to zero.
+ if v, ok := amount.Unscaled(); v != int64(0) || !ok {
+ amount.Round(amount, Nano.infScale(), inf.RoundUp)
+ }
+
+ // The max is just a simple cap.
+ // TODO: this prevents accumulating quantities greater than int64, for instance quota across a cluster
+ if format == BinarySI && amount.Cmp(maxAllowed.Dec) > 0 {
+ amount.Set(maxAllowed.Dec)
+ }
+
+ if format == BinarySI && amount.Cmp(decOne) < 0 && amount.Cmp(decZero) > 0 {
+ // This avoids rounding and hopefully confusion, too.
+ format = DecimalSI
+ }
+ if sign == -1 {
+ amount.Neg(amount)
+ }
+
+ return Quantity{d: infDecAmount{amount}, Format: format}, nil
+}
+
+// DeepCopy returns a deep-copy of the Quantity value. Note that the method
+// receiver is a value, so we can mutate it in-place and return it.
+func (q Quantity) DeepCopy() Quantity {
+ if q.d.Dec != nil {
+ tmp := &inf.Dec{}
+ q.d.Dec = tmp.Set(q.d.Dec)
+ }
+ return q
+}
+
+// OpenAPIDefinition returns openAPI definition for this type.
+func (_ Quantity) OpenAPIDefinition() openapi.OpenAPIDefinition {
+ return openapi.OpenAPIDefinition{
+ Schema: spec.Schema{
+ SchemaProps: spec.SchemaProps{
+ Type: []string{"string"},
+ Format: "",
+ },
+ },
+ }
+}
+
+// CanonicalizeBytes returns the canonical form of q and its suffix (see comment on Quantity).
+//
+// Note about BinarySI:
+// * If q.Format is set to BinarySI and q.Amount represents a non-zero value between
+// -1 and +1, it will be emitted as if q.Format were DecimalSI.
+// * Otherwise, if q.Format is set to BinarySI, frational parts of q.Amount will be
+// rounded up. (1.1i becomes 2i.)
+func (q *Quantity) CanonicalizeBytes(out []byte) (result, suffix []byte) {
+ if q.IsZero() {
+ return zeroBytes, nil
+ }
+
+ var rounded CanonicalValue
+ format := q.Format
+ switch format {
+ case DecimalExponent, DecimalSI:
+ case BinarySI:
+ if q.CmpInt64(-1024) > 0 && q.CmpInt64(1024) < 0 {
+ // This avoids rounding and hopefully confusion, too.
+ format = DecimalSI
+ } else {
+ var exact bool
+ if rounded, exact = q.AsScale(0); !exact {
+ // Don't lose precision-- show as DecimalSI
+ format = DecimalSI
+ }
+ }
+ default:
+ format = DecimalExponent
+ }
+
+ // TODO: If BinarySI formatting is requested but would cause rounding, upgrade to
+ // one of the other formats.
+ switch format {
+ case DecimalExponent, DecimalSI:
+ number, exponent := q.AsCanonicalBytes(out)
+ suffix, _ := quantitySuffixer.constructBytes(10, exponent, format)
+ return number, suffix
+ default:
+ // format must be BinarySI
+ number, exponent := rounded.AsCanonicalBase1024Bytes(out)
+ suffix, _ := quantitySuffixer.constructBytes(2, exponent*10, format)
+ return number, suffix
+ }
+}
+
+// AsInt64 returns a representation of the current value as an int64 if a fast conversion
+// is possible. If false is returned, callers must use the inf.Dec form of this quantity.
+func (q *Quantity) AsInt64() (int64, bool) {
+ if q.d.Dec != nil {
+ return 0, false
+ }
+ return q.i.AsInt64()
+}
+
+// ToDec promotes the quantity in place to use an inf.Dec representation and returns itself.
+func (q *Quantity) ToDec() *Quantity {
+ if q.d.Dec == nil {
+ q.d.Dec = q.i.AsDec()
+ q.i = int64Amount{}
+ }
+ return q
+}
+
+// AsDec returns the quantity as represented by a scaled inf.Dec.
+func (q *Quantity) AsDec() *inf.Dec {
+ if q.d.Dec != nil {
+ return q.d.Dec
+ }
+ q.d.Dec = q.i.AsDec()
+ q.i = int64Amount{}
+ return q.d.Dec
+}
+
+// AsCanonicalBytes returns the canonical byte representation of this quantity as a mantissa
+// and base 10 exponent. The out byte slice may be passed to the method to avoid an extra
+// allocation.
+func (q *Quantity) AsCanonicalBytes(out []byte) (result []byte, exponent int32) {
+ if q.d.Dec != nil {
+ return q.d.AsCanonicalBytes(out)
+ }
+ return q.i.AsCanonicalBytes(out)
+}
+
+// IsZero returns true if the quantity is equal to zero.
+func (q *Quantity) IsZero() bool {
+ if q.d.Dec != nil {
+ return q.d.Dec.Sign() == 0
+ }
+ return q.i.value == 0
+}
+
+// Sign returns 0 if the quantity is zero, -1 if the quantity is less than zero, or 1 if the
+// quantity is greater than zero.
+func (q *Quantity) Sign() int {
+ if q.d.Dec != nil {
+ return q.d.Dec.Sign()
+ }
+ return q.i.Sign()
+}
+
+// AsScaled returns the current value, rounded up to the provided scale, and returns
+// false if the scale resulted in a loss of precision.
+func (q *Quantity) AsScale(scale Scale) (CanonicalValue, bool) {
+ if q.d.Dec != nil {
+ return q.d.AsScale(scale)
+ }
+ return q.i.AsScale(scale)
+}
+
+// RoundUp updates the quantity to the provided scale, ensuring that the value is at
+// least 1. False is returned if the rounding operation resulted in a loss of precision.
+// Negative numbers are rounded away from zero (-9 scale 1 rounds to -10).
+func (q *Quantity) RoundUp(scale Scale) bool {
+ if q.d.Dec != nil {
+ q.s = ""
+ d, exact := q.d.AsScale(scale)
+ q.d = d
+ return exact
+ }
+ // avoid clearing the string value if we have already calculated it
+ if q.i.scale >= scale {
+ return true
+ }
+ q.s = ""
+ i, exact := q.i.AsScale(scale)
+ q.i = i
+ return exact
+}
+
+// Add adds the provide y quantity to the current value. If the current value is zero,
+// the format of the quantity will be updated to the format of y.
+func (q *Quantity) Add(y Quantity) {
+ q.s = ""
+ if q.d.Dec == nil && y.d.Dec == nil {
+ if q.i.value == 0 {
+ q.Format = y.Format
+ }
+ if q.i.Add(y.i) {
+ return
+ }
+ } else if q.IsZero() {
+ q.Format = y.Format
+ }
+ q.ToDec().d.Dec.Add(q.d.Dec, y.AsDec())
+}
+
+// Sub subtracts the provided quantity from the current value in place. If the current
+// value is zero, the format of the quantity will be updated to the format of y.
+func (q *Quantity) Sub(y Quantity) {
+ q.s = ""
+ if q.IsZero() {
+ q.Format = y.Format
+ }
+ if q.d.Dec == nil && y.d.Dec == nil && q.i.Sub(y.i) {
+ return
+ }
+ q.ToDec().d.Dec.Sub(q.d.Dec, y.AsDec())
+}
+
+// Cmp returns 0 if the quantity is equal to y, -1 if the quantity is less than y, or 1 if the
+// quantity is greater than y.
+func (q *Quantity) Cmp(y Quantity) int {
+ if q.d.Dec == nil && y.d.Dec == nil {
+ return q.i.Cmp(y.i)
+ }
+ return q.AsDec().Cmp(y.AsDec())
+}
+
+// CmpInt64 returns 0 if the quantity is equal to y, -1 if the quantity is less than y, or 1 if the
+// quantity is greater than y.
+func (q *Quantity) CmpInt64(y int64) int {
+ if q.d.Dec != nil {
+ return q.d.Dec.Cmp(inf.NewDec(y, inf.Scale(0)))
+ }
+ return q.i.Cmp(int64Amount{value: y})
+}
+
+// Neg sets quantity to be the negative value of itself.
+func (q *Quantity) Neg() {
+ q.s = ""
+ if q.d.Dec == nil {
+ q.i.value = -q.i.value
+ return
+ }
+ q.d.Dec.Neg(q.d.Dec)
+}
+
+// int64QuantityExpectedBytes is the expected width in bytes of the canonical string representation
+// of most Quantity values.
+const int64QuantityExpectedBytes = 18
+
+// String formats the Quantity as a string, caching the result if not calculated.
+// String is an expensive operation and caching this result significantly reduces the cost of
+// normal parse / marshal operations on Quantity.
+func (q *Quantity) String() string {
+ if len(q.s) == 0 {
+ result := make([]byte, 0, int64QuantityExpectedBytes)
+ number, suffix := q.CanonicalizeBytes(result)
+ number = append(number, suffix...)
+ q.s = string(number)
+ }
+ return q.s
+}
+
+// MarshalJSON implements the json.Marshaller interface.
+func (q Quantity) MarshalJSON() ([]byte, error) {
+ if len(q.s) > 0 {
+ out := make([]byte, len(q.s)+2)
+ out[0], out[len(out)-1] = '"', '"'
+ copy(out[1:], q.s)
+ return out, nil
+ }
+ result := make([]byte, int64QuantityExpectedBytes, int64QuantityExpectedBytes)
+ result[0] = '"'
+ number, suffix := q.CanonicalizeBytes(result[1:1])
+ // if the same slice was returned to us that we passed in, avoid another allocation by copying number into
+ // the source slice and returning that
+ if len(number) > 0 && &number[0] == &result[1] && (len(number)+len(suffix)+2) <= int64QuantityExpectedBytes {
+ number = append(number, suffix...)
+ number = append(number, '"')
+ return result[:1+len(number)], nil
+ }
+ // if CanonicalizeBytes needed more space than our slice provided, we may need to allocate again so use
+ // append
+ result = result[:1]
+ result = append(result, number...)
+ result = append(result, suffix...)
+ result = append(result, '"')
+ return result, nil
+}
+
+// UnmarshalJSON implements the json.Unmarshaller interface.
+// TODO: Remove support for leading/trailing whitespace
+func (q *Quantity) UnmarshalJSON(value []byte) error {
+ l := len(value)
+ if l == 4 && bytes.Equal(value, []byte("null")) {
+ q.d.Dec = nil
+ q.i = int64Amount{}
+ return nil
+ }
+ if l >= 2 && value[0] == '"' && value[l-1] == '"' {
+ value = value[1 : l-1]
+ }
+
+ parsed, err := ParseQuantity(strings.TrimSpace(string(value)))
+ if err != nil {
+ return err
+ }
+
+ // This copy is safe because parsed will not be referred to again.
+ *q = parsed
+ return nil
+}
+
+// NewQuantity returns a new Quantity representing the given
+// value in the given format.
+func NewQuantity(value int64, format Format) *Quantity {
+ return &Quantity{
+ i: int64Amount{value: value},
+ Format: format,
+ }
+}
+
+// NewMilliQuantity returns a new Quantity representing the given
+// value * 1/1000 in the given format. Note that BinarySI formatting
+// will round fractional values, and will be changed to DecimalSI for
+// values x where (-1 < x < 1) && (x != 0).
+func NewMilliQuantity(value int64, format Format) *Quantity {
+ return &Quantity{
+ i: int64Amount{value: value, scale: -3},
+ Format: format,
+ }
+}
+
+// NewScaledQuantity returns a new Quantity representing the given
+// value * 10^scale in DecimalSI format.
+func NewScaledQuantity(value int64, scale Scale) *Quantity {
+ return &Quantity{
+ i: int64Amount{value: value, scale: scale},
+ Format: DecimalSI,
+ }
+}
+
+// Value returns the value of q; any fractional part will be lost.
+func (q *Quantity) Value() int64 {
+ return q.ScaledValue(0)
+}
+
+// MilliValue returns the value of ceil(q * 1000); this could overflow an int64;
+// if that's a concern, call Value() first to verify the number is small enough.
+func (q *Quantity) MilliValue() int64 {
+ return q.ScaledValue(Milli)
+}
+
+// ScaledValue returns the value of ceil(q * 10^scale); this could overflow an int64.
+// To detect overflow, call Value() first and verify the expected magnitude.
+func (q *Quantity) ScaledValue(scale Scale) int64 {
+ if q.d.Dec == nil {
+ i, _ := q.i.AsScaledInt64(scale)
+ return i
+ }
+ dec := q.d.Dec
+ return scaledValue(dec.UnscaledBig(), int(dec.Scale()), int(scale.infScale()))
+}
+
+// Set sets q's value to be value.
+func (q *Quantity) Set(value int64) {
+ q.SetScaled(value, 0)
+}
+
+// SetMilli sets q's value to be value * 1/1000.
+func (q *Quantity) SetMilli(value int64) {
+ q.SetScaled(value, Milli)
+}
+
+// SetScaled sets q's value to be value * 10^scale
+func (q *Quantity) SetScaled(value int64, scale Scale) {
+ q.s = ""
+ q.d.Dec = nil
+ q.i = int64Amount{value: value, scale: scale}
+}
+
+// Copy is a convenience function that makes a deep copy for you. Non-deep
+// copies of quantities share pointers and you will regret that.
+func (q *Quantity) Copy() *Quantity {
+ if q.d.Dec == nil {
+ return &Quantity{
+ s: q.s,
+ i: q.i,
+ Format: q.Format,
+ }
+ }
+ tmp := &inf.Dec{}
+ return &Quantity{
+ s: q.s,
+ d: infDecAmount{tmp.Set(q.d.Dec)},
+ Format: q.Format,
+ }
+}
+
+// qFlag is a helper type for the Flag function
+type qFlag struct {
+ dest *Quantity
+}
+
+// Sets the value of the internal Quantity. (used by flag & pflag)
+func (qf qFlag) Set(val string) error {
+ q, err := ParseQuantity(val)
+ if err != nil {
+ return err
+ }
+ // This copy is OK because q will not be referenced again.
+ *qf.dest = q
+ return nil
+}
+
+// Converts the value of the internal Quantity to a string. (used by flag & pflag)
+func (qf qFlag) String() string {
+ return qf.dest.String()
+}
+
+// States the type of flag this is (Quantity). (used by pflag)
+func (qf qFlag) Type() string {
+ return "quantity"
+}
+
+// QuantityFlag is a helper that makes a quantity flag (using standard flag package).
+// Will panic if defaultValue is not a valid quantity.
+func QuantityFlag(flagName, defaultValue, description string) *Quantity {
+ q := MustParse(defaultValue)
+ flag.Var(NewQuantityFlagValue(&q), flagName, description)
+ return &q
+}
+
+// NewQuantityFlagValue returns an object that can be used to back a flag,
+// pointing at the given Quantity variable.
+func NewQuantityFlagValue(q *Quantity) flag.Value {
+ return qFlag{q}
+}
diff --git a/vendor/k8s.io/apimachinery/pkg/api/resource/quantity_proto.go b/vendor/k8s.io/apimachinery/pkg/api/resource/quantity_proto.go
new file mode 100644
index 000000000..74dfb4e4b
--- /dev/null
+++ b/vendor/k8s.io/apimachinery/pkg/api/resource/quantity_proto.go
@@ -0,0 +1,284 @@
+/*
+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 resource
+
+import (
+ "fmt"
+ "io"
+
+ "github.com/gogo/protobuf/proto"
+)
+
+var _ proto.Sizer = &Quantity{}
+
+func (m *Quantity) Marshal() (data []byte, err error) {
+ size := m.Size()
+ data = make([]byte, size)
+ n, err := m.MarshalTo(data)
+ if err != nil {
+ return nil, err
+ }
+ return data[:n], nil
+}
+
+// MarshalTo is a customized version of the generated Protobuf unmarshaler for a struct
+// with a single string field.
+func (m *Quantity) MarshalTo(data []byte) (int, error) {
+ var i int
+ _ = i
+ var l int
+ _ = l
+
+ data[i] = 0xa
+ i++
+ // BEGIN CUSTOM MARSHAL
+ out := m.String()
+ i = encodeVarintGenerated(data, i, uint64(len(out)))
+ i += copy(data[i:], out)
+ // END CUSTOM MARSHAL
+
+ return i, nil
+}
+
+func encodeVarintGenerated(data []byte, offset int, v uint64) int {
+ for v >= 1<<7 {
+ data[offset] = uint8(v&0x7f | 0x80)
+ v >>= 7
+ offset++
+ }
+ data[offset] = uint8(v)
+ return offset + 1
+}
+
+func (m *Quantity) Size() (n int) {
+ var l int
+ _ = l
+
+ // BEGIN CUSTOM SIZE
+ l = len(m.String())
+ // END CUSTOM SIZE
+
+ n += 1 + l + sovGenerated(uint64(l))
+ return n
+}
+
+func sovGenerated(x uint64) (n int) {
+ for {
+ n++
+ x >>= 7
+ if x == 0 {
+ break
+ }
+ }
+ return n
+}
+
+// Unmarshal is a customized version of the generated Protobuf unmarshaler for a struct
+// with a single string field.
+func (m *Quantity) Unmarshal(data []byte) error {
+ l := len(data)
+ iNdEx := 0
+ for iNdEx < l {
+ preIndex := iNdEx
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ fieldNum := int32(wire >> 3)
+ wireType := int(wire & 0x7)
+ if wireType == 4 {
+ return fmt.Errorf("proto: Quantity: wiretype end group for non-group")
+ }
+ if fieldNum <= 0 {
+ return fmt.Errorf("proto: Quantity: illegal tag %d (wire type %d)", fieldNum, wire)
+ }
+ switch fieldNum {
+ case 1:
+ if wireType != 2 {
+ return fmt.Errorf("proto: wrong wireType = %d for field String_", wireType)
+ }
+ var stringLen uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ stringLen |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ intStringLen := int(stringLen)
+ if intStringLen < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ postIndex := iNdEx + intStringLen
+ if postIndex > l {
+ return io.ErrUnexpectedEOF
+ }
+ s := string(data[iNdEx:postIndex])
+
+ // BEGIN CUSTOM DECODE
+ p, err := ParseQuantity(s)
+ if err != nil {
+ return err
+ }
+ *m = p
+ // END CUSTOM DECODE
+
+ iNdEx = postIndex
+ default:
+ iNdEx = preIndex
+ skippy, err := skipGenerated(data[iNdEx:])
+ if err != nil {
+ return err
+ }
+ if skippy < 0 {
+ return ErrInvalidLengthGenerated
+ }
+ if (iNdEx + skippy) > l {
+ return io.ErrUnexpectedEOF
+ }
+ iNdEx += skippy
+ }
+ }
+
+ if iNdEx > l {
+ return io.ErrUnexpectedEOF
+ }
+ return nil
+}
+
+func skipGenerated(data []byte) (n int, err error) {
+ l := len(data)
+ iNdEx := 0
+ for iNdEx < l {
+ var wire uint64
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return 0, ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ wire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ wireType := int(wire & 0x7)
+ switch wireType {
+ case 0:
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return 0, ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ iNdEx++
+ if data[iNdEx-1] < 0x80 {
+ break
+ }
+ }
+ return iNdEx, nil
+ case 1:
+ iNdEx += 8
+ return iNdEx, nil
+ case 2:
+ var length int
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return 0, ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ length |= (int(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ iNdEx += length
+ if length < 0 {
+ return 0, ErrInvalidLengthGenerated
+ }
+ return iNdEx, nil
+ case 3:
+ for {
+ var innerWire uint64
+ var start int = iNdEx
+ for shift := uint(0); ; shift += 7 {
+ if shift >= 64 {
+ return 0, ErrIntOverflowGenerated
+ }
+ if iNdEx >= l {
+ return 0, io.ErrUnexpectedEOF
+ }
+ b := data[iNdEx]
+ iNdEx++
+ innerWire |= (uint64(b) & 0x7F) << shift
+ if b < 0x80 {
+ break
+ }
+ }
+ innerWireType := int(innerWire & 0x7)
+ if innerWireType == 4 {
+ break
+ }
+ next, err := skipGenerated(data[start:])
+ if err != nil {
+ return 0, err
+ }
+ iNdEx = start + next
+ }
+ return iNdEx, nil
+ case 4:
+ return iNdEx, nil
+ case 5:
+ iNdEx += 4
+ return iNdEx, nil
+ default:
+ return 0, fmt.Errorf("proto: illegal wireType %d", wireType)
+ }
+ }
+ panic("unreachable")
+}
+
+var (
+ ErrInvalidLengthGenerated = fmt.Errorf("proto: negative length found during unmarshaling")
+ ErrIntOverflowGenerated = fmt.Errorf("proto: integer overflow")
+)
diff --git a/vendor/k8s.io/apimachinery/pkg/api/resource/scale_int.go b/vendor/k8s.io/apimachinery/pkg/api/resource/scale_int.go
new file mode 100644
index 000000000..55e177b0e
--- /dev/null
+++ b/vendor/k8s.io/apimachinery/pkg/api/resource/scale_int.go
@@ -0,0 +1,95 @@
+/*
+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 resource
+
+import (
+ "math"
+ "math/big"
+ "sync"
+)
+
+var (
+ // A sync pool to reduce allocation.
+ intPool sync.Pool
+ maxInt64 = big.NewInt(math.MaxInt64)
+)
+
+func init() {
+ intPool.New = func() interface{} {
+ return &big.Int{}
+ }
+}
+
+// scaledValue scales given unscaled value from scale to new Scale and returns
+// an int64. It ALWAYS rounds up the result when scale down. The final result might
+// overflow.
+//
+// scale, newScale represents the scale of the unscaled decimal.
+// The mathematical value of the decimal is unscaled * 10**(-scale).
+func scaledValue(unscaled *big.Int, scale, newScale int) int64 {
+ dif := scale - newScale
+ if dif == 0 {
+ return unscaled.Int64()
+ }
+
+ // Handle scale up
+ // This is an easy case, we do not need to care about rounding and overflow.
+ // If any intermediate operation causes overflow, the result will overflow.
+ if dif < 0 {
+ return unscaled.Int64() * int64(math.Pow10(-dif))
+ }
+
+ // Handle scale down
+ // We have to be careful about the intermediate operations.
+
+ // fast path when unscaled < max.Int64 and exp(10,dif) < max.Int64
+ const log10MaxInt64 = 19
+ if unscaled.Cmp(maxInt64) < 0 && dif < log10MaxInt64 {
+ divide := int64(math.Pow10(dif))
+ result := unscaled.Int64() / divide
+ mod := unscaled.Int64() % divide
+ if mod != 0 {
+ return result + 1
+ }
+ return result
+ }
+
+ // We should only convert back to int64 when getting the result.
+ divisor := intPool.Get().(*big.Int)
+ exp := intPool.Get().(*big.Int)
+ result := intPool.Get().(*big.Int)
+ defer func() {
+ intPool.Put(divisor)
+ intPool.Put(exp)
+ intPool.Put(result)
+ }()
+
+ // divisor = 10^(dif)
+ // TODO: create loop up table if exp costs too much.
+ divisor.Exp(bigTen, exp.SetInt64(int64(dif)), nil)
+ // reuse exp
+ remainder := exp
+
+ // result = unscaled / divisor
+ // remainder = unscaled % divisor
+ result.DivMod(unscaled, divisor, remainder)
+ if remainder.Sign() != 0 {
+ return result.Int64() + 1
+ }
+
+ return result.Int64()
+}
diff --git a/vendor/k8s.io/apimachinery/pkg/api/resource/suffix.go b/vendor/k8s.io/apimachinery/pkg/api/resource/suffix.go
new file mode 100644
index 000000000..5ed7abe66
--- /dev/null
+++ b/vendor/k8s.io/apimachinery/pkg/api/resource/suffix.go
@@ -0,0 +1,198 @@
+/*
+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 resource
+
+import (
+ "strconv"
+)
+
+type suffix string
+
+// suffixer can interpret and construct suffixes.
+type suffixer interface {
+ interpret(suffix) (base, exponent int32, fmt Format, ok bool)
+ construct(base, exponent int32, fmt Format) (s suffix, ok bool)
+ constructBytes(base, exponent int32, fmt Format) (s []byte, ok bool)
+}
+
+// quantitySuffixer handles suffixes for all three formats that quantity
+// can handle.
+var quantitySuffixer = newSuffixer()
+
+type bePair struct {
+ base, exponent int32
+}
+
+type listSuffixer struct {
+ suffixToBE map[suffix]bePair
+ beToSuffix map[bePair]suffix
+ beToSuffixBytes map[bePair][]byte
+}
+
+func (ls *listSuffixer) addSuffix(s suffix, pair bePair) {
+ if ls.suffixToBE == nil {
+ ls.suffixToBE = map[suffix]bePair{}
+ }
+ if ls.beToSuffix == nil {
+ ls.beToSuffix = map[bePair]suffix{}
+ }
+ if ls.beToSuffixBytes == nil {
+ ls.beToSuffixBytes = map[bePair][]byte{}
+ }
+ ls.suffixToBE[s] = pair
+ ls.beToSuffix[pair] = s
+ ls.beToSuffixBytes[pair] = []byte(s)
+}
+
+func (ls *listSuffixer) lookup(s suffix) (base, exponent int32, ok bool) {
+ pair, ok := ls.suffixToBE[s]
+ if !ok {
+ return 0, 0, false
+ }
+ return pair.base, pair.exponent, true
+}
+
+func (ls *listSuffixer) construct(base, exponent int32) (s suffix, ok bool) {
+ s, ok = ls.beToSuffix[bePair{base, exponent}]
+ return
+}
+
+func (ls *listSuffixer) constructBytes(base, exponent int32) (s []byte, ok bool) {
+ s, ok = ls.beToSuffixBytes[bePair{base, exponent}]
+ return
+}
+
+type suffixHandler struct {
+ decSuffixes listSuffixer
+ binSuffixes listSuffixer
+}
+
+type fastLookup struct {
+ *suffixHandler
+}
+
+func (l fastLookup) interpret(s suffix) (base, exponent int32, format Format, ok bool) {
+ switch s {
+ case "":
+ return 10, 0, DecimalSI, true
+ case "n":
+ return 10, -9, DecimalSI, true
+ case "u":
+ return 10, -6, DecimalSI, true
+ case "m":
+ return 10, -3, DecimalSI, true
+ case "k":
+ return 10, 3, DecimalSI, true
+ case "M":
+ return 10, 6, DecimalSI, true
+ case "G":
+ return 10, 9, DecimalSI, true
+ }
+ return l.suffixHandler.interpret(s)
+}
+
+func newSuffixer() suffixer {
+ sh := &suffixHandler{}
+
+ // IMPORTANT: if you change this section you must change fastLookup
+
+ sh.binSuffixes.addSuffix("Ki", bePair{2, 10})
+ sh.binSuffixes.addSuffix("Mi", bePair{2, 20})
+ sh.binSuffixes.addSuffix("Gi", bePair{2, 30})
+ sh.binSuffixes.addSuffix("Ti", bePair{2, 40})
+ sh.binSuffixes.addSuffix("Pi", bePair{2, 50})
+ sh.binSuffixes.addSuffix("Ei", bePair{2, 60})
+ // Don't emit an error when trying to produce
+ // a suffix for 2^0.
+ sh.decSuffixes.addSuffix("", bePair{2, 0})
+
+ sh.decSuffixes.addSuffix("n", bePair{10, -9})
+ sh.decSuffixes.addSuffix("u", bePair{10, -6})
+ sh.decSuffixes.addSuffix("m", bePair{10, -3})
+ sh.decSuffixes.addSuffix("", bePair{10, 0})
+ sh.decSuffixes.addSuffix("k", bePair{10, 3})
+ sh.decSuffixes.addSuffix("M", bePair{10, 6})
+ sh.decSuffixes.addSuffix("G", bePair{10, 9})
+ sh.decSuffixes.addSuffix("T", bePair{10, 12})
+ sh.decSuffixes.addSuffix("P", bePair{10, 15})
+ sh.decSuffixes.addSuffix("E", bePair{10, 18})
+
+ return fastLookup{sh}
+}
+
+func (sh *suffixHandler) construct(base, exponent int32, fmt Format) (s suffix, ok bool) {
+ switch fmt {
+ case DecimalSI:
+ return sh.decSuffixes.construct(base, exponent)
+ case BinarySI:
+ return sh.binSuffixes.construct(base, exponent)
+ case DecimalExponent:
+ if base != 10 {
+ return "", false
+ }
+ if exponent == 0 {
+ return "", true
+ }
+ return suffix("e" + strconv.FormatInt(int64(exponent), 10)), true
+ }
+ return "", false
+}
+
+func (sh *suffixHandler) constructBytes(base, exponent int32, format Format) (s []byte, ok bool) {
+ switch format {
+ case DecimalSI:
+ return sh.decSuffixes.constructBytes(base, exponent)
+ case BinarySI:
+ return sh.binSuffixes.constructBytes(base, exponent)
+ case DecimalExponent:
+ if base != 10 {
+ return nil, false
+ }
+ if exponent == 0 {
+ return nil, true
+ }
+ result := make([]byte, 8, 8)
+ result[0] = 'e'
+ number := strconv.AppendInt(result[1:1], int64(exponent), 10)
+ if &result[1] == &number[0] {
+ return result[:1+len(number)], true
+ }
+ result = append(result[:1], number...)
+ return result, true
+ }
+ return nil, false
+}
+
+func (sh *suffixHandler) interpret(suffix suffix) (base, exponent int32, fmt Format, ok bool) {
+ // Try lookup tables first
+ if b, e, ok := sh.decSuffixes.lookup(suffix); ok {
+ return b, e, DecimalSI, true
+ }
+ if b, e, ok := sh.binSuffixes.lookup(suffix); ok {
+ return b, e, BinarySI, true
+ }
+
+ if len(suffix) > 1 && (suffix[0] == 'E' || suffix[0] == 'e') {
+ parsed, err := strconv.ParseInt(string(suffix[1:]), 10, 64)
+ if err != nil {
+ return 0, 0, DecimalExponent, false
+ }
+ return 10, int32(parsed), DecimalExponent, true
+ }
+
+ return 0, 0, DecimalExponent, false
+}
diff --git a/vendor/k8s.io/apimachinery/pkg/api/validation/doc.go b/vendor/k8s.io/apimachinery/pkg/api/validation/doc.go
new file mode 100644
index 000000000..9f20152e4
--- /dev/null
+++ b/vendor/k8s.io/apimachinery/pkg/api/validation/doc.go
@@ -0,0 +1,18 @@
+/*
+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 validation contains generic api type validation functions.
+package validation // import "k8s.io/apimachinery/pkg/api/validation"
diff --git a/vendor/k8s.io/apimachinery/pkg/api/validation/generic.go b/vendor/k8s.io/apimachinery/pkg/api/validation/generic.go
new file mode 100644
index 000000000..348cdc087
--- /dev/null
+++ b/vendor/k8s.io/apimachinery/pkg/api/validation/generic.go
@@ -0,0 +1,85 @@
+/*
+Copyright 2014 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package validation
+
+import (
+ "strings"
+
+ "k8s.io/apimachinery/pkg/util/validation"
+ "k8s.io/apimachinery/pkg/util/validation/field"
+)
+
+const IsNegativeErrorMsg string = `must be greater than or equal to 0`
+
+// ValidateNameFunc validates that the provided name is valid for a given resource type.
+// Not all resources have the same validation rules for names. Prefix is true
+// if the name will have a value appended to it. If the name is not valid,
+// this returns a list of descriptions of individual characteristics of the
+// value that were not valid. Otherwise this returns an empty list or nil.
+type ValidateNameFunc func(name string, prefix bool) []string
+
+// NameIsDNSSubdomain is a ValidateNameFunc for names that must be a DNS subdomain.
+func NameIsDNSSubdomain(name string, prefix bool) []string {
+ if prefix {
+ name = maskTrailingDash(name)
+ }
+ return validation.IsDNS1123Subdomain(name)
+}
+
+// NameIsDNSLabel is a ValidateNameFunc for names that must be a DNS 1123 label.
+func NameIsDNSLabel(name string, prefix bool) []string {
+ if prefix {
+ name = maskTrailingDash(name)
+ }
+ return validation.IsDNS1123Label(name)
+}
+
+// NameIsDNS1035Label is a ValidateNameFunc for names that must be a DNS 952 label.
+func NameIsDNS1035Label(name string, prefix bool) []string {
+ if prefix {
+ name = maskTrailingDash(name)
+ }
+ return validation.IsDNS1035Label(name)
+}
+
+// ValidateNamespaceName can be used to check whether the given namespace name is valid.
+// Prefix indicates this name will be used as part of generation, in which case
+// trailing dashes are allowed.
+var ValidateNamespaceName = NameIsDNSLabel
+
+// ValidateServiceAccountName can be used to check whether the given service account name is valid.
+// Prefix indicates this name will be used as part of generation, in which case
+// trailing dashes are allowed.
+var ValidateServiceAccountName = NameIsDNSSubdomain
+
+// maskTrailingDash replaces the final character of a string with a subdomain safe
+// value if is a dash.
+func maskTrailingDash(name string) string {
+ if strings.HasSuffix(name, "-") {
+ return name[:len(name)-2] + "a"
+ }
+ return name
+}
+
+// Validates that given value is not negative.
+func ValidateNonnegativeField(value int64, fldPath *field.Path) field.ErrorList {
+ allErrs := field.ErrorList{}
+ if value < 0 {
+ allErrs = append(allErrs, field.Invalid(fldPath, value, IsNegativeErrorMsg))
+ }
+ return allErrs
+}
diff --git a/vendor/k8s.io/apimachinery/pkg/api/validation/objectmeta.go b/vendor/k8s.io/apimachinery/pkg/api/validation/objectmeta.go
new file mode 100644
index 000000000..84bd9cded
--- /dev/null
+++ b/vendor/k8s.io/apimachinery/pkg/api/validation/objectmeta.go
@@ -0,0 +1,348 @@
+/*
+Copyright 2014 The Kubernetes Authors.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+package validation
+
+import (
+ "fmt"
+ "strings"
+
+ apiequality "k8s.io/apimachinery/pkg/api/equality"
+ "k8s.io/apimachinery/pkg/api/meta"
+ metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
+ v1validation "k8s.io/apimachinery/pkg/apis/meta/v1/validation"
+ "k8s.io/apimachinery/pkg/runtime/schema"
+ "k8s.io/apimachinery/pkg/util/sets"
+ "k8s.io/apimachinery/pkg/util/validation"
+ "k8s.io/apimachinery/pkg/util/validation/field"
+)
+
+// TODO: delete this global variable when we enable the validation of common
+// fields by default.
+var RepairMalformedUpdates bool = true
+
+const FieldImmutableErrorMsg string = `field is immutable`
+
+const totalAnnotationSizeLimitB int = 256 * (1 << 10) // 256 kB
+
+// BannedOwners is a black list of object that are not allowed to be owners.
+var BannedOwners = map[schema.GroupVersionKind]struct{}{
+ {Group: "", Version: "v1", Kind: "Event"}: {},
+}
+
+// ValidateClusterName can be used to check whether the given cluster name is valid.
+var ValidateClusterName = NameIsDNS1035Label
+
+// ValidateAnnotations validates that a set of annotations are correctly defined.
+func ValidateAnnotations(annotations map[string]string, fldPath *field.Path) field.ErrorList {
+ allErrs := field.ErrorList{}
+ var totalSize int64
+ for k, v := range annotations {
+ for _, msg := range validation.IsQualifiedName(strings.ToLower(k)) {
+ allErrs = append(allErrs, field.Invalid(fldPath, k, msg))
+ }
+ totalSize += (int64)(len(k)) + (int64)(len(v))
+ }
+ if totalSize > (int64)(totalAnnotationSizeLimitB) {
+ allErrs = append(allErrs, field.TooLong(fldPath, "", totalAnnotationSizeLimitB))
+ }
+ return allErrs
+}
+
+func validateOwnerReference(ownerReference metav1.OwnerReference, fldPath *field.Path) field.ErrorList {
+ allErrs := field.ErrorList{}
+ gvk := schema.FromAPIVersionAndKind(ownerReference.APIVersion, ownerReference.Kind)
+ // gvk.Group is empty for the legacy group.
+ if len(gvk.Version) == 0 {
+ allErrs = append(allErrs, field.Invalid(fldPath.Child("apiVersion"), ownerReference.APIVersion, "version must not be empty"))
+ }
+ if len(gvk.Kind) == 0 {
+ allErrs = append(allErrs, field.Invalid(fldPath.Child("kind"), ownerReference.Kind, "kind must not be empty"))
+ }
+ if len(ownerReference.Name) == 0 {
+ allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), ownerReference.Name, "name must not be empty"))
+ }
+ if len(ownerReference.UID) == 0 {
+ allErrs = append(allErrs, field.Invalid(fldPath.Child("uid"), ownerReference.UID, "uid must not be empty"))
+ }
+ if _, ok := BannedOwners[gvk]; ok {
+ allErrs = append(allErrs, field.Invalid(fldPath, ownerReference, fmt.Sprintf("%s is disallowed from being an owner", gvk)))
+ }
+ return allErrs
+}
+
+func ValidateOwnerReferences(ownerReferences []metav1.OwnerReference, fldPath *field.Path) field.ErrorList {
+ allErrs := field.ErrorList{}
+ controllerName := ""
+ for _, ref := range ownerReferences {
+ allErrs = append(allErrs, validateOwnerReference(ref, fldPath)...)
+ if ref.Controller != nil && *ref.Controller {
+ if controllerName != "" {
+ allErrs = append(allErrs, field.Invalid(fldPath, ownerReferences,
+ fmt.Sprintf("Only one reference can have Controller set to true. Found \"true\" in references for %v and %v", controllerName, ref.Name)))
+ } else {
+ controllerName = ref.Name
+ }
+ }
+ }
+ return allErrs
+}
+
+// Validate finalizer names
+func ValidateFinalizerName(stringValue string, fldPath *field.Path) field.ErrorList {
+ allErrs := field.ErrorList{}
+ for _, msg := range validation.IsQualifiedName(stringValue) {
+ allErrs = append(allErrs, field.Invalid(fldPath, stringValue, msg))
+ }
+
+ return allErrs
+}
+
+func ValidateNoNewFinalizers(newFinalizers []string, oldFinalizers []string, fldPath *field.Path) field.ErrorList {
+ allErrs := field.ErrorList{}
+ extra := sets.NewString(newFinalizers...).Difference(sets.NewString(oldFinalizers...))
+ if len(extra) != 0 {
+ allErrs = append(allErrs, field.Forbidden(fldPath, fmt.Sprintf("no new finalizers can be added if the object is being deleted, found new finalizers %#v", extra.List())))
+ }
+ return allErrs
+}
+
+func ValidateImmutableField(newVal, oldVal interface{}, fldPath *field.Path) field.ErrorList {
+ allErrs := field.ErrorList{}
+ if !apiequality.Semantic.DeepEqual(oldVal, newVal) {
+ allErrs = append(allErrs, field.Invalid(fldPath, newVal, FieldImmutableErrorMsg))
+ }
+ return allErrs
+}
+
+// ValidateObjectMeta validates an object's metadata on creation. It expects that name generation has already
+// been performed.
+// It doesn't return an error for rootscoped resources with namespace, because namespace should already be cleared before.
+func ValidateObjectMeta(objMeta *metav1.ObjectMeta, requiresNamespace bool, nameFn ValidateNameFunc, fldPath *field.Path) field.ErrorList {
+ metadata, err := meta.Accessor(objMeta)
+ if err != nil {
+ allErrs := field.ErrorList{}
+ allErrs = append(allErrs, field.Invalid(fldPath, objMeta, err.Error()))
+ return allErrs
+ }
+ return ValidateObjectMetaAccessor(metadata, requiresNamespace, nameFn, fldPath)
+}
+
+// ValidateObjectMeta validates an object's metadata on creation. It expects that name generation has already
+// been performed.
+// It doesn't return an error for rootscoped resources with namespace, because namespace should already be cleared before.
+func ValidateObjectMetaAccessor(meta metav1.Object, requiresNamespace bool, nameFn ValidateNameFunc, fldPath *field.Path) field.ErrorList {
+ allErrs := field.ErrorList{}
+
+ if len(meta.GetGenerateName()) != 0 {
+ for _, msg := range nameFn(meta.GetGenerateName(), true) {
+ allErrs = append(allErrs, field.Invalid(fldPath.Child("generateName"), meta.GetGenerateName(), msg))
+ }
+ }
+ // If the generated name validates, but the calculated value does not, it's a problem with generation, and we
+ // report it here. This may confuse users, but indicates a programming bug and still must be validated.
+ // If there are multiple fields out of which one is required then add an or as a separator
+ if len(meta.GetName()) == 0 {
+ allErrs = append(allErrs, field.Required(fldPath.Child("name"), "name or generateName is required"))
+ } else {
+ for _, msg := range nameFn(meta.GetName(), false) {
+ allErrs = append(allErrs, field.Invalid(fldPath.Child("name"), meta.GetName(), msg))
+ }
+ }
+ if requiresNamespace {
+ if len(meta.GetNamespace()) == 0 {
+ allErrs = append(allErrs, field.Required(fldPath.Child("namespace"), ""))
+ } else {
+ for _, msg := range ValidateNamespaceName(meta.GetNamespace(), false) {
+ allErrs = append(allErrs, field.Invalid(fldPath.Child("namespace"), meta.GetNamespace(), msg))
+ }
+ }
+ } else {
+ if len(meta.GetNamespace()) != 0 {
+ allErrs = append(allErrs, field.Forbidden(fldPath.Child("namespace"), "not allowed on this type"))
+ }
+ }
+ if len(meta.GetClusterName()) != 0 {
+ for _, msg := range ValidateClusterName(meta.GetClusterName(), false) {
+ allErrs = append(allErrs, field.Invalid(fldPath.Child("clusterName"), meta.GetClusterName(), msg))
+ }
+ }
+ allErrs = append(allErrs, ValidateNonnegativeField(meta.GetGeneration(), fldPath.Child("generation"))...)
+ allErrs = append(allErrs, v1validation.ValidateLabels(meta.GetLabels(), fldPath.Child("labels"))...)
+ allErrs = append(allErrs, ValidateAnnotations(meta.GetAnnotations(), fldPath.Child("annotations"))...)
+ allErrs = append(allErrs, ValidateOwnerReferences(meta.GetOwnerReferences(), fldPath.Child("ownerReferences"))...)
+ allErrs = append(allErrs, ValidateInitializers(meta.GetInitializers(), fldPath.Child("initializers"))...)
+ allErrs = append(allErrs, ValidateFinalizers(meta.GetFinalizers(), fldPath.Child("finalizers"))...)
+ return allErrs
+}
+
+func ValidateInitializers(initializers *metav1.Initializers, fldPath *field.Path) field.ErrorList {
+ var allErrs field.ErrorList
+ if initializers == nil {
+ return allErrs
+ }
+ for i, initializer := range initializers.Pending {
+ for _, msg := range validation.IsQualifiedName(initializer.Name) {
+ allErrs = append(allErrs, field.Invalid(fldPath.Child("pending").Index(i), initializer.Name, msg))
+ }
+ }
+ allErrs = append(allErrs, validateInitializersResult(initializers.Result, fldPath.Child("result"))...)
+ if len(initializers.Pending) == 0 && initializers.Result == nil {
+ allErrs = append(allErrs, field.Invalid(fldPath.Child("pending"), nil, "must be non-empty when result is not set"))
+ }
+ return allErrs
+}
+
+func validateInitializersResult(result *metav1.Status, fldPath *field.Path) field.ErrorList {
+ var allErrs field.ErrorList
+ if result == nil {
+ return allErrs
+ }
+ switch result.Status {
+ case metav1.StatusFailure:
+ default:
+ allErrs = append(allErrs, field.Invalid(fldPath.Child("status"), result.Status, "must be 'Failure'"))
+ }
+ return allErrs
+}
+
+// ValidateFinalizers tests if the finalizers name are valid, and if there are conflicting finalizers.
+func ValidateFinalizers(finalizers []string, fldPath *field.Path) field.ErrorList {
+ allErrs := field.ErrorList{}
+ hasFinalizerOrphanDependents := false
+ hasFinalizerDeleteDependents := false
+ for _, finalizer := range finalizers {
+ allErrs = append(allErrs, ValidateFinalizerName(finalizer, fldPath)...)
+ if finalizer == metav1.FinalizerOrphanDependents {
+ hasFinalizerOrphanDependents = true
+ }
+ if finalizer == metav1.FinalizerDeleteDependents {
+ hasFinalizerDeleteDependents = true
+ }
+ }
+ if hasFinalizerDeleteDependents && hasFinalizerOrphanDependents {
+ allErrs = append(allErrs, field.Invalid(fldPath, finalizers, fmt.Sprintf("finalizer %s and %s cannot be both set", metav1.FinalizerOrphanDependents, metav1.FinalizerDeleteDependents)))
+ }
+ return allErrs
+}
+
+// ValidateObjectMetaUpdate validates an object's metadata when updated
+func ValidateObjectMetaUpdate(newMeta, oldMeta *metav1.ObjectMeta, fldPath *field.Path) field.ErrorList {
+ newMetadata, err := meta.Accessor(newMeta)
+ if err != nil {
+ allErrs := field.ErrorList{}
+ allErrs = append(allErrs, field.Invalid(fldPath, newMeta, err.Error()))
+ return allErrs
+ }
+ oldMetadata, err := meta.Accessor(oldMeta)
+ if err != nil {
+ allErrs := field.ErrorList{}
+ allErrs = append(allErrs, field.Invalid(fldPath, oldMeta, err.Error()))
+ return allErrs
+ }
+ return ValidateObjectMetaAccessorUpdate(newMetadata, oldMetadata, fldPath)
+}
+
+func ValidateObjectMetaAccessorUpdate(newMeta, oldMeta metav1.Object, fldPath *field.Path) field.ErrorList {
+ var allErrs field.ErrorList
+
+ if !RepairMalformedUpdates && newMeta.GetUID() != oldMeta.GetUID() {
+ allErrs = append(allErrs, field.Invalid(fldPath.Child("uid"), newMeta.GetUID(), "field is immutable"))
+ }
+ // in the event it is left empty, set it, to allow clients more flexibility
+ // TODO: remove the following code that repairs the update request when we retire the clients that modify the immutable fields.
+ // Please do not copy this pattern elsewhere; validation functions should not be modifying the objects they are passed!
+ if RepairMalformedUpdates {
+ if len(newMeta.GetUID()) == 0 {
+ newMeta.SetUID(oldMeta.GetUID())
+ }
+ // ignore changes to timestamp
+ if oldCreationTime := oldMeta.GetCreationTimestamp(); oldCreationTime.IsZero() {
+ oldMeta.SetCreationTimestamp(newMeta.GetCreationTimestamp())
+ } else {
+ newMeta.SetCreationTimestamp(oldMeta.GetCreationTimestamp())
+ }
+ // an object can never remove a deletion timestamp or clear/change grace period seconds
+ if !oldMeta.GetDeletionTimestamp().IsZero() {
+ newMeta.SetDeletionTimestamp(oldMeta.GetDeletionTimestamp())
+ }
+ if oldMeta.GetDeletionGracePeriodSeconds() != nil && newMeta.GetDeletionGracePeriodSeconds() == nil {
+ newMeta.SetDeletionGracePeriodSeconds(oldMeta.GetDeletionGracePeriodSeconds())
+ }
+ }
+
+ // TODO: needs to check if newMeta==nil && oldMeta !=nil after the repair logic is removed.
+ if newMeta.GetDeletionGracePeriodSeconds() != nil && (oldMeta.GetDeletionGracePeriodSeconds() == nil || *newMeta.GetDeletionGracePeriodSeconds() != *oldMeta.GetDeletionGracePeriodSeconds()) {
+ allErrs = append(allErrs, field.Invalid(fldPath.Child("deletionGracePeriodSeconds"), newMeta.GetDeletionGracePeriodSeconds(), "field is immutable; may only be changed via deletion"))
+ }
+ if newMeta.GetDeletionTimestamp() != nil && (oldMeta.GetDeletionTimestamp() == nil || !newMeta.GetDeletionTimestamp().Equal(*oldMeta.GetDeletionTimestamp())) {
+ allErrs = append(allErrs, field.Invalid(fldPath.Child("deletionTimestamp"), newMeta.GetDeletionTimestamp(), "field is immutable; may only be changed via deletion"))
+ }
+
+ // Finalizers cannot be added if the object is already being deleted.
+ if oldMeta.GetDeletionTimestamp() != nil {
+ allErrs = append(allErrs, ValidateNoNewFinalizers(newMeta.GetFinalizers(), oldMeta.GetFinalizers(), fldPath.Child("finalizers"))...)
+ }
+
+ // Reject updates that don't specify a resource version
+ if len(newMeta.GetResourceVersion()) == 0 {
+ allErrs = append(allErrs, field.Invalid(fldPath.Child("resourceVersion"), newMeta.GetResourceVersion(), "must be specified for an update"))
+ }
+
+ // Generation shouldn't be decremented
+ if newMeta.GetGeneration() < oldMeta.GetGeneration() {
+ allErrs = append(allErrs, field.Invalid(fldPath.Child("generation"), newMeta.GetGeneration(), "must not be decremented"))
+ }
+
+ allErrs = append(allErrs, ValidateInitializersUpdate(newMeta.GetInitializers(), oldMeta.GetInitializers(), fldPath.Child("initializers"))...)
+
+ allErrs = append(allErrs, ValidateImmutableField(newMeta.GetName(), oldMeta.GetName(), fldPath.Child("name"))...)
+ allErrs = append(allErrs, ValidateImmutableField(newMeta.GetNamespace(), oldMeta.GetNamespace(), fldPath.Child("namespace"))...)
+ allErrs = append(allErrs, ValidateImmutableField(newMeta.GetUID(), oldMeta.GetUID(), fldPath.Child("uid"))...)
+ allErrs = append(allErrs, ValidateImmutableField(newMeta.GetCreationTimestamp(), oldMeta.GetCreationTimestamp(), fldPath.Child("creationTimestamp"))...)
+ allErrs = append(allErrs, ValidateImmutableField(newMeta.GetClusterName(), oldMeta.GetClusterName(), fldPath.Child("clusterName"))...)
+
+ allErrs = append(allErrs, v1validation.ValidateLabels(newMeta.GetLabels(), fldPath.Child("labels"))...)
+ allErrs = append(allErrs, ValidateAnnotations(newMeta.GetAnnotations(), fldPath.Child("annotations"))...)
+ allErrs = append(allErrs, ValidateOwnerReferences(newMeta.GetOwnerReferences(), fldPath.Child("ownerReferences"))...)
+
+ return allErrs
+}
+
+// ValidateInitializersUpdate checks the update of the metadata initializers field
+func ValidateInitializersUpdate(newInit, oldInit *metav1.Initializers, fldPath *field.Path) field.ErrorList {
+ var allErrs field.ErrorList
+ switch {
+ case oldInit == nil && newInit != nil:
+ // Initializers may not be set on new objects
+ allErrs = append(allErrs, field.Invalid(fldPath, nil, "field is immutable once initialization has completed"))
+ case oldInit != nil && newInit == nil:
+ // this is a valid transition and means initialization was successful
+ case oldInit != nil && newInit != nil:
+ // validate changes to initializers
+ switch {
+ case oldInit.Result == nil && newInit.Result != nil:
+ // setting a result is allowed
+ allErrs = append(allErrs, validateInitializersResult(newInit.Result, fldPath.Child("result"))...)
+ case oldInit.Result != nil:
+ // setting Result implies permanent failure, and all future updates will be prevented
+ allErrs = append(allErrs, ValidateImmutableField(newInit.Result, oldInit.Result, fldPath.Child("result"))...)
+ default:
+ // leaving the result nil is allowed
+ }
+ }
+ return allErrs
+}