Switch to github.com/golang/dep for vendoring

Signed-off-by: Mrunal Patel <mrunalp@gmail.com>
This commit is contained in:
Mrunal Patel 2017-01-31 16:45:59 -08:00
parent d6ab91be27
commit 8e5b17cf13
15431 changed files with 3971413 additions and 8881 deletions

29
vendor/k8s.io/apimachinery/README.md generated vendored Normal file
View file

@ -0,0 +1,29 @@
# apimachinery
Scheme, typing, encoding, decoding, and conversion packages for Kubernetes and Kubernetes-like API objects.
## Purpose
This library is a shared dependency for servers and clients to work with Kubernetes API infrastructure without direct
type dependencies. It's first comsumers are `k8s.io/kubernetes`, `k8s.io/client-go`, and `k8s.io/apiserver`.
## Compatibility
There are *NO compatibility guarantees* for this repository. It is in direct support of Kubernetes, so branches
will track Kubernetes and be compatible with that repo. As we more cleanly separate the layers, we will review the
compatibility guarantee.
## Where does it come from?
`apimachinery` is synced from https://github.com/kubernetes/kubernetes/blob/master/staging/src/k8s.io/apimachinery.
Code changes are made in that location, merged into `k8s.io/kubernetes` and later synced here.
## Things you should *NOT* do
1. Add API types to this repo. This is for the machinery, not for the types.
2. Directly modify any files under `pkg` in this repo. Those are driven from `k8s.io/kuberenetes/staging/src/k8s.io/apimachinery`.
3. Expect compatibility. This repo is direct support of Kubernetes and the API isn't yet stable enough for API guarantees.

1
vendor/k8s.io/apimachinery/filter-branch-sha generated vendored Normal file
View file

@ -0,0 +1 @@
acda1e7401fbf79d89ba23df5e11d81f4a9d4e0d

61
vendor/k8s.io/apimachinery/hack/sync-from-kubernetes.sh generated vendored Executable file
View file

@ -0,0 +1,61 @@
#!/bin/bash
# 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.
# overall flow
# 1. fetch the current level of k8s.io/kubernetes
# 2. check out the k8s.io/kubernetes HEAD into a separate branch
# 3. rewrite the history on that branch to *only* include staging/src/k8s.io/apimachinery
# 4. locate all commits between the last time we sync'ed and now
# 5. switch back to the starting branch
# 6. for each commit, cherry-pick it (which will keep authorship) into current branch
# 7. update metadata files indicating which commits we've sync'ed to
set -e
dir=$(mktemp -d "${TMPDIR:-/tmp/}$(basename 0).XXXXXXXXXXXX")
git remote add upstream-kube git@github.com:kubernetes/kubernetes.git || true
git fetch upstream-kube
currBranch=$(git rev-parse --abbrev-ref HEAD)
previousKubeSHA=$(cat kubernetes-sha)
previousBranchSHA=$(cat filter-branch-sha)
git branch -D kube-sync || true
git checkout upstream-kube/master -b kube-sync
git reset --hard upstream-kube/master
newKubeSHA=$(git log --oneline --format='%H' kube-sync -1)
# this command rewrite git history to *only* include staging/src/k8s.io/apimachinery
git filter-branch -f --subdirectory-filter staging/src/k8s.io/apimachinery HEAD
newBranchSHA=$(git log --oneline --format='%H' kube-sync -1)
git log --no-merges --format='%H' --reverse ${previousBranchSHA}..HEAD > ${dir}/commits
git checkout ${currBranch}
while read commitSHA; do
echo "working ${commitSHA}"
git cherry-pick ${commitSHA}
done <${dir}/commits
# track the k8s.io/kubernetes commit SHA so we can always determine which level of kube this repo matches
# track the filtered branch commit SHA so that we can determine which commits need to be picked
echo ${newKubeSHA} > kubernetes-sha
echo ${newBranchSHA} > filter-branch-sha
git commit -m "sync(k8s.io/kubernetes): ${newKubeSHA}" -- kubernetes-sha filter-branch-sha

1
vendor/k8s.io/apimachinery/kubernetes-sha generated vendored Normal file
View file

@ -0,0 +1 @@
424a0e9e827a39c714ee03e1891c43635adabcdc

5
vendor/k8s.io/apimachinery/pkg/OWNERS generated vendored Normal file
View file

@ -0,0 +1,5 @@
approvers:
- caesarxuchao
- deads2k
- lavalamp
- smarterclayton

View file

@ -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()
},
)

27
vendor/k8s.io/apimachinery/pkg/api/errors/OWNERS generated vendored Executable file
View file

@ -0,0 +1,27 @@
reviewers:
- thockin
- lavalamp
- smarterclayton
- wojtek-t
- deads2k
- brendandburns
- derekwaynecarr
- caesarxuchao
- mikedanese
- liggitt
- nikhiljindal
- gmarek
- erictune
- saad-ali
- janetkuo
- timstclair
- eparis
- timothysc
- dims
- spxtr
- hongchaodeng
- krousey
- satnam6502
- cjcullen
- david-mcmahon
- goltermann

18
vendor/k8s.io/apimachinery/pkg/api/errors/doc.go generated vendored Normal file
View file

@ -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"

478
vendor/k8s.io/apimachinery/pkg/api/errors/errors.go generated vendored Normal file
View file

@ -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
}

View file

@ -0,0 +1,196 @@
/*
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 (
"errors"
"fmt"
"reflect"
"testing"
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"
)
func resource(resource string) schema.GroupResource {
return schema.GroupResource{Group: "", Resource: resource}
}
func kind(kind string) schema.GroupKind {
return schema.GroupKind{Group: "", Kind: kind}
}
func TestErrorNew(t *testing.T) {
err := NewAlreadyExists(resource("tests"), "1")
if !IsAlreadyExists(err) {
t.Errorf("expected to be %s", metav1.StatusReasonAlreadyExists)
}
if IsConflict(err) {
t.Errorf("expected to not be %s", metav1.StatusReasonConflict)
}
if IsNotFound(err) {
t.Errorf(fmt.Sprintf("expected to not be %s", metav1.StatusReasonNotFound))
}
if IsInvalid(err) {
t.Errorf("expected to not be %s", metav1.StatusReasonInvalid)
}
if IsBadRequest(err) {
t.Errorf("expected to not be %s", metav1.StatusReasonBadRequest)
}
if IsForbidden(err) {
t.Errorf("expected to not be %s", metav1.StatusReasonForbidden)
}
if IsServerTimeout(err) {
t.Errorf("expected to not be %s", metav1.StatusReasonServerTimeout)
}
if IsMethodNotSupported(err) {
t.Errorf("expected to not be %s", metav1.StatusReasonMethodNotAllowed)
}
if !IsConflict(NewConflict(resource("tests"), "2", errors.New("message"))) {
t.Errorf("expected to be conflict")
}
if !IsNotFound(NewNotFound(resource("tests"), "3")) {
t.Errorf("expected to be %s", metav1.StatusReasonNotFound)
}
if !IsInvalid(NewInvalid(kind("Test"), "2", nil)) {
t.Errorf("expected to be %s", metav1.StatusReasonInvalid)
}
if !IsBadRequest(NewBadRequest("reason")) {
t.Errorf("expected to be %s", metav1.StatusReasonBadRequest)
}
if !IsForbidden(NewForbidden(resource("tests"), "2", errors.New("reason"))) {
t.Errorf("expected to be %s", metav1.StatusReasonForbidden)
}
if !IsUnauthorized(NewUnauthorized("reason")) {
t.Errorf("expected to be %s", metav1.StatusReasonUnauthorized)
}
if !IsServerTimeout(NewServerTimeout(resource("tests"), "reason", 0)) {
t.Errorf("expected to be %s", metav1.StatusReasonServerTimeout)
}
if time, ok := SuggestsClientDelay(NewServerTimeout(resource("tests"), "doing something", 10)); time != 10 || !ok {
t.Errorf("expected to be %s", metav1.StatusReasonServerTimeout)
}
if time, ok := SuggestsClientDelay(NewTimeoutError("test reason", 10)); time != 10 || !ok {
t.Errorf("expected to be %s", metav1.StatusReasonTimeout)
}
if !IsMethodNotSupported(NewMethodNotSupported(resource("foos"), "delete")) {
t.Errorf("expected to be %s", metav1.StatusReasonMethodNotAllowed)
}
}
func TestNewInvalid(t *testing.T) {
testCases := []struct {
Err *field.Error
Details *metav1.StatusDetails
}{
{
field.Duplicate(field.NewPath("field[0].name"), "bar"),
&metav1.StatusDetails{
Kind: "Kind",
Name: "name",
Causes: []metav1.StatusCause{{
Type: metav1.CauseTypeFieldValueDuplicate,
Field: "field[0].name",
}},
},
},
{
field.Invalid(field.NewPath("field[0].name"), "bar", "detail"),
&metav1.StatusDetails{
Kind: "Kind",
Name: "name",
Causes: []metav1.StatusCause{{
Type: metav1.CauseTypeFieldValueInvalid,
Field: "field[0].name",
}},
},
},
{
field.NotFound(field.NewPath("field[0].name"), "bar"),
&metav1.StatusDetails{
Kind: "Kind",
Name: "name",
Causes: []metav1.StatusCause{{
Type: metav1.CauseTypeFieldValueNotFound,
Field: "field[0].name",
}},
},
},
{
field.NotSupported(field.NewPath("field[0].name"), "bar", nil),
&metav1.StatusDetails{
Kind: "Kind",
Name: "name",
Causes: []metav1.StatusCause{{
Type: metav1.CauseTypeFieldValueNotSupported,
Field: "field[0].name",
}},
},
},
{
field.Required(field.NewPath("field[0].name"), ""),
&metav1.StatusDetails{
Kind: "Kind",
Name: "name",
Causes: []metav1.StatusCause{{
Type: metav1.CauseTypeFieldValueRequired,
Field: "field[0].name",
}},
},
},
}
for i, testCase := range testCases {
vErr, expected := testCase.Err, testCase.Details
expected.Causes[0].Message = vErr.ErrorBody()
err := NewInvalid(kind("Kind"), "name", field.ErrorList{vErr})
status := err.ErrStatus
if status.Code != 422 || status.Reason != metav1.StatusReasonInvalid {
t.Errorf("%d: unexpected status: %#v", i, status)
}
if !reflect.DeepEqual(expected, status.Details) {
t.Errorf("%d: expected %#v, got %#v", i, expected, status.Details)
}
}
}
func Test_reasonForError(t *testing.T) {
if e, a := metav1.StatusReasonUnknown, reasonForError(nil); e != a {
t.Errorf("unexpected reason type: %#v", a)
}
}
type TestType struct{}
func (obj *TestType) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind }
func TestFromObject(t *testing.T) {
table := []struct {
obj runtime.Object
message string
}{
{&metav1.Status{Message: "foobar"}, "foobar"},
{&TestType{}, "unexpected object: &{}"},
}
for _, item := range table {
if e, a := item.message, FromObject(item.obj).Error(); e != a {
t.Errorf("Expected %v, got %v", e, a)
}
}
}

26
vendor/k8s.io/apimachinery/pkg/api/meta/OWNERS generated vendored Executable file
View file

@ -0,0 +1,26 @@
reviewers:
- thockin
- smarterclayton
- wojtek-t
- deads2k
- brendandburns
- derekwaynecarr
- caesarxuchao
- mikedanese
- liggitt
- nikhiljindal
- gmarek
- kargakis
- janetkuo
- ncdc
- eparis
- dims
- krousey
- markturansky
- fabioy
- resouer
- david-mcmahon
- mfojtik
- jianhuiz
- feihujiang
- ghodss

51
vendor/k8s.io/apimachinery/pkg/api/meta/default.go generated vendored Normal file
View file

@ -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
}

19
vendor/k8s.io/apimachinery/pkg/api/meta/doc.go generated vendored Normal file
View file

@ -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"

105
vendor/k8s.io/apimachinery/pkg/api/meta/errors.go generated vendored Normal file
View file

@ -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
}
}

View file

@ -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)
}

199
vendor/k8s.io/apimachinery/pkg/api/meta/help.go generated vendored Normal file
View file

@ -0,0 +1,199 @@
/*
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)
// 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
}

147
vendor/k8s.io/apimachinery/pkg/api/meta/interfaces.go generated vendored Normal file
View file

@ -0,0 +1,147 @@
/*
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)
AliasesForResource(resource string) ([]string, bool)
ResourceSingularizer(resource string) (singular string, err error)
}

567
vendor/k8s.io/apimachinery/pkg/api/meta/meta.go generated vendored Normal file
View file

@ -0,0 +1,567 @@
/*
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"
"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
case List, metav1.List, ListMetaAccessor, metav1.ListMetaAccessor:
return nil, errNotObject
default:
return nil, errNotObject
}
}
// 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
}
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
}
}
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
}

View file

@ -0,0 +1,231 @@
/*
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"
"k8s.io/apimachinery/pkg/util/sets"
)
// 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
}
// AliasesForResource finds the first alias response for the provided mappers.
func (m MultiRESTMapper) AliasesForResource(alias string) ([]string, bool) {
seenAliases := sets.NewString()
allAliases := []string{}
handled := false
for _, t := range m {
if currAliases, currOk := t.AliasesForResource(alias); currOk {
for _, currAlias := range currAliases {
if !seenAliases.Has(currAlias) {
allAliases = append(allAliases, currAlias)
seenAliases.Insert(currAlias)
}
}
handled = true
}
}
return allAliases, handled
}

View file

@ -0,0 +1,359 @@
/*
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 (
"errors"
"reflect"
"testing"
"k8s.io/apimachinery/pkg/runtime/schema"
)
func TestMultiRESTMapperResourceFor(t *testing.T) {
tcs := []struct {
name string
mapper MultiRESTMapper
input schema.GroupVersionResource
result schema.GroupVersionResource
err error
}{
{
name: "empty",
mapper: MultiRESTMapper{},
input: schema.GroupVersionResource{Resource: "foo"},
result: schema.GroupVersionResource{},
err: &NoResourceMatchError{PartialResource: schema.GroupVersionResource{Resource: "foo"}},
},
{
name: "ignore not found",
mapper: MultiRESTMapper{fixedRESTMapper{err: &NoResourceMatchError{PartialResource: schema.GroupVersionResource{Resource: "IGNORE_THIS"}}}},
input: schema.GroupVersionResource{Resource: "foo"},
result: schema.GroupVersionResource{},
err: &NoResourceMatchError{PartialResource: schema.GroupVersionResource{Resource: "foo"}},
},
{
name: "accept first failure",
mapper: MultiRESTMapper{fixedRESTMapper{err: errors.New("fail on this")}, fixedRESTMapper{resourcesFor: []schema.GroupVersionResource{{Resource: "unused"}}}},
input: schema.GroupVersionResource{Resource: "foo"},
result: schema.GroupVersionResource{},
err: errors.New("fail on this"),
},
}
for _, tc := range tcs {
actualResult, actualErr := tc.mapper.ResourceFor(tc.input)
if e, a := tc.result, actualResult; e != a {
t.Errorf("%s: expected %v, got %v", tc.name, e, a)
}
switch {
case tc.err == nil && actualErr == nil:
case tc.err == nil:
t.Errorf("%s: unexpected error: %v", tc.name, actualErr)
case actualErr == nil:
t.Errorf("%s: expected error: %v got nil", tc.name, tc.err)
case tc.err.Error() != actualErr.Error():
t.Errorf("%s: expected %v, got %v", tc.name, tc.err, actualErr)
}
}
}
func TestMultiRESTMapperResourcesFor(t *testing.T) {
tcs := []struct {
name string
mapper MultiRESTMapper
input schema.GroupVersionResource
result []schema.GroupVersionResource
err error
}{
{
name: "empty",
mapper: MultiRESTMapper{},
input: schema.GroupVersionResource{Resource: "foo"},
result: nil,
err: &NoResourceMatchError{PartialResource: schema.GroupVersionResource{Resource: "foo"}},
},
{
name: "ignore not found",
mapper: MultiRESTMapper{fixedRESTMapper{err: &NoResourceMatchError{PartialResource: schema.GroupVersionResource{Resource: "IGNORE_THIS"}}}},
input: schema.GroupVersionResource{Resource: "foo"},
result: nil,
err: &NoResourceMatchError{PartialResource: schema.GroupVersionResource{Resource: "foo"}},
},
{
name: "accept first failure",
mapper: MultiRESTMapper{fixedRESTMapper{err: errors.New("fail on this")}, fixedRESTMapper{resourcesFor: []schema.GroupVersionResource{{Resource: "unused"}}}},
input: schema.GroupVersionResource{Resource: "foo"},
result: nil,
err: errors.New("fail on this"),
},
{
name: "union and dedup",
mapper: MultiRESTMapper{
fixedRESTMapper{resourcesFor: []schema.GroupVersionResource{{Resource: "dupe"}, {Resource: "first"}}},
fixedRESTMapper{resourcesFor: []schema.GroupVersionResource{{Resource: "dupe"}, {Resource: "second"}}},
},
input: schema.GroupVersionResource{Resource: "foo"},
result: []schema.GroupVersionResource{{Resource: "dupe"}, {Resource: "first"}, {Resource: "second"}},
},
{
name: "skip not and continue",
mapper: MultiRESTMapper{
fixedRESTMapper{err: &NoResourceMatchError{PartialResource: schema.GroupVersionResource{Resource: "IGNORE_THIS"}}},
fixedRESTMapper{resourcesFor: []schema.GroupVersionResource{{Resource: "first"}, {Resource: "second"}}},
},
input: schema.GroupVersionResource{Resource: "foo"},
result: []schema.GroupVersionResource{{Resource: "first"}, {Resource: "second"}},
},
}
for _, tc := range tcs {
actualResult, actualErr := tc.mapper.ResourcesFor(tc.input)
if e, a := tc.result, actualResult; !reflect.DeepEqual(e, a) {
t.Errorf("%s: expected %v, got %v", tc.name, e, a)
}
switch {
case tc.err == nil && actualErr == nil:
case tc.err == nil:
t.Errorf("%s: unexpected error: %v", tc.name, actualErr)
case actualErr == nil:
t.Errorf("%s: expected error: %v got nil", tc.name, tc.err)
case tc.err.Error() != actualErr.Error():
t.Errorf("%s: expected %v, got %v", tc.name, tc.err, actualErr)
}
}
}
func TestMultiRESTMapperKindsFor(t *testing.T) {
tcs := []struct {
name string
mapper MultiRESTMapper
input schema.GroupVersionResource
result []schema.GroupVersionKind
err error
}{
{
name: "empty",
mapper: MultiRESTMapper{},
input: schema.GroupVersionResource{Resource: "foo"},
result: nil,
err: &NoResourceMatchError{PartialResource: schema.GroupVersionResource{Resource: "foo"}},
},
{
name: "ignore not found",
mapper: MultiRESTMapper{fixedRESTMapper{err: &NoResourceMatchError{PartialResource: schema.GroupVersionResource{Resource: "IGNORE_THIS"}}}},
input: schema.GroupVersionResource{Resource: "foo"},
result: nil,
err: &NoResourceMatchError{PartialResource: schema.GroupVersionResource{Resource: "foo"}},
},
{
name: "accept first failure",
mapper: MultiRESTMapper{fixedRESTMapper{err: errors.New("fail on this")}, fixedRESTMapper{kindsFor: []schema.GroupVersionKind{{Kind: "unused"}}}},
input: schema.GroupVersionResource{Resource: "foo"},
result: nil,
err: errors.New("fail on this"),
},
{
name: "union and dedup",
mapper: MultiRESTMapper{
fixedRESTMapper{kindsFor: []schema.GroupVersionKind{{Kind: "dupe"}, {Kind: "first"}}},
fixedRESTMapper{kindsFor: []schema.GroupVersionKind{{Kind: "dupe"}, {Kind: "second"}}},
},
input: schema.GroupVersionResource{Resource: "foo"},
result: []schema.GroupVersionKind{{Kind: "dupe"}, {Kind: "first"}, {Kind: "second"}},
},
{
name: "skip not and continue",
mapper: MultiRESTMapper{
fixedRESTMapper{err: &NoResourceMatchError{PartialResource: schema.GroupVersionResource{Resource: "IGNORE_THIS"}}},
fixedRESTMapper{kindsFor: []schema.GroupVersionKind{{Kind: "first"}, {Kind: "second"}}},
},
input: schema.GroupVersionResource{Resource: "foo"},
result: []schema.GroupVersionKind{{Kind: "first"}, {Kind: "second"}},
},
}
for _, tc := range tcs {
actualResult, actualErr := tc.mapper.KindsFor(tc.input)
if e, a := tc.result, actualResult; !reflect.DeepEqual(e, a) {
t.Errorf("%s: expected %v, got %v", tc.name, e, a)
}
switch {
case tc.err == nil && actualErr == nil:
case tc.err == nil:
t.Errorf("%s: unexpected error: %v", tc.name, actualErr)
case actualErr == nil:
t.Errorf("%s: expected error: %v got nil", tc.name, tc.err)
case tc.err.Error() != actualErr.Error():
t.Errorf("%s: expected %v, got %v", tc.name, tc.err, actualErr)
}
}
}
func TestMultiRESTMapperKindFor(t *testing.T) {
tcs := []struct {
name string
mapper MultiRESTMapper
input schema.GroupVersionResource
result schema.GroupVersionKind
err error
}{
{
name: "empty",
mapper: MultiRESTMapper{},
input: schema.GroupVersionResource{Resource: "foo"},
result: schema.GroupVersionKind{},
err: &NoResourceMatchError{PartialResource: schema.GroupVersionResource{Resource: "foo"}},
},
{
name: "ignore not found",
mapper: MultiRESTMapper{fixedRESTMapper{err: &NoResourceMatchError{PartialResource: schema.GroupVersionResource{Resource: "IGNORE_THIS"}}}},
input: schema.GroupVersionResource{Resource: "foo"},
result: schema.GroupVersionKind{},
err: &NoResourceMatchError{PartialResource: schema.GroupVersionResource{Resource: "foo"}},
},
{
name: "accept first failure",
mapper: MultiRESTMapper{fixedRESTMapper{err: errors.New("fail on this")}, fixedRESTMapper{kindsFor: []schema.GroupVersionKind{{Kind: "unused"}}}},
input: schema.GroupVersionResource{Resource: "foo"},
result: schema.GroupVersionKind{},
err: errors.New("fail on this"),
},
}
for _, tc := range tcs {
actualResult, actualErr := tc.mapper.KindFor(tc.input)
if e, a := tc.result, actualResult; e != a {
t.Errorf("%s: expected %v, got %v", tc.name, e, a)
}
switch {
case tc.err == nil && actualErr == nil:
case tc.err == nil:
t.Errorf("%s: unexpected error: %v", tc.name, actualErr)
case actualErr == nil:
t.Errorf("%s: expected error: %v got nil", tc.name, tc.err)
case tc.err.Error() != actualErr.Error():
t.Errorf("%s: expected %v, got %v", tc.name, tc.err, actualErr)
}
}
}
func TestMultiRESTMapperRESTMappings(t *testing.T) {
mapping1, mapping2 := &RESTMapping{}, &RESTMapping{}
tcs := []struct {
name string
mapper MultiRESTMapper
input schema.GroupKind
result []*RESTMapping
err error
}{
{
name: "empty",
mapper: MultiRESTMapper{},
input: schema.GroupKind{Kind: "Foo"},
result: nil,
err: &NoKindMatchError{PartialKind: schema.GroupVersionKind{Kind: "Foo"}},
},
{
name: "ignore not found",
mapper: MultiRESTMapper{fixedRESTMapper{err: &NoKindMatchError{PartialKind: schema.GroupVersionKind{Kind: "IGNORE_THIS"}}}},
input: schema.GroupKind{Kind: "Foo"},
result: nil,
err: &NoKindMatchError{PartialKind: schema.GroupVersionKind{Kind: "Foo"}},
},
{
name: "accept first failure",
mapper: MultiRESTMapper{fixedRESTMapper{err: errors.New("fail on this")}, fixedRESTMapper{mappings: []*RESTMapping{mapping1}}},
input: schema.GroupKind{Kind: "Foo"},
result: nil,
err: errors.New("fail on this"),
},
{
name: "return both",
mapper: MultiRESTMapper{fixedRESTMapper{mappings: []*RESTMapping{mapping1}}, fixedRESTMapper{mappings: []*RESTMapping{mapping2}}},
input: schema.GroupKind{Kind: "Foo"},
result: []*RESTMapping{mapping1, mapping2},
},
}
for _, tc := range tcs {
actualResult, actualErr := tc.mapper.RESTMappings(tc.input)
if e, a := tc.result, actualResult; !reflect.DeepEqual(e, a) {
t.Errorf("%s: expected %v, got %v", tc.name, e, a)
}
switch {
case tc.err == nil && actualErr == nil:
case tc.err == nil:
t.Errorf("%s: unexpected error: %v", tc.name, actualErr)
case actualErr == nil:
t.Errorf("%s: expected error: %v got nil", tc.name, tc.err)
case tc.err.Error() != actualErr.Error():
t.Errorf("%s: expected %v, got %v", tc.name, tc.err, actualErr)
}
}
}
type fixedRESTMapper struct {
resourcesFor []schema.GroupVersionResource
kindsFor []schema.GroupVersionKind
resourceFor schema.GroupVersionResource
kindFor schema.GroupVersionKind
mappings []*RESTMapping
err error
}
func (m fixedRESTMapper) ResourceSingularizer(resource string) (singular string, err error) {
return "", m.err
}
func (m fixedRESTMapper) ResourcesFor(resource schema.GroupVersionResource) ([]schema.GroupVersionResource, error) {
return m.resourcesFor, m.err
}
func (m fixedRESTMapper) KindsFor(resource schema.GroupVersionResource) (gvk []schema.GroupVersionKind, err error) {
return m.kindsFor, m.err
}
func (m fixedRESTMapper) ResourceFor(resource schema.GroupVersionResource) (schema.GroupVersionResource, error) {
return m.resourceFor, m.err
}
func (m fixedRESTMapper) KindFor(resource schema.GroupVersionResource) (schema.GroupVersionKind, error) {
return m.kindFor, m.err
}
func (m fixedRESTMapper) RESTMapping(gk schema.GroupKind, versions ...string) (mapping *RESTMapping, err error) {
return nil, m.err
}
func (m fixedRESTMapper) RESTMappings(gk schema.GroupKind, versions ...string) (mappings []*RESTMapping, err error) {
return m.mappings, m.err
}
func (m fixedRESTMapper) AliasesForResource(alias string) (aliases []string, ok bool) {
return nil, false
}
func (m fixedRESTMapper) ResourceIsValid(resource schema.GroupVersionResource) bool {
return false
}

226
vendor/k8s.io/apimachinery/pkg/api/meta/priority.go generated vendored Normal file
View file

@ -0,0 +1,226 @@
/*
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) AliasesForResource(alias string) (aliases []string, ok bool) {
return m.Delegate.AliasesForResource(alias)
}
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)
}

View file

@ -0,0 +1,346 @@
/*
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 (
"errors"
"reflect"
"strings"
"testing"
"k8s.io/apimachinery/pkg/runtime/schema"
)
func TestPriorityRESTMapperResourceForErrorHandling(t *testing.T) {
tcs := []struct {
name string
delegate RESTMapper
resourcePatterns []schema.GroupVersionResource
result schema.GroupVersionResource
err string
}{
{
name: "single hit",
delegate: fixedRESTMapper{resourcesFor: []schema.GroupVersionResource{{Resource: "single-hit"}}},
result: schema.GroupVersionResource{Resource: "single-hit"},
},
{
name: "ambiguous match",
delegate: fixedRESTMapper{resourcesFor: []schema.GroupVersionResource{
{Group: "one", Version: "a", Resource: "first"},
{Group: "two", Version: "b", Resource: "second"},
}},
err: "matches multiple resources",
},
{
name: "group selection",
delegate: fixedRESTMapper{resourcesFor: []schema.GroupVersionResource{
{Group: "one", Version: "a", Resource: "first"},
{Group: "two", Version: "b", Resource: "second"},
}},
resourcePatterns: []schema.GroupVersionResource{
{Group: "one", Version: AnyVersion, Resource: AnyResource},
},
result: schema.GroupVersionResource{Group: "one", Version: "a", Resource: "first"},
},
{
name: "empty match continues",
delegate: fixedRESTMapper{resourcesFor: []schema.GroupVersionResource{
{Group: "one", Version: "a", Resource: "first"},
{Group: "two", Version: "b", Resource: "second"},
}},
resourcePatterns: []schema.GroupVersionResource{
{Group: "fail", Version: AnyVersion, Resource: AnyResource},
{Group: "one", Version: AnyVersion, Resource: AnyResource},
},
result: schema.GroupVersionResource{Group: "one", Version: "a", Resource: "first"},
},
{
name: "group followed by version selection",
delegate: fixedRESTMapper{resourcesFor: []schema.GroupVersionResource{
{Group: "one", Version: "a", Resource: "first"},
{Group: "two", Version: "b", Resource: "second"},
{Group: "one", Version: "c", Resource: "third"},
}},
resourcePatterns: []schema.GroupVersionResource{
{Group: "one", Version: AnyVersion, Resource: AnyResource},
{Group: AnyGroup, Version: "a", Resource: AnyResource},
},
result: schema.GroupVersionResource{Group: "one", Version: "a", Resource: "first"},
},
{
name: "resource selection",
delegate: fixedRESTMapper{resourcesFor: []schema.GroupVersionResource{
{Group: "one", Version: "a", Resource: "first"},
{Group: "one", Version: "a", Resource: "second"},
}},
resourcePatterns: []schema.GroupVersionResource{
{Group: AnyGroup, Version: AnyVersion, Resource: "second"},
},
result: schema.GroupVersionResource{Group: "one", Version: "a", Resource: "second"},
},
}
for _, tc := range tcs {
mapper := PriorityRESTMapper{Delegate: tc.delegate, ResourcePriority: tc.resourcePatterns}
actualResult, actualErr := mapper.ResourceFor(schema.GroupVersionResource{})
if e, a := tc.result, actualResult; e != a {
t.Errorf("%s: expected %v, got %v", tc.name, e, a)
}
if len(tc.err) == 0 && actualErr == nil {
continue
}
if len(tc.err) > 0 && actualErr == nil {
t.Errorf("%s: missing expected err: %v", tc.name, tc.err)
continue
}
if !strings.Contains(actualErr.Error(), tc.err) {
t.Errorf("%s: expected %v, got %v", tc.name, tc.err, actualErr)
}
}
}
func TestPriorityRESTMapperKindForErrorHandling(t *testing.T) {
tcs := []struct {
name string
delegate RESTMapper
kindPatterns []schema.GroupVersionKind
result schema.GroupVersionKind
err string
}{
{
name: "single hit",
delegate: fixedRESTMapper{kindsFor: []schema.GroupVersionKind{{Kind: "single-hit"}}},
result: schema.GroupVersionKind{Kind: "single-hit"},
},
{
name: "ambiguous match",
delegate: fixedRESTMapper{kindsFor: []schema.GroupVersionKind{
{Group: "one", Version: "a", Kind: "first"},
{Group: "two", Version: "b", Kind: "second"},
}},
err: "matches multiple kinds",
},
{
name: "group selection",
delegate: fixedRESTMapper{kindsFor: []schema.GroupVersionKind{
{Group: "one", Version: "a", Kind: "first"},
{Group: "two", Version: "b", Kind: "second"},
}},
kindPatterns: []schema.GroupVersionKind{
{Group: "one", Version: AnyVersion, Kind: AnyKind},
},
result: schema.GroupVersionKind{Group: "one", Version: "a", Kind: "first"},
},
{
name: "empty match continues",
delegate: fixedRESTMapper{kindsFor: []schema.GroupVersionKind{
{Group: "one", Version: "a", Kind: "first"},
{Group: "two", Version: "b", Kind: "second"},
}},
kindPatterns: []schema.GroupVersionKind{
{Group: "fail", Version: AnyVersion, Kind: AnyKind},
{Group: "one", Version: AnyVersion, Kind: AnyKind},
},
result: schema.GroupVersionKind{Group: "one", Version: "a", Kind: "first"},
},
{
name: "group followed by version selection",
delegate: fixedRESTMapper{kindsFor: []schema.GroupVersionKind{
{Group: "one", Version: "a", Kind: "first"},
{Group: "two", Version: "b", Kind: "second"},
{Group: "one", Version: "c", Kind: "third"},
}},
kindPatterns: []schema.GroupVersionKind{
{Group: "one", Version: AnyVersion, Kind: AnyKind},
{Group: AnyGroup, Version: "a", Kind: AnyKind},
},
result: schema.GroupVersionKind{Group: "one", Version: "a", Kind: "first"},
},
{
name: "kind selection",
delegate: fixedRESTMapper{kindsFor: []schema.GroupVersionKind{
{Group: "one", Version: "a", Kind: "first"},
{Group: "one", Version: "a", Kind: "second"},
}},
kindPatterns: []schema.GroupVersionKind{
{Group: AnyGroup, Version: AnyVersion, Kind: "second"},
},
result: schema.GroupVersionKind{Group: "one", Version: "a", Kind: "second"},
},
}
for _, tc := range tcs {
mapper := PriorityRESTMapper{Delegate: tc.delegate, KindPriority: tc.kindPatterns}
actualResult, actualErr := mapper.KindFor(schema.GroupVersionResource{})
if e, a := tc.result, actualResult; e != a {
t.Errorf("%s: expected %v, got %v", tc.name, e, a)
}
if len(tc.err) == 0 && actualErr == nil {
continue
}
if len(tc.err) > 0 && actualErr == nil {
t.Errorf("%s: missing expected err: %v", tc.name, tc.err)
continue
}
if !strings.Contains(actualErr.Error(), tc.err) {
t.Errorf("%s: expected %v, got %v", tc.name, tc.err, actualErr)
}
}
}
func TestPriorityRESTMapperRESTMapping(t *testing.T) {
mapping1 := &RESTMapping{
GroupVersionKind: schema.GroupVersionKind{Kind: "Foo", Version: "v1alpha1"},
}
mapping2 := &RESTMapping{
GroupVersionKind: schema.GroupVersionKind{Kind: "Foo", Version: "v1"},
}
mapping3 := &RESTMapping{
GroupVersionKind: schema.GroupVersionKind{Group: "other", Kind: "Foo", Version: "v1"},
}
allMappers := MultiRESTMapper{
fixedRESTMapper{mappings: []*RESTMapping{mapping1}},
fixedRESTMapper{mappings: []*RESTMapping{mapping2}},
fixedRESTMapper{mappings: []*RESTMapping{mapping3}},
}
tcs := []struct {
name string
mapper PriorityRESTMapper
input schema.GroupKind
result *RESTMapping
err error
}{
{
name: "empty",
mapper: PriorityRESTMapper{Delegate: MultiRESTMapper{}},
input: schema.GroupKind{Kind: "Foo"},
err: &NoKindMatchError{PartialKind: schema.GroupVersionKind{Kind: "Foo"}},
},
{
name: "ignore not found",
mapper: PriorityRESTMapper{Delegate: MultiRESTMapper{fixedRESTMapper{err: &NoKindMatchError{PartialKind: schema.GroupVersionKind{Kind: "IGNORE_THIS"}}}}},
input: schema.GroupKind{Kind: "Foo"},
err: &NoKindMatchError{PartialKind: schema.GroupVersionKind{Kind: "Foo"}},
},
{
name: "accept first failure",
mapper: PriorityRESTMapper{Delegate: MultiRESTMapper{fixedRESTMapper{err: errors.New("fail on this")}, fixedRESTMapper{mappings: []*RESTMapping{mapping1}}}},
input: schema.GroupKind{Kind: "Foo"},
err: errors.New("fail on this"),
},
{
name: "return error for ambiguous",
mapper: PriorityRESTMapper{
Delegate: allMappers,
},
input: schema.GroupKind{Kind: "Foo"},
err: &AmbiguousKindError{
PartialKind: schema.GroupVersionKind{Kind: "Foo"},
MatchingKinds: []schema.GroupVersionKind{
{Kind: "Foo", Version: "v1alpha1"},
{Kind: "Foo", Version: "v1"},
{Group: "other", Kind: "Foo", Version: "v1"},
},
},
},
{
name: "accept only item",
mapper: PriorityRESTMapper{
Delegate: fixedRESTMapper{mappings: []*RESTMapping{mapping1}},
},
input: schema.GroupKind{Kind: "Foo"},
result: mapping1,
},
{
name: "return single priority",
mapper: PriorityRESTMapper{
Delegate: allMappers,
KindPriority: []schema.GroupVersionKind{{Version: "v1", Kind: AnyKind}, {Version: "v1alpha1", Kind: AnyKind}},
},
input: schema.GroupKind{Kind: "Foo"},
result: mapping2,
},
{
name: "return out of group match",
mapper: PriorityRESTMapper{
Delegate: allMappers,
KindPriority: []schema.GroupVersionKind{{Group: AnyGroup, Version: "v1", Kind: AnyKind}, {Group: "other", Version: AnyVersion, Kind: AnyKind}},
},
input: schema.GroupKind{Kind: "Foo"},
result: mapping3,
},
}
for _, tc := range tcs {
actualResult, actualErr := tc.mapper.RESTMapping(tc.input)
if e, a := tc.result, actualResult; !reflect.DeepEqual(e, a) {
t.Errorf("%s: expected %v, got %v", tc.name, e, a)
}
switch {
case tc.err == nil && actualErr == nil:
case tc.err == nil:
t.Errorf("%s: unexpected error: %v", tc.name, actualErr)
case actualErr == nil:
t.Errorf("%s: expected error: %v got nil", tc.name, tc.err)
case tc.err.Error() != actualErr.Error():
t.Errorf("%s: expected %v, got %v", tc.name, tc.err, actualErr)
}
}
}
func TestPriorityRESTMapperRESTMappingHonorsUserVersion(t *testing.T) {
mappingV2alpha1 := &RESTMapping{
GroupVersionKind: schema.GroupVersionKind{Group: "Bar", Kind: "Foo", Version: "v2alpha1"},
}
mappingV1 := &RESTMapping{
GroupVersionKind: schema.GroupVersionKind{Group: "Bar", Kind: "Foo", Version: "v1"},
}
allMappers := MultiRESTMapper{
fixedRESTMapper{mappings: []*RESTMapping{mappingV2alpha1}},
fixedRESTMapper{mappings: []*RESTMapping{mappingV1}},
}
mapper := PriorityRESTMapper{
Delegate: allMappers,
KindPriority: []schema.GroupVersionKind{{Group: "Bar", Version: "v2alpha1", Kind: AnyKind}, {Group: "Bar", Version: AnyVersion, Kind: AnyKind}},
}
outMapping1, err := mapper.RESTMapping(schema.GroupKind{Group: "Bar", Kind: "Foo"}, "v1")
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if outMapping1 != mappingV1 {
t.Errorf("asked for version %v, expected mapping for %v, got mapping for %v", "v1", mappingV1.GroupVersionKind, outMapping1.GroupVersionKind)
}
outMapping2, err := mapper.RESTMapping(schema.GroupKind{Group: "Bar", Kind: "Foo"}, "v2alpha1")
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if outMapping2 != mappingV2alpha1 {
t.Errorf("asked for version %v, expected mapping for %v, got mapping for %v", "v2alpha1", mappingV2alpha1.GroupVersionKind, outMapping2.GroupVersionKind)
}
}

566
vendor/k8s.io/apimachinery/pkg/api/meta/restmapper.go generated vendored Normal file
View file

@ -0,0 +1,566 @@
/*
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
// aliasToResource is used for mapping aliases to resources
aliasToResource map[string][]string
}
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)
aliasToResource := make(map[string][]string)
// TODO: verify name mappings work correctly when versions differ
return &DefaultRESTMapper{
resourceToKind: resourceToKind,
kindToPluralResource: kindToPluralResource,
kindToScope: kindToScope,
defaultGroupVersions: defaultGroupVersions,
singularToPlural: singularToPlural,
pluralToSingular: pluralToSingular,
aliasToResource: aliasToResource,
interfacesFunc: f,
}
}
func (m *DefaultRESTMapper) Add(kind schema.GroupVersionKind, scope RESTScope) {
plural, singular := KindToResource(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",
}
// KindToResource 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 KindToResource(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
}
// AddResourceAlias maps aliases to resources
func (m *DefaultRESTMapper) AddResourceAlias(alias string, resources ...string) {
if len(resources) == 0 {
return
}
m.aliasToResource[alias] = resources
}
// AliasesForResource returns whether a resource has an alias or not
func (m *DefaultRESTMapper) AliasesForResource(alias string) ([]string, bool) {
if res, ok := m.aliasToResource[alias]; ok {
return res, true
}
return nil, false
}

View file

@ -0,0 +1,751 @@
/*
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 (
"errors"
"reflect"
"strings"
"testing"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
type fakeConvertor struct{}
func (fakeConvertor) Convert(in, out, context interface{}) error {
return nil
}
func (fakeConvertor) ConvertToVersion(in runtime.Object, _ runtime.GroupVersioner) (runtime.Object, error) {
return in, nil
}
func (fakeConvertor) ConvertFieldLabel(version, kind, label, value string) (string, string, error) {
return label, value, nil
}
var validAccessor = resourceAccessor{}
var validConvertor = fakeConvertor{}
func fakeInterfaces(version schema.GroupVersion) (*VersionInterfaces, error) {
return &VersionInterfaces{ObjectConvertor: validConvertor, MetadataAccessor: validAccessor}, nil
}
var unmatchedErr = errors.New("no version")
func unmatchedVersionInterfaces(version schema.GroupVersion) (*VersionInterfaces, error) {
return nil, unmatchedErr
}
func TestRESTMapperVersionAndKindForResource(t *testing.T) {
testGroup := "test.group"
testVersion := "test"
testGroupVersion := schema.GroupVersion{Group: testGroup, Version: testVersion}
testCases := []struct {
Resource schema.GroupVersionResource
GroupVersionToRegister schema.GroupVersion
ExpectedGVK schema.GroupVersionKind
Err bool
}{
{Resource: schema.GroupVersionResource{Resource: "internalobjec"}, Err: true},
{Resource: schema.GroupVersionResource{Resource: "internalObjec"}, Err: true},
{Resource: schema.GroupVersionResource{Resource: "internalobject"}, ExpectedGVK: testGroupVersion.WithKind("InternalObject")},
{Resource: schema.GroupVersionResource{Resource: "internalobjects"}, ExpectedGVK: testGroupVersion.WithKind("InternalObject")},
}
for i, testCase := range testCases {
mapper := NewDefaultRESTMapper([]schema.GroupVersion{testGroupVersion}, fakeInterfaces)
if len(testCase.ExpectedGVK.Kind) != 0 {
mapper.Add(testCase.ExpectedGVK, RESTScopeNamespace)
}
actualGVK, err := mapper.KindFor(testCase.Resource)
hasErr := err != nil
if hasErr != testCase.Err {
t.Errorf("%d: unexpected error behavior %t: %v", i, testCase.Err, err)
continue
}
if err != nil {
continue
}
if actualGVK != testCase.ExpectedGVK {
t.Errorf("%d: unexpected version and kind: e=%s a=%s", i, testCase.ExpectedGVK, actualGVK)
}
}
}
func TestRESTMapperGroupForResource(t *testing.T) {
testCases := []struct {
Resource schema.GroupVersionResource
GroupVersionKind schema.GroupVersionKind
Err bool
}{
{Resource: schema.GroupVersionResource{Resource: "myObject"}, GroupVersionKind: schema.GroupVersionKind{Group: "testapi", Version: "test", Kind: "MyObject"}},
{Resource: schema.GroupVersionResource{Resource: "myobject"}, GroupVersionKind: schema.GroupVersionKind{Group: "testapi2", Version: "test", Kind: "MyObject"}},
{Resource: schema.GroupVersionResource{Resource: "myObje"}, Err: true, GroupVersionKind: schema.GroupVersionKind{Group: "testapi", Version: "test", Kind: "MyObject"}},
{Resource: schema.GroupVersionResource{Resource: "myobje"}, Err: true, GroupVersionKind: schema.GroupVersionKind{Group: "testapi", Version: "test", Kind: "MyObject"}},
}
for i, testCase := range testCases {
mapper := NewDefaultRESTMapper([]schema.GroupVersion{testCase.GroupVersionKind.GroupVersion()}, fakeInterfaces)
mapper.Add(testCase.GroupVersionKind, RESTScopeNamespace)
actualGVK, err := mapper.KindFor(testCase.Resource)
if testCase.Err {
if err == nil {
t.Errorf("%d: expected error", i)
}
} else if err != nil {
t.Errorf("%d: unexpected error: %v", i, err)
} else if actualGVK != testCase.GroupVersionKind {
t.Errorf("%d: expected group %q, got %q", i, testCase.GroupVersionKind, actualGVK)
}
}
}
func TestRESTMapperKindsFor(t *testing.T) {
testCases := []struct {
Name string
PreferredOrder []schema.GroupVersion
KindsToRegister []schema.GroupVersionKind
PartialResourceToRequest schema.GroupVersionResource
ExpectedKinds []schema.GroupVersionKind
ExpectedKindErr string
}{
{
// exact matches are prefered
Name: "groups, with group exact",
PreferredOrder: []schema.GroupVersion{
{Group: "first-group-1", Version: "first-version"},
{Group: "first-group", Version: "first-version"},
},
KindsToRegister: []schema.GroupVersionKind{
{Group: "first-group-1", Version: "first-version", Kind: "my-kind"},
{Group: "first-group", Version: "first-version", Kind: "my-kind"},
},
PartialResourceToRequest: schema.GroupVersionResource{Group: "first-group", Resource: "my-kind"},
ExpectedKinds: []schema.GroupVersionKind{
{Group: "first-group", Version: "first-version", Kind: "my-kind"},
},
},
{
// group prefixes work
Name: "groups, with group prefix",
PreferredOrder: []schema.GroupVersion{
{Group: "second-group", Version: "first-version"},
{Group: "first-group", Version: "first-version"},
},
KindsToRegister: []schema.GroupVersionKind{
{Group: "first-group", Version: "first-version", Kind: "my-kind"},
{Group: "second-group", Version: "first-version", Kind: "my-kind"},
},
PartialResourceToRequest: schema.GroupVersionResource{Group: "first", Resource: "my-kind"},
ExpectedKinds: []schema.GroupVersionKind{
{Group: "first-group", Version: "first-version", Kind: "my-kind"},
},
},
{
// group prefixes can be ambiguous
Name: "groups, with ambiguous group prefix",
PreferredOrder: []schema.GroupVersion{
{Group: "first-group-1", Version: "first-version"},
{Group: "first-group", Version: "first-version"},
},
KindsToRegister: []schema.GroupVersionKind{
{Group: "first-group-1", Version: "first-version", Kind: "my-kind"},
{Group: "first-group", Version: "first-version", Kind: "my-kind"},
},
PartialResourceToRequest: schema.GroupVersionResource{Group: "first", Resource: "my-kind"},
ExpectedKinds: []schema.GroupVersionKind{
{Group: "first-group-1", Version: "first-version", Kind: "my-kind"},
{Group: "first-group", Version: "first-version", Kind: "my-kind"},
},
ExpectedKindErr: " matches multiple kinds ",
},
{
Name: "ambiguous groups, with preference order",
PreferredOrder: []schema.GroupVersion{
{Group: "second-group", Version: "first-version"},
{Group: "first-group", Version: "first-version"},
},
KindsToRegister: []schema.GroupVersionKind{
{Group: "first-group", Version: "first-version", Kind: "my-kind"},
{Group: "first-group", Version: "first-version", Kind: "your-kind"},
{Group: "second-group", Version: "first-version", Kind: "my-kind"},
{Group: "second-group", Version: "first-version", Kind: "your-kind"},
},
PartialResourceToRequest: schema.GroupVersionResource{Resource: "my-kinds"},
ExpectedKinds: []schema.GroupVersionKind{
{Group: "second-group", Version: "first-version", Kind: "my-kind"},
{Group: "first-group", Version: "first-version", Kind: "my-kind"},
},
ExpectedKindErr: " matches multiple kinds ",
},
{
Name: "ambiguous groups, with explicit group match",
PreferredOrder: []schema.GroupVersion{
{Group: "second-group", Version: "first-version"},
{Group: "first-group", Version: "first-version"},
},
KindsToRegister: []schema.GroupVersionKind{
{Group: "first-group", Version: "first-version", Kind: "my-kind"},
{Group: "first-group", Version: "first-version", Kind: "your-kind"},
{Group: "second-group", Version: "first-version", Kind: "my-kind"},
{Group: "second-group", Version: "first-version", Kind: "your-kind"},
},
PartialResourceToRequest: schema.GroupVersionResource{Group: "first-group", Resource: "my-kinds"},
ExpectedKinds: []schema.GroupVersionKind{
{Group: "first-group", Version: "first-version", Kind: "my-kind"},
},
},
{
Name: "ambiguous groups, with ambiguous version match",
PreferredOrder: []schema.GroupVersion{
{Group: "first-group", Version: "first-version"},
{Group: "second-group", Version: "first-version"},
},
KindsToRegister: []schema.GroupVersionKind{
{Group: "first-group", Version: "first-version", Kind: "my-kind"},
{Group: "first-group", Version: "first-version", Kind: "your-kind"},
{Group: "second-group", Version: "first-version", Kind: "my-kind"},
{Group: "second-group", Version: "first-version", Kind: "your-kind"},
},
PartialResourceToRequest: schema.GroupVersionResource{Version: "first-version", Resource: "my-kinds"},
ExpectedKinds: []schema.GroupVersionKind{
{Group: "first-group", Version: "first-version", Kind: "my-kind"},
{Group: "second-group", Version: "first-version", Kind: "my-kind"},
},
ExpectedKindErr: " matches multiple kinds ",
},
}
for _, testCase := range testCases {
tcName := testCase.Name
mapper := NewDefaultRESTMapper(testCase.PreferredOrder, fakeInterfaces)
for _, kind := range testCase.KindsToRegister {
mapper.Add(kind, RESTScopeNamespace)
}
actualKinds, err := mapper.KindsFor(testCase.PartialResourceToRequest)
if err != nil {
t.Errorf("%s: unexpected error: %v", tcName, err)
continue
}
if !reflect.DeepEqual(testCase.ExpectedKinds, actualKinds) {
t.Errorf("%s: expected %v, got %v", tcName, testCase.ExpectedKinds, actualKinds)
}
singleKind, err := mapper.KindFor(testCase.PartialResourceToRequest)
if err == nil && len(testCase.ExpectedKindErr) != 0 {
t.Errorf("%s: expected error: %v", tcName, testCase.ExpectedKindErr)
continue
}
if err != nil {
if len(testCase.ExpectedKindErr) == 0 {
t.Errorf("%s: unexpected error: %v", tcName, err)
continue
} else {
if !strings.Contains(err.Error(), testCase.ExpectedKindErr) {
t.Errorf("%s: expected %v, got %v", tcName, testCase.ExpectedKindErr, err)
continue
}
}
} else {
if testCase.ExpectedKinds[0] != singleKind {
t.Errorf("%s: expected %v, got %v", tcName, testCase.ExpectedKinds[0], singleKind)
}
}
}
}
func TestRESTMapperResourcesFor(t *testing.T) {
testCases := []struct {
Name string
PreferredOrder []schema.GroupVersion
KindsToRegister []schema.GroupVersionKind
PluralPartialResourceToRequest schema.GroupVersionResource
SingularPartialResourceToRequest schema.GroupVersionResource
ExpectedResources []schema.GroupVersionResource
ExpectedResourceErr string
}{
{
// exact matches are prefered
Name: "groups, with group exact",
PreferredOrder: []schema.GroupVersion{
{Group: "first-group-1", Version: "first-version"},
{Group: "first-group", Version: "first-version"},
},
KindsToRegister: []schema.GroupVersionKind{
{Group: "first-group-1", Version: "first-version", Kind: "my-kind"},
{Group: "first-group", Version: "first-version", Kind: "my-kind"},
},
PluralPartialResourceToRequest: schema.GroupVersionResource{Group: "first-group", Resource: "my-kinds"},
SingularPartialResourceToRequest: schema.GroupVersionResource{Group: "first-group", Resource: "my-kind"},
ExpectedResources: []schema.GroupVersionResource{
{Group: "first-group", Version: "first-version", Resource: "my-kinds"},
},
},
{
// group prefixes work
Name: "groups, with group prefix",
PreferredOrder: []schema.GroupVersion{
{Group: "second-group", Version: "first-version"},
{Group: "first-group", Version: "first-version"},
},
KindsToRegister: []schema.GroupVersionKind{
{Group: "first-group", Version: "first-version", Kind: "my-kind"},
{Group: "second-group", Version: "first-version", Kind: "my-kind"},
},
PluralPartialResourceToRequest: schema.GroupVersionResource{Group: "first", Resource: "my-kinds"},
SingularPartialResourceToRequest: schema.GroupVersionResource{Group: "first", Resource: "my-kind"},
ExpectedResources: []schema.GroupVersionResource{
{Group: "first-group", Version: "first-version", Resource: "my-kinds"},
},
},
{
// group prefixes can be ambiguous
Name: "groups, with ambiguous group prefix",
PreferredOrder: []schema.GroupVersion{
{Group: "first-group-1", Version: "first-version"},
{Group: "first-group", Version: "first-version"},
},
KindsToRegister: []schema.GroupVersionKind{
{Group: "first-group-1", Version: "first-version", Kind: "my-kind"},
{Group: "first-group", Version: "first-version", Kind: "my-kind"},
},
PluralPartialResourceToRequest: schema.GroupVersionResource{Group: "first", Resource: "my-kinds"},
SingularPartialResourceToRequest: schema.GroupVersionResource{Group: "first", Resource: "my-kind"},
ExpectedResources: []schema.GroupVersionResource{
{Group: "first-group-1", Version: "first-version", Resource: "my-kinds"},
{Group: "first-group", Version: "first-version", Resource: "my-kinds"},
},
ExpectedResourceErr: " matches multiple resources ",
},
{
Name: "ambiguous groups, with preference order",
PreferredOrder: []schema.GroupVersion{
{Group: "second-group", Version: "first-version"},
{Group: "first-group", Version: "first-version"},
},
KindsToRegister: []schema.GroupVersionKind{
{Group: "first-group", Version: "first-version", Kind: "my-kind"},
{Group: "first-group", Version: "first-version", Kind: "your-kind"},
{Group: "second-group", Version: "first-version", Kind: "my-kind"},
{Group: "second-group", Version: "first-version", Kind: "your-kind"},
},
PluralPartialResourceToRequest: schema.GroupVersionResource{Resource: "my-kinds"},
SingularPartialResourceToRequest: schema.GroupVersionResource{Resource: "my-kind"},
ExpectedResources: []schema.GroupVersionResource{
{Group: "second-group", Version: "first-version", Resource: "my-kinds"},
{Group: "first-group", Version: "first-version", Resource: "my-kinds"},
},
ExpectedResourceErr: " matches multiple resources ",
},
{
Name: "ambiguous groups, with explicit group match",
PreferredOrder: []schema.GroupVersion{
{Group: "second-group", Version: "first-version"},
{Group: "first-group", Version: "first-version"},
},
KindsToRegister: []schema.GroupVersionKind{
{Group: "first-group", Version: "first-version", Kind: "my-kind"},
{Group: "first-group", Version: "first-version", Kind: "your-kind"},
{Group: "second-group", Version: "first-version", Kind: "my-kind"},
{Group: "second-group", Version: "first-version", Kind: "your-kind"},
},
PluralPartialResourceToRequest: schema.GroupVersionResource{Group: "first-group", Resource: "my-kinds"},
SingularPartialResourceToRequest: schema.GroupVersionResource{Group: "first-group", Resource: "my-kind"},
ExpectedResources: []schema.GroupVersionResource{
{Group: "first-group", Version: "first-version", Resource: "my-kinds"},
},
},
{
Name: "ambiguous groups, with ambiguous version match",
PreferredOrder: []schema.GroupVersion{
{Group: "first-group", Version: "first-version"},
{Group: "second-group", Version: "first-version"},
},
KindsToRegister: []schema.GroupVersionKind{
{Group: "first-group", Version: "first-version", Kind: "my-kind"},
{Group: "first-group", Version: "first-version", Kind: "your-kind"},
{Group: "second-group", Version: "first-version", Kind: "my-kind"},
{Group: "second-group", Version: "first-version", Kind: "your-kind"},
},
PluralPartialResourceToRequest: schema.GroupVersionResource{Version: "first-version", Resource: "my-kinds"},
SingularPartialResourceToRequest: schema.GroupVersionResource{Version: "first-version", Resource: "my-kind"},
ExpectedResources: []schema.GroupVersionResource{
{Group: "first-group", Version: "first-version", Resource: "my-kinds"},
{Group: "second-group", Version: "first-version", Resource: "my-kinds"},
},
ExpectedResourceErr: " matches multiple resources ",
},
}
for _, testCase := range testCases {
tcName := testCase.Name
for _, partialResource := range []schema.GroupVersionResource{testCase.PluralPartialResourceToRequest, testCase.SingularPartialResourceToRequest} {
mapper := NewDefaultRESTMapper(testCase.PreferredOrder, fakeInterfaces)
for _, kind := range testCase.KindsToRegister {
mapper.Add(kind, RESTScopeNamespace)
}
actualResources, err := mapper.ResourcesFor(partialResource)
if err != nil {
t.Errorf("%s: unexpected error: %v", tcName, err)
continue
}
if !reflect.DeepEqual(testCase.ExpectedResources, actualResources) {
t.Errorf("%s: expected %v, got %v", tcName, testCase.ExpectedResources, actualResources)
}
singleResource, err := mapper.ResourceFor(partialResource)
if err == nil && len(testCase.ExpectedResourceErr) != 0 {
t.Errorf("%s: expected error: %v", tcName, testCase.ExpectedResourceErr)
continue
}
if err != nil {
if len(testCase.ExpectedResourceErr) == 0 {
t.Errorf("%s: unexpected error: %v", tcName, err)
continue
} else {
if !strings.Contains(err.Error(), testCase.ExpectedResourceErr) {
t.Errorf("%s: expected %v, got %v", tcName, testCase.ExpectedResourceErr, err)
continue
}
}
} else {
if testCase.ExpectedResources[0] != singleResource {
t.Errorf("%s: expected %v, got %v", tcName, testCase.ExpectedResources[0], singleResource)
}
}
}
}
}
func TestKindToResource(t *testing.T) {
testCases := []struct {
Kind string
Plural, Singular string
}{
{Kind: "Pod", Plural: "pods", Singular: "pod"},
{Kind: "ReplicationController", Plural: "replicationcontrollers", Singular: "replicationcontroller"},
// Add "ies" when ending with "y"
{Kind: "ImageRepository", Plural: "imagerepositories", Singular: "imagerepository"},
// Add "es" when ending with "s"
{Kind: "miss", Plural: "misses", Singular: "miss"},
// Add "s" otherwise
{Kind: "lowercase", Plural: "lowercases", Singular: "lowercase"},
}
for i, testCase := range testCases {
version := schema.GroupVersion{}
plural, singular := KindToResource(version.WithKind(testCase.Kind))
if singular != version.WithResource(testCase.Singular) || plural != version.WithResource(testCase.Plural) {
t.Errorf("%d: unexpected plural and singular: %v %v", i, plural, singular)
}
}
}
func TestRESTMapperResourceSingularizer(t *testing.T) {
testGroupVersion := schema.GroupVersion{Group: "tgroup", Version: "test"}
testCases := []struct {
Kind string
Plural string
Singular string
}{
{Kind: "Pod", Plural: "pods", Singular: "pod"},
{Kind: "ReplicationController", Plural: "replicationcontrollers", Singular: "replicationcontroller"},
{Kind: "ImageRepository", Plural: "imagerepositories", Singular: "imagerepository"},
{Kind: "Status", Plural: "statuses", Singular: "status"},
{Kind: "lowercase", Plural: "lowercases", Singular: "lowercase"},
// TODO this test is broken. This updates to reflect actual behavior. Kinds are expected to be singular
// old (incorrect), coment: Don't add extra s if the original object is already plural
{Kind: "lowercases", Plural: "lowercaseses", Singular: "lowercases"},
}
for i, testCase := range testCases {
mapper := NewDefaultRESTMapper([]schema.GroupVersion{testGroupVersion}, fakeInterfaces)
// create singular/plural mapping
mapper.Add(testGroupVersion.WithKind(testCase.Kind), RESTScopeNamespace)
singular, err := mapper.ResourceSingularizer(testCase.Plural)
if err != nil {
t.Errorf("%d: unexpected error: %v", i, err)
}
if singular != testCase.Singular {
t.Errorf("%d: mismatched singular: got %v, expected %v", i, singular, testCase.Singular)
}
}
}
func TestRESTMapperRESTMapping(t *testing.T) {
testGroup := "tgroup"
testGroupVersion := schema.GroupVersion{Group: testGroup, Version: "test"}
internalGroupVersion := schema.GroupVersion{Group: testGroup, Version: "test"}
testCases := []struct {
Kind string
APIGroupVersions []schema.GroupVersion
DefaultVersions []schema.GroupVersion
Resource string
ExpectedGroupVersion *schema.GroupVersion
Err bool
}{
{Kind: "Unknown", Err: true},
{Kind: "InternalObject", Err: true},
{DefaultVersions: []schema.GroupVersion{testGroupVersion}, Kind: "Unknown", Err: true},
{DefaultVersions: []schema.GroupVersion{testGroupVersion}, Kind: "InternalObject", APIGroupVersions: []schema.GroupVersion{{Group: testGroup, Version: "test"}}, Resource: "internalobjects"},
{DefaultVersions: []schema.GroupVersion{testGroupVersion}, Kind: "InternalObject", APIGroupVersions: []schema.GroupVersion{{Group: testGroup, Version: "test"}}, Resource: "internalobjects"},
{DefaultVersions: []schema.GroupVersion{testGroupVersion}, Kind: "InternalObject", APIGroupVersions: []schema.GroupVersion{{Group: testGroup, Version: "test"}}, Resource: "internalobjects"},
{DefaultVersions: []schema.GroupVersion{testGroupVersion}, Kind: "InternalObject", APIGroupVersions: []schema.GroupVersion{}, Resource: "internalobjects", ExpectedGroupVersion: &schema.GroupVersion{Group: testGroup, Version: "test"}},
{DefaultVersions: []schema.GroupVersion{testGroupVersion}, Kind: "InternalObject", APIGroupVersions: []schema.GroupVersion{{Group: testGroup, Version: "test"}}, Resource: "internalobjects"},
// TODO: add test for a resource that exists in one version but not another
}
for i, testCase := range testCases {
mapper := NewDefaultRESTMapper(testCase.DefaultVersions, fakeInterfaces)
mapper.Add(internalGroupVersion.WithKind("InternalObject"), RESTScopeNamespace)
preferredVersions := []string{}
for _, gv := range testCase.APIGroupVersions {
preferredVersions = append(preferredVersions, gv.Version)
}
gk := schema.GroupKind{Group: testGroup, Kind: testCase.Kind}
mapping, err := mapper.RESTMapping(gk, preferredVersions...)
hasErr := err != nil
if hasErr != testCase.Err {
t.Errorf("%d: unexpected error behavior %t: %v", i, testCase.Err, err)
}
if hasErr {
continue
}
if mapping.Resource != testCase.Resource {
t.Errorf("%d: unexpected resource: %#v", i, mapping)
}
if mapping.MetadataAccessor == nil || mapping.ObjectConvertor == nil {
t.Errorf("%d: missing codec and accessor: %#v", i, mapping)
}
groupVersion := testCase.ExpectedGroupVersion
if groupVersion == nil {
groupVersion = &testCase.APIGroupVersions[0]
}
if mapping.GroupVersionKind.GroupVersion() != *groupVersion {
t.Errorf("%d: unexpected version: %#v", i, mapping)
}
}
}
func TestRESTMapperRESTMappingSelectsVersion(t *testing.T) {
expectedGroupVersion1 := schema.GroupVersion{Group: "tgroup", Version: "test1"}
expectedGroupVersion2 := schema.GroupVersion{Group: "tgroup", Version: "test2"}
expectedGroupVersion3 := schema.GroupVersion{Group: "tgroup", Version: "test3"}
internalObjectGK := schema.GroupKind{Group: "tgroup", Kind: "InternalObject"}
otherObjectGK := schema.GroupKind{Group: "tgroup", Kind: "OtherObject"}
mapper := NewDefaultRESTMapper([]schema.GroupVersion{expectedGroupVersion1, expectedGroupVersion2}, fakeInterfaces)
mapper.Add(expectedGroupVersion1.WithKind("InternalObject"), RESTScopeNamespace)
mapper.Add(expectedGroupVersion2.WithKind("OtherObject"), RESTScopeNamespace)
// pick default matching object kind based on search order
mapping, err := mapper.RESTMapping(otherObjectGK)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if mapping.Resource != "otherobjects" || mapping.GroupVersionKind.GroupVersion() != expectedGroupVersion2 {
t.Errorf("unexpected mapping: %#v", mapping)
}
mapping, err = mapper.RESTMapping(internalObjectGK)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if mapping.Resource != "internalobjects" || mapping.GroupVersionKind.GroupVersion() != expectedGroupVersion1 {
t.Errorf("unexpected mapping: %#v", mapping)
}
// mismatch of version
mapping, err = mapper.RESTMapping(internalObjectGK, expectedGroupVersion2.Version)
if err == nil {
t.Errorf("unexpected non-error")
}
mapping, err = mapper.RESTMapping(otherObjectGK, expectedGroupVersion1.Version)
if err == nil {
t.Errorf("unexpected non-error")
}
// not in the search versions
mapping, err = mapper.RESTMapping(otherObjectGK, expectedGroupVersion3.Version)
if err == nil {
t.Errorf("unexpected non-error")
}
// explicit search order
mapping, err = mapper.RESTMapping(otherObjectGK, expectedGroupVersion3.Version, expectedGroupVersion1.Version)
if err == nil {
t.Errorf("unexpected non-error")
}
mapping, err = mapper.RESTMapping(otherObjectGK, expectedGroupVersion3.Version, expectedGroupVersion2.Version)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if mapping.Resource != "otherobjects" || mapping.GroupVersionKind.GroupVersion() != expectedGroupVersion2 {
t.Errorf("unexpected mapping: %#v", mapping)
}
}
func TestRESTMapperRESTMappings(t *testing.T) {
testGroup := "tgroup"
testGroupVersion := schema.GroupVersion{Group: testGroup, Version: "v1"}
testCases := []struct {
Kind string
APIGroupVersions []schema.GroupVersion
DefaultVersions []schema.GroupVersion
AddGroupVersionKind []schema.GroupVersionKind
ExpectedRESTMappings []*RESTMapping
Err bool
}{
{Kind: "Unknown", Err: true},
{Kind: "InternalObject", Err: true},
{DefaultVersions: []schema.GroupVersion{testGroupVersion}, Kind: "Unknown", Err: true},
// ask for specific version - not available - thus error
{DefaultVersions: []schema.GroupVersion{testGroupVersion}, Kind: "InternalObject", APIGroupVersions: []schema.GroupVersion{{Group: testGroup, Version: "v2"}}, Err: true},
// ask for specific version - available - check ExpectedRESTMappings
{
DefaultVersions: []schema.GroupVersion{testGroupVersion},
Kind: "InternalObject",
APIGroupVersions: []schema.GroupVersion{{Group: testGroup, Version: "v2"}},
AddGroupVersionKind: []schema.GroupVersionKind{schema.GroupVersion{Group: testGroup, Version: "v2"}.WithKind("InternalObject")},
ExpectedRESTMappings: []*RESTMapping{{Resource: "internalobjects", GroupVersionKind: schema.GroupVersionKind{Group: testGroup, Version: "v2", Kind: "InternalObject"}}},
},
// ask for specific versions - only one available - check ExpectedRESTMappings
{
DefaultVersions: []schema.GroupVersion{testGroupVersion},
Kind: "InternalObject",
APIGroupVersions: []schema.GroupVersion{{Group: testGroup, Version: "v3"}, {Group: testGroup, Version: "v2"}},
AddGroupVersionKind: []schema.GroupVersionKind{schema.GroupVersion{Group: testGroup, Version: "v2"}.WithKind("InternalObject")},
ExpectedRESTMappings: []*RESTMapping{{Resource: "internalobjects", GroupVersionKind: schema.GroupVersionKind{Group: testGroup, Version: "v2", Kind: "InternalObject"}}},
},
// do not ask for specific version - search through default versions - check ExpectedRESTMappings
{
DefaultVersions: []schema.GroupVersion{testGroupVersion, {Group: testGroup, Version: "v2"}},
Kind: "InternalObject",
AddGroupVersionKind: []schema.GroupVersionKind{schema.GroupVersion{Group: testGroup, Version: "v1"}.WithKind("InternalObject"), schema.GroupVersion{Group: testGroup, Version: "v2"}.WithKind("InternalObject")},
ExpectedRESTMappings: []*RESTMapping{{Resource: "internalobjects", GroupVersionKind: schema.GroupVersionKind{Group: testGroup, Version: "v1", Kind: "InternalObject"}}, {Resource: "internalobjects", GroupVersionKind: schema.GroupVersionKind{Group: testGroup, Version: "v2", Kind: "InternalObject"}}},
},
}
for i, testCase := range testCases {
mapper := NewDefaultRESTMapper(testCase.DefaultVersions, fakeInterfaces)
for _, gvk := range testCase.AddGroupVersionKind {
mapper.Add(gvk, RESTScopeNamespace)
}
preferredVersions := []string{}
for _, gv := range testCase.APIGroupVersions {
preferredVersions = append(preferredVersions, gv.Version)
}
gk := schema.GroupKind{Group: testGroup, Kind: testCase.Kind}
mappings, err := mapper.RESTMappings(gk, preferredVersions...)
hasErr := err != nil
if hasErr != testCase.Err {
t.Errorf("%d: unexpected error behavior %t: %v", i, testCase.Err, err)
}
if hasErr {
continue
}
if len(mappings) != len(testCase.ExpectedRESTMappings) {
t.Errorf("%d: unexpected number = %d of rest mappings was returned, expected = %d", i, len(mappings), len(testCase.ExpectedRESTMappings))
}
for j, mapping := range mappings {
exp := testCase.ExpectedRESTMappings[j]
if mapping.Resource != exp.Resource {
t.Errorf("%d - %d: unexpected resource: %#v", i, j, mapping)
}
if mapping.MetadataAccessor == nil || mapping.ObjectConvertor == nil {
t.Errorf("%d - %d: missing codec and accessor: %#v", i, j, mapping)
}
if mapping.GroupVersionKind != exp.GroupVersionKind {
t.Errorf("%d - %d: unexpected GroupVersionKind: %#v", i, j, mapping)
}
}
}
}
func TestRESTMapperReportsErrorOnBadVersion(t *testing.T) {
expectedGroupVersion1 := schema.GroupVersion{Group: "tgroup", Version: "test1"}
expectedGroupVersion2 := schema.GroupVersion{Group: "tgroup", Version: "test2"}
internalObjectGK := schema.GroupKind{Group: "tgroup", Kind: "InternalObject"}
mapper := NewDefaultRESTMapper([]schema.GroupVersion{expectedGroupVersion1, expectedGroupVersion2}, unmatchedVersionInterfaces)
mapper.Add(expectedGroupVersion1.WithKind("InternalObject"), RESTScopeNamespace)
_, err := mapper.RESTMapping(internalObjectGK, expectedGroupVersion1.Version)
if err == nil {
t.Errorf("unexpected non-error")
}
}

View file

@ -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
}

17
vendor/k8s.io/apimachinery/pkg/api/resource/OWNERS generated vendored Executable file
View file

@ -0,0 +1,17 @@
reviewers:
- thockin
- lavalamp
- smarterclayton
- wojtek-t
- derekwaynecarr
- mikedanese
- saad-ali
- janetkuo
- timstclair
- eparis
- timothysc
- jbeda
- xiang90
- mbohlool
- david-mcmahon
- goltermann

299
vendor/k8s.io/apimachinery/pkg/api/resource/amount.go generated vendored Normal file
View file

@ -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
}

View file

@ -0,0 +1,111 @@
/*
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 (
"testing"
)
func TestInt64AmountAsInt64(t *testing.T) {
for _, test := range []struct {
value int64
scale Scale
result int64
ok bool
}{
{100, 0, 100, true},
{100, 1, 1000, true},
{100, -5, 0, false},
{100, 100, 0, false},
} {
r, ok := int64Amount{value: test.value, scale: test.scale}.AsInt64()
if r != test.result {
t.Errorf("%v: unexpected result: %d", test, r)
}
if ok != test.ok {
t.Errorf("%v: unexpected ok: %t", test, ok)
}
}
}
func TestInt64AmountAdd(t *testing.T) {
for _, test := range []struct {
a, b, c int64Amount
ok bool
}{
{int64Amount{value: 100, scale: 1}, int64Amount{value: 10, scale: 2}, int64Amount{value: 200, scale: 1}, true},
{int64Amount{value: 100, scale: 1}, int64Amount{value: 1, scale: 2}, int64Amount{value: 110, scale: 1}, true},
{int64Amount{value: 100, scale: 1}, int64Amount{value: 1, scale: 100}, int64Amount{value: 1, scale: 100}, false},
{int64Amount{value: -5, scale: 2}, int64Amount{value: 50, scale: 1}, int64Amount{value: 0, scale: 1}, true},
{int64Amount{value: -5, scale: 2}, int64Amount{value: 5, scale: 2}, int64Amount{value: 0, scale: 2}, true},
{int64Amount{value: mostPositive, scale: -1}, int64Amount{value: 1, scale: -1}, int64Amount{value: 0, scale: -1}, false},
{int64Amount{value: mostPositive, scale: -1}, int64Amount{value: 0, scale: -1}, int64Amount{value: mostPositive, scale: -1}, true},
{int64Amount{value: mostPositive / 10, scale: 1}, int64Amount{value: 10, scale: 0}, int64Amount{value: mostPositive, scale: -1}, false},
} {
c := test.a
ok := c.Add(test.b)
if ok != test.ok {
t.Errorf("%v: unexpected ok: %t", test, ok)
}
if ok {
if c != test.c {
t.Errorf("%v: unexpected result: %d", test, c)
}
} else {
if c != test.a {
t.Errorf("%v: overflow addition mutated source: %d", test, c)
}
}
// addition is commutative
c = test.b
if ok := c.Add(test.a); ok != test.ok {
t.Errorf("%v: unexpected ok: %t", test, ok)
}
if ok {
if c != test.c {
t.Errorf("%v: unexpected result: %d", test, c)
}
} else {
if c != test.b {
t.Errorf("%v: overflow addition mutated source: %d", test, c)
}
}
}
}
func TestInt64AsCanonicalString(t *testing.T) {
for _, test := range []struct {
value int64
scale Scale
result string
exponent int32
}{
{100, 0, "100", 0},
{100, 1, "1", 3},
{100, -1, "10", 0},
{10800, -10, "1080", -9},
} {
r, exp := int64Amount{value: test.value, scale: test.scale}.AsCanonicalBytes(nil)
if string(r) != test.result {
t.Errorf("%v: unexpected result: %s", test, r)
}
if exp != test.exponent {
t.Errorf("%v: unexpected exponent: %d", test, exp)
}
}
}

View file

@ -0,0 +1,71 @@
/*
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.
const _ = proto.GoGoProtoPackageIsVersion1
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")
}
var fileDescriptorGenerated = []byte{
// 253 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x74, 0x8f, 0xb1, 0x4a, 0x03, 0x41,
0x10, 0x86, 0x77, 0x1b, 0x89, 0x57, 0x06, 0x11, 0x49, 0xb1, 0x17, 0xc4, 0x42, 0x04, 0x77, 0x0a,
0x9b, 0x60, 0x69, 0x6f, 0xa1, 0xa5, 0xdd, 0xdd, 0x65, 0xdc, 0x2c, 0x67, 0x76, 0x8f, 0xd9, 0x59,
0x21, 0x5d, 0x4a, 0xcb, 0x94, 0x96, 0xb9, 0xb7, 0x49, 0x99, 0xd2, 0xc2, 0xc2, 0x3b, 0x5f, 0x44,
0x72, 0xc9, 0x81, 0x08, 0x76, 0xf3, 0xfd, 0xc3, 0x37, 0xfc, 0x93, 0xdc, 0x97, 0x93, 0xa0, 0xad,
0x87, 0x32, 0xe6, 0x48, 0x0e, 0x19, 0x03, 0xbc, 0xa2, 0x9b, 0x7a, 0x82, 0xc3, 0x22, 0xab, 0xec,
0x3c, 0x2b, 0x66, 0xd6, 0x21, 0x2d, 0xa0, 0x2a, 0xcd, 0x2e, 0x00, 0xc2, 0xe0, 0x23, 0x15, 0x08,
0x06, 0x1d, 0x52, 0xc6, 0x38, 0xd5, 0x15, 0x79, 0xf6, 0xc3, 0x8b, 0xbd, 0xa5, 0x7f, 0x5b, 0xba,
0x2a, 0xcd, 0x2e, 0xd0, 0xbd, 0x35, 0xba, 0x36, 0x96, 0x67, 0x31, 0xd7, 0x85, 0x9f, 0x83, 0xf1,
0xc6, 0x43, 0x27, 0xe7, 0xf1, 0xb9, 0xa3, 0x0e, 0xba, 0x69, 0x7f, 0x74, 0x74, 0xf3, 0x5f, 0x95,
0xc8, 0xf6, 0x05, 0xac, 0xe3, 0xc0, 0xf4, 0xb7, 0xc9, 0xf9, 0x24, 0x19, 0x3c, 0xc4, 0xcc, 0xb1,
0xe5, 0xc5, 0xf0, 0x34, 0x39, 0x0a, 0x4c, 0xd6, 0x99, 0x33, 0x39, 0x96, 0x97, 0xc7, 0x8f, 0x07,
0xba, 0x3d, 0x79, 0x5f, 0xa7, 0xe2, 0xad, 0x4e, 0xc5, 0xaa, 0x4e, 0xc5, 0xba, 0x4e, 0xc5, 0xf2,
0x73, 0x2c, 0xee, 0xae, 0x36, 0x8d, 0x12, 0xdb, 0x46, 0x89, 0x8f, 0x46, 0x89, 0x65, 0xab, 0xe4,
0xa6, 0x55, 0x72, 0xdb, 0x2a, 0xf9, 0xd5, 0x2a, 0xb9, 0xfa, 0x56, 0xe2, 0x69, 0xd0, 0x7f, 0xf2,
0x13, 0x00, 0x00, 0xff, 0xff, 0xdf, 0x3c, 0xf3, 0xc9, 0x3f, 0x01, 0x00, 0x00,
}

View file

@ -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;
}

327
vendor/k8s.io/apimachinery/pkg/api/resource/math.go generated vendored Normal file
View file

@ -0,0 +1,327 @@
/*
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
}
}
// powInt64 raises a to the bth power. Is not overflow aware.
func powInt64(a, b int64) int64 {
p := int64(1)
for b > 0 {
if b&1 != 0 {
p *= a
}
b >>= 1
a *= a
}
return p
}
// 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
}

View file

@ -0,0 +1,211 @@
/*
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 (
"testing"
)
func TestDetectOverflowAdd(t *testing.T) {
for _, test := range []struct {
a, b int64
c int64
ok bool
}{
{0, 0, 0, true},
{-1, 1, 0, true},
{0, 1, 1, true},
{2, 2, 4, true},
{2, -2, 0, true},
{-2, -2, -4, true},
{mostNegative, -1, 0, false},
{mostNegative, 1, mostNegative + 1, true},
{mostPositive, -1, mostPositive - 1, true},
{mostPositive, 1, 0, false},
{mostNegative, mostPositive, -1, true},
{mostPositive, mostNegative, -1, true},
{mostPositive, mostPositive, 0, false},
{mostNegative, mostNegative, 0, false},
{-mostPositive, mostNegative, 0, false},
{mostNegative, -mostPositive, 0, false},
{-mostPositive, -mostPositive, 0, false},
} {
c, ok := int64Add(test.a, test.b)
if c != test.c {
t.Errorf("%v: unexpected result: %d", test, c)
}
if ok != test.ok {
t.Errorf("%v: unexpected overflow: %t", test, ok)
}
// addition is commutative
d, ok2 := int64Add(test.b, test.a)
if c != d || ok != ok2 {
t.Errorf("%v: not commutative: %d %t", test, d, ok2)
}
}
}
func TestDetectOverflowMultiply(t *testing.T) {
for _, test := range []struct {
a, b int64
c int64
ok bool
}{
{0, 0, 0, true},
{-1, 1, -1, true},
{-1, -1, 1, true},
{1, 1, 1, true},
{0, 1, 0, true},
{1, 0, 0, true},
{2, 2, 4, true},
{2, -2, -4, true},
{-2, -2, 4, true},
{mostNegative, -1, 0, false},
{mostNegative, 1, mostNegative, true},
{mostPositive, -1, -mostPositive, true},
{mostPositive, 1, mostPositive, true},
{mostNegative, mostPositive, 0, false},
{mostPositive, mostNegative, 0, false},
{mostPositive, mostPositive, 1, false},
{mostNegative, mostNegative, 0, false},
{-mostPositive, mostNegative, 0, false},
{mostNegative, -mostPositive, 0, false},
{-mostPositive, -mostPositive, 1, false},
} {
c, ok := int64Multiply(test.a, test.b)
if c != test.c {
t.Errorf("%v: unexpected result: %d", test, c)
}
if ok != test.ok {
t.Errorf("%v: unexpected overflow: %t", test, ok)
}
// multiplication is commutative
d, ok2 := int64Multiply(test.b, test.a)
if c != d || ok != ok2 {
t.Errorf("%v: not commutative: %d %t", test, d, ok2)
}
}
}
func TestDetectOverflowScale(t *testing.T) {
for _, a := range []int64{0, -1, 1, 10, -10, mostPositive, mostNegative, -mostPositive} {
for _, b := range []int64{1, 2, 10, 100, 1000, mostPositive} {
expect, expectOk := int64Multiply(a, b)
c, ok := int64MultiplyScale(a, b)
if c != expect {
t.Errorf("%d*%d: unexpected result: %d", a, b, c)
}
if ok != expectOk {
t.Errorf("%d*%d: unexpected overflow: %t", a, b, ok)
}
}
for _, test := range []struct {
base int64
fn func(a int64) (int64, bool)
}{
{10, int64MultiplyScale10},
{100, int64MultiplyScale100},
{1000, int64MultiplyScale1000},
} {
expect, expectOk := int64Multiply(a, test.base)
c, ok := test.fn(a)
if c != expect {
t.Errorf("%d*%d: unexpected result: %d", a, test.base, c)
}
if ok != expectOk {
t.Errorf("%d*%d: unexpected overflow: %t", a, test.base, ok)
}
}
}
}
func TestRemoveInt64Factors(t *testing.T) {
for _, test := range []struct {
value int64
max int64
result int64
scale int32
}{
{100, 10, 1, 2},
{100, 10, 1, 2},
{100, 100, 1, 1},
{1, 10, 1, 0},
} {
r, s := removeInt64Factors(test.value, test.max)
if r != test.result {
t.Errorf("%v: unexpected result: %d", test, r)
}
if s != test.scale {
t.Errorf("%v: unexpected scale: %d", test, s)
}
}
}
func TestNegativeScaleInt64(t *testing.T) {
for _, test := range []struct {
base int64
scale Scale
result int64
exact bool
}{
{1234567, 0, 1234567, true},
{1234567, 1, 123457, false},
{1234567, 2, 12346, false},
{1234567, 3, 1235, false},
{1234567, 4, 124, false},
{-1234567, 0, -1234567, true},
{-1234567, 1, -123457, false},
{-1234567, 2, -12346, false},
{-1234567, 3, -1235, false},
{-1234567, 4, -124, false},
{1000, 0, 1000, true},
{1000, 1, 100, true},
{1000, 2, 10, true},
{1000, 3, 1, true},
{1000, 4, 1, false},
{-1000, 0, -1000, true},
{-1000, 1, -100, true},
{-1000, 2, -10, true},
{-1000, 3, -1, true},
{-1000, 4, -1, false},
{0, 0, 0, true},
{0, 1, 0, true},
{0, 2, 0, true},
// negative scale is undefined behavior
{1000, -1, 1000, true},
} {
result, exact := negativeScaleInt64(test.base, test.scale)
if result != test.result {
t.Errorf("%v: unexpected result: %d", test, result)
}
if exact != test.exact {
t.Errorf("%v: unexpected exact: %t", test, exact)
}
}
}

792
vendor/k8s.io/apimachinery/pkg/api/resource/quantity.go generated vendored Normal file
View file

@ -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}
}

View file

@ -0,0 +1,59 @@
/*
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_test
import (
"fmt"
"k8s.io/apimachinery/pkg/api/resource"
)
func ExampleFormat() {
memorySize := resource.NewQuantity(5*1024*1024*1024, resource.BinarySI)
fmt.Printf("memorySize = %v\n", memorySize)
diskSize := resource.NewQuantity(5*1000*1000*1000, resource.DecimalSI)
fmt.Printf("diskSize = %v\n", diskSize)
cores := resource.NewMilliQuantity(5300, resource.DecimalSI)
fmt.Printf("cores = %v\n", cores)
// Output:
// memorySize = 5Gi
// diskSize = 5G
// cores = 5300m
}
func ExampleMustParse() {
memorySize := resource.MustParse("5Gi")
fmt.Printf("memorySize = %v (%v)\n", memorySize.Value(), memorySize.Format)
diskSize := resource.MustParse("5G")
fmt.Printf("diskSize = %v (%v)\n", diskSize.Value(), diskSize.Format)
cores := resource.MustParse("5300m")
fmt.Printf("milliCores = %v (%v)\n", cores.MilliValue(), cores.Format)
cores2 := resource.MustParse("5.4")
fmt.Printf("milliCores = %v (%v)\n", cores2.MilliValue(), cores2.Format)
// Output:
// memorySize = 5368709120 (BinarySI)
// diskSize = 5000000000 (DecimalSI)
// milliCores = 5300 (DecimalSI)
// milliCores = 5400 (DecimalSI)
}

View file

@ -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")
)

File diff suppressed because it is too large Load diff

View file

@ -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()
}

View file

@ -0,0 +1,85 @@
/*
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"
"testing"
)
func TestScaledValueInternal(t *testing.T) {
tests := []struct {
unscaled *big.Int
scale int
newScale int
want int64
}{
// remain scale
{big.NewInt(1000), 0, 0, 1000},
// scale down
{big.NewInt(1000), 0, -3, 1},
{big.NewInt(1000), 3, 0, 1},
{big.NewInt(0), 3, 0, 0},
// always round up
{big.NewInt(999), 3, 0, 1},
{big.NewInt(500), 3, 0, 1},
{big.NewInt(499), 3, 0, 1},
{big.NewInt(1), 3, 0, 1},
// large scaled value does not lose precision
{big.NewInt(0).Sub(maxInt64, bigOne), 1, 0, (math.MaxInt64-1)/10 + 1},
// large intermidiate result.
{big.NewInt(1).Exp(big.NewInt(10), big.NewInt(100), nil), 100, 0, 1},
// scale up
{big.NewInt(0), 0, 3, 0},
{big.NewInt(1), 0, 3, 1000},
{big.NewInt(1), -3, 0, 1000},
{big.NewInt(1000), -3, 2, 100000000},
{big.NewInt(0).Div(big.NewInt(math.MaxInt64), bigThousand), 0, 3,
(math.MaxInt64 / 1000) * 1000},
}
for i, tt := range tests {
old := (&big.Int{}).Set(tt.unscaled)
got := scaledValue(tt.unscaled, tt.scale, tt.newScale)
if got != tt.want {
t.Errorf("#%d: got = %v, want %v", i, got, tt.want)
}
if tt.unscaled.Cmp(old) != 0 {
t.Errorf("#%d: unscaled = %v, want %v", i, tt.unscaled, old)
}
}
}
func BenchmarkScaledValueSmall(b *testing.B) {
s := big.NewInt(1000)
for i := 0; i < b.N; i++ {
scaledValue(s, 3, 0)
}
}
func BenchmarkScaledValueLarge(b *testing.B) {
s := big.NewInt(math.MaxInt64)
s.Mul(s, big.NewInt(1000))
for i := 0; i < b.N; i++ {
scaledValue(s, 10, 0)
}
}

198
vendor/k8s.io/apimachinery/pkg/api/resource/suffix.go generated vendored Normal file
View file

@ -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
}

133
vendor/k8s.io/apimachinery/pkg/api/testing/fuzzer.go generated vendored Normal file
View file

@ -0,0 +1,133 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package testing
import (
"strconv"
"math/rand"
"testing"
"github.com/google/gofuzz"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
)
func GenericFuzzerFuncs(t TestingCommon) []interface{} {
return []interface{}{
func(j *int, c fuzz.Continue) {
*j = int(c.Int31())
},
func(j **int, c fuzz.Continue) {
if c.RandBool() {
i := int(c.Int31())
*j = &i
} else {
*j = nil
}
},
func(j *runtime.TypeMeta, c fuzz.Continue) {
// We have to customize the randomization of TypeMetas because their
// APIVersion and Kind must remain blank in memory.
j.APIVersion = ""
j.Kind = ""
},
func(j *metav1.TypeMeta, c fuzz.Continue) {
// We have to customize the randomization of TypeMetas because their
// APIVersion and Kind must remain blank in memory.
j.APIVersion = ""
j.Kind = ""
},
func(j *metav1.ObjectMeta, c fuzz.Continue) {
j.Name = c.RandString()
j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 10)
j.SelfLink = c.RandString()
j.UID = types.UID(c.RandString())
j.GenerateName = c.RandString()
var sec, nsec int64
c.Fuzz(&sec)
c.Fuzz(&nsec)
j.CreationTimestamp = metav1.Unix(sec, nsec).Rfc3339Copy()
},
func(j *metav1.ListMeta, c fuzz.Continue) {
j.ResourceVersion = strconv.FormatUint(c.RandUint64(), 10)
j.SelfLink = c.RandString()
},
func(j *runtime.Object, c fuzz.Continue) {
// TODO: uncomment when round trip starts from a versioned object
if true { //c.RandBool() {
*j = &runtime.Unknown{
// We do not set TypeMeta here because it is not carried through a round trip
Raw: []byte(`{"apiVersion":"unknown.group/unknown","kind":"Something","someKey":"someValue"}`),
ContentType: runtime.ContentTypeJSON,
}
} else {
types := []runtime.Object{&metav1.Status{}, &metav1.APIGroup{}}
t := types[c.Rand.Intn(len(types))]
c.Fuzz(t)
*j = t
}
},
func(r *runtime.RawExtension, c fuzz.Continue) {
// Pick an arbitrary type and fuzz it
types := []runtime.Object{&metav1.Status{}, &metav1.APIGroup{}}
obj := types[c.Rand.Intn(len(types))]
c.Fuzz(obj)
// Find a codec for converting the object to raw bytes. This is necessary for the
// api version and kind to be correctly set be serialization.
var codec = Codec(metav1.SchemeGroupVersion)
// Convert the object to raw bytes
bytes, err := runtime.Encode(codec, obj)
if err != nil {
t.Errorf("Failed to encode object: %v", err)
return
}
// Set the bytes field on the RawExtension
r.Raw = bytes
},
}
}
// TestingCommon abstracts testing.T and testing.B
type TestingCommon interface {
Log(args ...interface{})
Logf(format string, args ...interface{})
Error(args ...interface{})
Errorf(format string, args ...interface{})
Fatal(args ...interface{})
Fatalf(format string, args ...interface{})
}
var (
_ TestingCommon = &testing.T{}
_ TestingCommon = &testing.B{}
)
// FuzzerFor can randomly populate api objects that are destined for version.
func FuzzerFor(funcs []interface{}, src rand.Source) *fuzz.Fuzzer {
f := fuzz.New().NilChance(.5).NumElements(0, 1)
if src != nil {
f.RandSource(src)
}
f.Funcs(funcs...)
return f
}

57
vendor/k8s.io/apimachinery/pkg/api/testing/register.go generated vendored Normal file
View file

@ -0,0 +1,57 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package testing
import (
"os"
"mime"
"fmt"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/apimachinery/pkg/runtime/schema"
)
var scheme = runtime.NewScheme()
var codecs = runtimeserializer.NewCodecFactory(scheme)
var serializer runtime.SerializerInfo
// Codec returns the codec for the API version to test against, as set by the
// KUBE_TEST_API_TYPE env var.
func Codec(gvs ...schema.GroupVersion) runtime.Codec {
if serializer.Serializer == nil {
return codecs.LegacyCodec(gvs...)
}
return codecs.CodecForVersions(serializer.Serializer, codecs.UniversalDeserializer(), schema.GroupVersions(gvs), nil)
}
func init() {
metav1.AddToGroupVersion(scheme, metav1.SchemeGroupVersion)
if apiMediaType := os.Getenv("KUBE_TEST_API_TYPE"); len(apiMediaType) > 0 {
var ok bool
mediaType, _, err := mime.ParseMediaType(apiMediaType)
if err != nil {
panic(err)
}
serializer, ok = runtime.SerializerInfoForMediaType(codecs.SupportedMediaTypes(), mediaType)
if !ok {
panic(fmt.Sprintf("no serializer for %s", apiMediaType))
}
}
}

18
vendor/k8s.io/apimachinery/pkg/api/validation/doc.go generated vendored Normal file
View file

@ -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"

View file

@ -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
}

View file

@ -0,0 +1,68 @@
/*
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 path
import (
"fmt"
"strings"
)
// NameMayNotBe specifies strings that cannot be used as names specified as path segments (like the REST API or etcd store)
var NameMayNotBe = []string{".", ".."}
// NameMayNotContain specifies substrings that cannot be used in names specified as path segments (like the REST API or etcd store)
var NameMayNotContain = []string{"/", "%"}
// IsValidPathSegmentName validates the name can be safely encoded as a path segment
func IsValidPathSegmentName(name string) []string {
for _, illegalName := range NameMayNotBe {
if name == illegalName {
return []string{fmt.Sprintf(`may not be '%s'`, illegalName)}
}
}
var errors []string
for _, illegalContent := range NameMayNotContain {
if strings.Contains(name, illegalContent) {
errors = append(errors, fmt.Sprintf(`may not contain '%s'`, illegalContent))
}
}
return errors
}
// IsValidPathSegmentPrefix validates the name can be used as a prefix for a name which will be encoded as a path segment
// It does not check for exact matches with disallowed names, since an arbitrary suffix might make the name valid
func IsValidPathSegmentPrefix(name string) []string {
var errors []string
for _, illegalContent := range NameMayNotContain {
if strings.Contains(name, illegalContent) {
errors = append(errors, fmt.Sprintf(`may not contain '%s'`, illegalContent))
}
}
return errors
}
// ValidatePathSegmentName validates the name can be safely encoded as a path segment
func ValidatePathSegmentName(name string, prefix bool) []string {
if prefix {
return IsValidPathSegmentPrefix(name)
} else {
return IsValidPathSegmentName(name)
}
}

View file

@ -0,0 +1,168 @@
/*
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 path
import (
"strings"
"testing"
)
func TestValidatePathSegmentName(t *testing.T) {
testcases := map[string]struct {
Name string
Prefix bool
ExpectedMsg string
}{
"empty": {
Name: "",
Prefix: false,
ExpectedMsg: "",
},
"empty,prefix": {
Name: "",
Prefix: true,
ExpectedMsg: "",
},
"valid": {
Name: "foo.bar.baz",
Prefix: false,
ExpectedMsg: "",
},
"valid,prefix": {
Name: "foo.bar.baz",
Prefix: true,
ExpectedMsg: "",
},
// Make sure mixed case, non DNS subdomain characters are tolerated
"valid complex": {
Name: "sha256:ABCDEF012345@ABCDEF012345",
Prefix: false,
ExpectedMsg: "",
},
// Make sure non-ascii characters are tolerated
"valid extended charset": {
Name: "Iñtërnâtiônàlizætiøn",
Prefix: false,
ExpectedMsg: "",
},
"dot": {
Name: ".",
Prefix: false,
ExpectedMsg: ".",
},
"dot leading": {
Name: ".test",
Prefix: false,
ExpectedMsg: "",
},
"dot,prefix": {
Name: ".",
Prefix: true,
ExpectedMsg: "",
},
"dot dot": {
Name: "..",
Prefix: false,
ExpectedMsg: "..",
},
"dot dot leading": {
Name: "..test",
Prefix: false,
ExpectedMsg: "",
},
"dot dot,prefix": {
Name: "..",
Prefix: true,
ExpectedMsg: "",
},
"slash": {
Name: "foo/bar",
Prefix: false,
ExpectedMsg: "/",
},
"slash,prefix": {
Name: "foo/bar",
Prefix: true,
ExpectedMsg: "/",
},
"percent": {
Name: "foo%bar",
Prefix: false,
ExpectedMsg: "%",
},
"percent,prefix": {
Name: "foo%bar",
Prefix: true,
ExpectedMsg: "%",
},
}
for k, tc := range testcases {
msgs := ValidatePathSegmentName(tc.Name, tc.Prefix)
if len(tc.ExpectedMsg) == 0 && len(msgs) > 0 {
t.Errorf("%s: expected no message, got %v", k, msgs)
}
if len(tc.ExpectedMsg) > 0 && len(msgs) == 0 {
t.Errorf("%s: expected error message, got none", k)
}
if len(tc.ExpectedMsg) > 0 && !strings.Contains(msgs[0], tc.ExpectedMsg) {
t.Errorf("%s: expected message containing %q, got %v", k, tc.ExpectedMsg, msgs[0])
}
}
}
func TestValidateWithMultiErrors(t *testing.T) {
testcases := map[string]struct {
Name string
Prefix bool
ExpectedMsg []string
}{
"slash,percent": {
Name: "foo//bar%",
Prefix: false,
ExpectedMsg: []string{"may not contain '/'", "may not contain '%'"},
},
"slash,percent,prefix": {
Name: "foo//bar%",
Prefix: true,
ExpectedMsg: []string{"may not contain '/'", "may not contain '%'"},
},
}
for k, tc := range testcases {
msgs := ValidatePathSegmentName(tc.Name, tc.Prefix)
if len(tc.ExpectedMsg) == 0 && len(msgs) > 0 {
t.Errorf("%s: expected no message, got %v", k, msgs)
}
if len(tc.ExpectedMsg) > 0 && len(msgs) == 0 {
t.Errorf("%s: expected error message, got none", k)
}
if len(tc.ExpectedMsg) > 0 {
for i := 0; i < len(tc.ExpectedMsg); i++ {
if msgs[i] != tc.ExpectedMsg[i] {
t.Errorf("%s: expected message containing %q, got %v", k, tc.ExpectedMsg[i], msgs[i])
}
}
}
}
}

View file

@ -0,0 +1,99 @@
/*
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 announced contains tools for announcing API group factories. This is
// distinct from registration (in the 'registered' package) in that it's safe
// to announce every possible group linked in, but only groups requested at
// runtime should be registered. This package contains both a registry, and
// factory code (which was formerly copy-pasta in every install package).
package announced
import (
"fmt"
"k8s.io/apimachinery/pkg/apimachinery/registered"
"k8s.io/apimachinery/pkg/runtime"
)
// APIGroupFactoryRegistry allows for groups and versions to announce themselves,
// which simply makes them available and doesn't take other actions. Later,
// users of the registry can select which groups and versions they'd actually
// like to register with an APIRegistrationManager.
//
// (Right now APIRegistrationManager has separate 'registration' and 'enabled'
// concepts-- APIGroupFactory is going to take over the former function;
// they will overlap untill the refactoring is finished.)
//
// The key is the group name. After initialization, this should be treated as
// read-only. It is implemented as a map from group name to group factory, and
// it is safe to use this knowledge to manually pick out groups to register
// (e.g., for testing).
type APIGroupFactoryRegistry map[string]*GroupMetaFactory
func (gar APIGroupFactoryRegistry) group(groupName string) *GroupMetaFactory {
gmf, ok := gar[groupName]
if !ok {
gmf = &GroupMetaFactory{VersionArgs: map[string]*GroupVersionFactoryArgs{}}
gar[groupName] = gmf
}
return gmf
}
// AnnounceGroupVersion adds the particular arguments for this group version to the group factory.
func (gar APIGroupFactoryRegistry) AnnounceGroupVersion(gvf *GroupVersionFactoryArgs) error {
gmf := gar.group(gvf.GroupName)
if _, ok := gmf.VersionArgs[gvf.VersionName]; ok {
return fmt.Errorf("version %q in group %q has already been announced", gvf.VersionName, gvf.GroupName)
}
gmf.VersionArgs[gvf.VersionName] = gvf
return nil
}
// AnnounceGroup adds the group-wide arguments to the group factory.
func (gar APIGroupFactoryRegistry) AnnounceGroup(args *GroupMetaFactoryArgs) error {
gmf := gar.group(args.GroupName)
if gmf.GroupArgs != nil {
return fmt.Errorf("group %q has already been announced", args.GroupName)
}
gmf.GroupArgs = args
return nil
}
// RegisterAndEnableAll throws every factory at the specified API registration
// manager, and lets it decide which to register. (If you want to do this a la
// cart, you may look through gar itself-- it's just a map.)
func (gar APIGroupFactoryRegistry) RegisterAndEnableAll(m *registered.APIRegistrationManager, scheme *runtime.Scheme) error {
for groupName, gmf := range gar {
if err := gmf.Register(m); err != nil {
return fmt.Errorf("error registering %v: %v", groupName, err)
}
if err := gmf.Enable(m, scheme); err != nil {
return fmt.Errorf("error enabling %v: %v", groupName, err)
}
}
return nil
}
// AnnouncePreconstructedFactory announces a factory which you've manually assembled.
// You may call this instead of calling AnnounceGroup and AnnounceGroupVersion.
func (gar APIGroupFactoryRegistry) AnnouncePreconstructedFactory(gmf *GroupMetaFactory) error {
name := gmf.GroupArgs.GroupName
if _, exists := gar[name]; exists {
return fmt.Errorf("the group %q has already been announced.", name)
}
gar[name] = gmf
return nil
}

View file

@ -0,0 +1,66 @@
/*
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 announced
import (
"reflect"
"testing"
"k8s.io/apimachinery/pkg/util/sets"
)
func TestFactoryRegistry(t *testing.T) {
regA := make(APIGroupFactoryRegistry)
regB := make(APIGroupFactoryRegistry)
if err := regA.AnnounceGroup(&GroupMetaFactoryArgs{
GroupName: "foo",
VersionPreferenceOrder: []string{"v2", "v1"},
ImportPrefix: "pkg/apis/foo",
RootScopedKinds: sets.NewString("namespaces"),
}); err != nil {
t.Fatalf("Unexpected error: %v", err)
}
if err := regA.AnnounceGroupVersion(&GroupVersionFactoryArgs{
GroupName: "foo",
VersionName: "v1",
}); err != nil {
t.Fatalf("Unexpected error: %v", err)
}
if err := regA.AnnounceGroupVersion(&GroupVersionFactoryArgs{
GroupName: "foo",
VersionName: "v2",
}); err != nil {
t.Fatalf("Unexpected error: %v", err)
}
if err := regB.AnnouncePreconstructedFactory(NewGroupMetaFactory(
&GroupMetaFactoryArgs{
GroupName: "foo",
VersionPreferenceOrder: []string{"v2", "v1"},
ImportPrefix: "pkg/apis/foo",
RootScopedKinds: sets.NewString("namespaces"),
},
VersionToSchemeFunc{"v1": nil, "v2": nil},
)); err != nil {
t.Fatalf("Unexpected error: %v", err)
}
if !reflect.DeepEqual(regA, regB) {
t.Errorf("Expected both ways of registering to be equivalent, but they were not.\n\n%#v\n\n%#v\n", regA, regB)
}
}

View file

@ -0,0 +1,246 @@
/*
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 announced
import (
"fmt"
"github.com/golang/glog"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/apimachinery"
"k8s.io/apimachinery/pkg/apimachinery/registered"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/sets"
)
type SchemeFunc func(*runtime.Scheme) error
type VersionToSchemeFunc map[string]SchemeFunc
// GroupVersionFactoryArgs contains all the per-version parts of a GroupMetaFactory.
type GroupVersionFactoryArgs struct {
GroupName string
VersionName string
AddToScheme SchemeFunc
}
// GroupMetaFactoryArgs contains the group-level args of a GroupMetaFactory.
type GroupMetaFactoryArgs struct {
GroupName string
VersionPreferenceOrder []string
ImportPrefix string
RootScopedKinds sets.String // nil is allowed
IgnoredKinds sets.String // nil is allowed
// May be nil if there are no internal objects.
AddInternalObjectsToScheme SchemeFunc
}
// NewGroupMetaFactory builds the args for you. This is for if you're
// constructing a factory all at once and not using the registry.
func NewGroupMetaFactory(groupArgs *GroupMetaFactoryArgs, versions VersionToSchemeFunc) *GroupMetaFactory {
gmf := &GroupMetaFactory{
GroupArgs: groupArgs,
VersionArgs: map[string]*GroupVersionFactoryArgs{},
}
for v, f := range versions {
gmf.VersionArgs[v] = &GroupVersionFactoryArgs{
GroupName: groupArgs.GroupName,
VersionName: v,
AddToScheme: f,
}
}
return gmf
}
// Announce adds this Group factory to the global factory registry. It should
// only be called if you constructed the GroupMetaFactory yourself via
// NewGroupMetadFactory.
// Note that this will panic on an error, since it's expected that you'll be
// calling this at initialization time and any error is a result of a
// programmer importing the wrong set of packages. If this assumption doesn't
// work for you, just call DefaultGroupFactoryRegistry.AnnouncePreconstructedFactory
// yourself.
func (gmf *GroupMetaFactory) Announce(groupFactoryRegistry APIGroupFactoryRegistry) *GroupMetaFactory {
if err := groupFactoryRegistry.AnnouncePreconstructedFactory(gmf); err != nil {
panic(err)
}
return gmf
}
// GroupMetaFactory has the logic for actually assembling and registering a group.
//
// There are two ways of obtaining one of these.
// 1. You can announce your group and versions separately, and then let the
// GroupFactoryRegistry assemble this object for you. (This allows group and
// versions to be imported separately, without referencing each other, to
// keep import trees small.)
// 2. You can call NewGroupMetaFactory(), which is mostly a drop-in replacement
// for the old, bad way of doing things. You can then call .Announce() to
// announce your constructed factory to any code that would like to do
// things the new, better way.
//
// Note that GroupMetaFactory actually does construct GroupMeta objects, but
// currently it does so in a way that's very entangled with an
// APIRegistrationManager. It's a TODO item to cleanly separate that interface.
type GroupMetaFactory struct {
GroupArgs *GroupMetaFactoryArgs
// map of version name to version factory
VersionArgs map[string]*GroupVersionFactoryArgs
// assembled by Register()
prioritizedVersionList []schema.GroupVersion
}
// Register constructs the finalized prioritized version list and sanity checks
// the announced group & versions. Then it calls register.
func (gmf *GroupMetaFactory) Register(m *registered.APIRegistrationManager) error {
if gmf.GroupArgs == nil {
return fmt.Errorf("partially announced groups are not allowed, only got versions: %#v", gmf.VersionArgs)
}
if len(gmf.VersionArgs) == 0 {
return fmt.Errorf("group %v announced but no versions announced", gmf.GroupArgs.GroupName)
}
pvSet := sets.NewString(gmf.GroupArgs.VersionPreferenceOrder...)
if pvSet.Len() != len(gmf.GroupArgs.VersionPreferenceOrder) {
return fmt.Errorf("preference order for group %v has duplicates: %v", gmf.GroupArgs.GroupName, gmf.GroupArgs.VersionPreferenceOrder)
}
prioritizedVersions := []schema.GroupVersion{}
for _, v := range gmf.GroupArgs.VersionPreferenceOrder {
prioritizedVersions = append(
prioritizedVersions,
schema.GroupVersion{
Group: gmf.GroupArgs.GroupName,
Version: v,
},
)
}
// Go through versions that weren't explicitly prioritized.
unprioritizedVersions := []schema.GroupVersion{}
for _, v := range gmf.VersionArgs {
if v.GroupName != gmf.GroupArgs.GroupName {
return fmt.Errorf("found %v/%v in group %v?", v.GroupName, v.VersionName, gmf.GroupArgs.GroupName)
}
if pvSet.Has(v.VersionName) {
pvSet.Delete(v.VersionName)
continue
}
unprioritizedVersions = append(unprioritizedVersions, schema.GroupVersion{Group: v.GroupName, Version: v.VersionName})
}
if len(unprioritizedVersions) > 1 {
glog.Warningf("group %v has multiple unprioritized versions: %#v. They will have an arbitrary preference order!", gmf.GroupArgs.GroupName, unprioritizedVersions)
}
if pvSet.Len() != 0 {
return fmt.Errorf("group %v has versions in the priority list that were never announced: %s", gmf.GroupArgs.GroupName, pvSet)
}
prioritizedVersions = append(prioritizedVersions, unprioritizedVersions...)
m.RegisterVersions(prioritizedVersions)
gmf.prioritizedVersionList = prioritizedVersions
return nil
}
func (gmf *GroupMetaFactory) newRESTMapper(scheme *runtime.Scheme, externalVersions []schema.GroupVersion, groupMeta *apimachinery.GroupMeta) meta.RESTMapper {
// the list of kinds that are scoped at the root of the api hierarchy
// if a kind is not enumerated here, it is assumed to have a namespace scope
rootScoped := sets.NewString()
if gmf.GroupArgs.RootScopedKinds != nil {
rootScoped = gmf.GroupArgs.RootScopedKinds
}
ignoredKinds := sets.NewString()
if gmf.GroupArgs.IgnoredKinds != nil {
ignoredKinds = gmf.GroupArgs.IgnoredKinds
}
return meta.NewDefaultRESTMapperFromScheme(
externalVersions,
groupMeta.InterfacesFor,
gmf.GroupArgs.ImportPrefix,
ignoredKinds,
rootScoped,
scheme,
)
}
// Enable enables group versions that are allowed, adds methods to the scheme, etc.
func (gmf *GroupMetaFactory) Enable(m *registered.APIRegistrationManager, scheme *runtime.Scheme) error {
externalVersions := []schema.GroupVersion{}
for _, v := range gmf.prioritizedVersionList {
if !m.IsAllowedVersion(v) {
continue
}
externalVersions = append(externalVersions, v)
if err := m.EnableVersions(v); err != nil {
return err
}
gmf.VersionArgs[v.Version].AddToScheme(scheme)
}
if len(externalVersions) == 0 {
glog.V(4).Infof("No version is registered for group %v", gmf.GroupArgs.GroupName)
return nil
}
if gmf.GroupArgs.AddInternalObjectsToScheme != nil {
gmf.GroupArgs.AddInternalObjectsToScheme(scheme)
}
preferredExternalVersion := externalVersions[0]
accessor := meta.NewAccessor()
groupMeta := &apimachinery.GroupMeta{
GroupVersion: preferredExternalVersion,
GroupVersions: externalVersions,
SelfLinker: runtime.SelfLinker(accessor),
}
for _, v := range externalVersions {
gvf := gmf.VersionArgs[v.Version]
if err := groupMeta.AddVersionInterfaces(
schema.GroupVersion{Group: gvf.GroupName, Version: gvf.VersionName},
&meta.VersionInterfaces{
ObjectConvertor: scheme,
MetadataAccessor: accessor,
},
); err != nil {
return err
}
}
groupMeta.InterfacesFor = groupMeta.DefaultInterfacesFor
groupMeta.RESTMapper = gmf.newRESTMapper(scheme, externalVersions, groupMeta)
if err := m.RegisterGroup(*groupMeta); err != nil {
return err
}
return nil
}
// RegisterAndEnable is provided only to allow this code to get added in multiple steps.
// It's really bad that this is called in init() methods, but supporting this
// temporarily lets us do the change incrementally.
func (gmf *GroupMetaFactory) RegisterAndEnable(registry *registered.APIRegistrationManager, scheme *runtime.Scheme) error {
if err := gmf.Register(registry); err != nil {
return err
}
if err := gmf.Enable(registry, scheme); err != nil {
return err
}
return nil
}

20
vendor/k8s.io/apimachinery/pkg/apimachinery/doc.go generated vendored Normal file
View file

@ -0,0 +1,20 @@
/*
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 apimachinery contains the generic API machinery code that
// is common to both server and clients.
// This package should never import specific API objects.
package apimachinery // import "k8s.io/apimachinery/pkg/apimachinery"

View file

@ -0,0 +1,376 @@
/*
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 to keep track of API Versions that can be registered and are enabled in api.Scheme.
package registered
import (
"fmt"
"sort"
"strings"
"github.com/golang/glog"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/apimachinery"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/sets"
)
// APIRegistrationManager provides the concept of what API groups are enabled.
//
// TODO: currently, it also provides a "registered" concept. But it's wrong to
// have both concepts in the same object. Therefore the "announced" package is
// going to take over the registered concept. After all the install packages
// are switched to using the announce package instead of this package, then we
// can combine the registered/enabled concepts in this object. Simplifying this
// isn't easy right now because there are so many callers of this package.
type APIRegistrationManager struct {
// registeredGroupVersions stores all API group versions for which RegisterGroup is called.
registeredVersions map[schema.GroupVersion]struct{}
// thirdPartyGroupVersions are API versions which are dynamically
// registered (and unregistered) via API calls to the apiserver
thirdPartyGroupVersions []schema.GroupVersion
// enabledVersions represents all enabled API versions. It should be a
// subset of registeredVersions. Please call EnableVersions() to add
// enabled versions.
enabledVersions map[schema.GroupVersion]struct{}
// map of group meta for all groups.
groupMetaMap map[string]*apimachinery.GroupMeta
// envRequestedVersions represents the versions requested via the
// KUBE_API_VERSIONS environment variable. The install package of each group
// checks this list before add their versions to the latest package and
// Scheme. This list is small and order matters, so represent as a slice
envRequestedVersions []schema.GroupVersion
}
// NewAPIRegistrationManager constructs a new manager. The argument ought to be
// the value of the KUBE_API_VERSIONS env var, or a value of this which you
// wish to test.
func NewAPIRegistrationManager(kubeAPIVersions string) (*APIRegistrationManager, error) {
m := &APIRegistrationManager{
registeredVersions: map[schema.GroupVersion]struct{}{},
thirdPartyGroupVersions: []schema.GroupVersion{},
enabledVersions: map[schema.GroupVersion]struct{}{},
groupMetaMap: map[string]*apimachinery.GroupMeta{},
envRequestedVersions: []schema.GroupVersion{},
}
if len(kubeAPIVersions) != 0 {
for _, version := range strings.Split(kubeAPIVersions, ",") {
gv, err := schema.ParseGroupVersion(version)
if err != nil {
return nil, fmt.Errorf("invalid api version: %s in KUBE_API_VERSIONS: %s.",
version, kubeAPIVersions)
}
m.envRequestedVersions = append(m.envRequestedVersions, gv)
}
}
return m, nil
}
func NewOrDie(kubeAPIVersions string) *APIRegistrationManager {
m, err := NewAPIRegistrationManager(kubeAPIVersions)
if err != nil {
glog.Fatalf("Could not construct version manager: %v (KUBE_API_VERSIONS=%q)", err, kubeAPIVersions)
}
return m
}
// RegisterVersions adds the given group versions to the list of registered group versions.
func (m *APIRegistrationManager) RegisterVersions(availableVersions []schema.GroupVersion) {
for _, v := range availableVersions {
m.registeredVersions[v] = struct{}{}
}
}
// RegisterGroup adds the given group to the list of registered groups.
func (m *APIRegistrationManager) RegisterGroup(groupMeta apimachinery.GroupMeta) error {
groupName := groupMeta.GroupVersion.Group
if _, found := m.groupMetaMap[groupName]; found {
return fmt.Errorf("group %q is already registered in groupsMap: %v", groupName, m.groupMetaMap)
}
m.groupMetaMap[groupName] = &groupMeta
return nil
}
// EnableVersions adds the versions for the given group to the list of enabled versions.
// Note that the caller should call RegisterGroup before calling this method.
// The caller of this function is responsible to add the versions to scheme and RESTMapper.
func (m *APIRegistrationManager) EnableVersions(versions ...schema.GroupVersion) error {
var unregisteredVersions []schema.GroupVersion
for _, v := range versions {
if _, found := m.registeredVersions[v]; !found {
unregisteredVersions = append(unregisteredVersions, v)
}
m.enabledVersions[v] = struct{}{}
}
if len(unregisteredVersions) != 0 {
return fmt.Errorf("Please register versions before enabling them: %v", unregisteredVersions)
}
return nil
}
// IsAllowedVersion returns if the version is allowed by the KUBE_API_VERSIONS
// environment variable. If the environment variable is empty, then it always
// returns true.
func (m *APIRegistrationManager) IsAllowedVersion(v schema.GroupVersion) bool {
if len(m.envRequestedVersions) == 0 {
return true
}
for _, envGV := range m.envRequestedVersions {
if v == envGV {
return true
}
}
return false
}
// IsEnabledVersion returns if a version is enabled.
func (m *APIRegistrationManager) IsEnabledVersion(v schema.GroupVersion) bool {
_, found := m.enabledVersions[v]
return found
}
// EnabledVersions returns all enabled versions. Groups are randomly ordered, but versions within groups
// are priority order from best to worst
func (m *APIRegistrationManager) EnabledVersions() []schema.GroupVersion {
ret := []schema.GroupVersion{}
for _, groupMeta := range m.groupMetaMap {
for _, version := range groupMeta.GroupVersions {
if m.IsEnabledVersion(version) {
ret = append(ret, version)
}
}
}
return ret
}
// EnabledVersionsForGroup returns all enabled versions for a group in order of best to worst
func (m *APIRegistrationManager) EnabledVersionsForGroup(group string) []schema.GroupVersion {
groupMeta, ok := m.groupMetaMap[group]
if !ok {
return []schema.GroupVersion{}
}
ret := []schema.GroupVersion{}
for _, version := range groupMeta.GroupVersions {
if m.IsEnabledVersion(version) {
ret = append(ret, version)
}
}
return ret
}
// Group returns the metadata of a group if the group is registered, otherwise
// an error is returned.
func (m *APIRegistrationManager) Group(group string) (*apimachinery.GroupMeta, error) {
groupMeta, found := m.groupMetaMap[group]
if !found {
return nil, fmt.Errorf("group %v has not been registered", group)
}
groupMetaCopy := *groupMeta
return &groupMetaCopy, nil
}
// IsRegistered takes a string and determines if it's one of the registered groups
func (m *APIRegistrationManager) IsRegistered(group string) bool {
_, found := m.groupMetaMap[group]
return found
}
// IsRegisteredVersion returns if a version is registered.
func (m *APIRegistrationManager) IsRegisteredVersion(v schema.GroupVersion) bool {
_, found := m.registeredVersions[v]
return found
}
// RegisteredGroupVersions returns all registered group versions.
func (m *APIRegistrationManager) RegisteredGroupVersions() []schema.GroupVersion {
ret := []schema.GroupVersion{}
for groupVersion := range m.registeredVersions {
ret = append(ret, groupVersion)
}
return ret
}
// IsThirdPartyAPIGroupVersion returns true if the api version is a user-registered group/version.
func (m *APIRegistrationManager) IsThirdPartyAPIGroupVersion(gv schema.GroupVersion) bool {
for ix := range m.thirdPartyGroupVersions {
if m.thirdPartyGroupVersions[ix] == gv {
return true
}
}
return false
}
// AddThirdPartyAPIGroupVersions sets the list of third party versions,
// registers them in the API machinery and enables them.
// Skips GroupVersions that are already registered.
// Returns the list of GroupVersions that were skipped.
func (m *APIRegistrationManager) AddThirdPartyAPIGroupVersions(gvs ...schema.GroupVersion) []schema.GroupVersion {
filteredGVs := []schema.GroupVersion{}
skippedGVs := []schema.GroupVersion{}
for ix := range gvs {
if !m.IsRegisteredVersion(gvs[ix]) {
filteredGVs = append(filteredGVs, gvs[ix])
} else {
glog.V(3).Infof("Skipping %s, because its already registered", gvs[ix].String())
skippedGVs = append(skippedGVs, gvs[ix])
}
}
if len(filteredGVs) == 0 {
return skippedGVs
}
m.RegisterVersions(filteredGVs)
m.EnableVersions(filteredGVs...)
m.thirdPartyGroupVersions = append(m.thirdPartyGroupVersions, filteredGVs...)
return skippedGVs
}
// InterfacesFor is a union meta.VersionInterfacesFunc func for all registered types
func (m *APIRegistrationManager) InterfacesFor(version schema.GroupVersion) (*meta.VersionInterfaces, error) {
groupMeta, err := m.Group(version.Group)
if err != nil {
return nil, err
}
return groupMeta.InterfacesFor(version)
}
// TODO: This is an expedient function, because we don't check if a Group is
// supported throughout the code base. We will abandon this function and
// checking the error returned by the Group() function.
func (m *APIRegistrationManager) GroupOrDie(group string) *apimachinery.GroupMeta {
groupMeta, found := m.groupMetaMap[group]
if !found {
if group == "" {
panic("The legacy v1 API is not registered.")
} else {
panic(fmt.Sprintf("Group %s is not registered.", group))
}
}
groupMetaCopy := *groupMeta
return &groupMetaCopy
}
// RESTMapper returns a union RESTMapper of all known types with priorities chosen in the following order:
// 1. if KUBE_API_VERSIONS is specified, then KUBE_API_VERSIONS in order, OR
// 1. legacy kube group preferred version, extensions preferred version, metrics perferred version, legacy
// kube any version, extensions any version, metrics any version, all other groups alphabetical preferred version,
// all other groups alphabetical.
func (m *APIRegistrationManager) RESTMapper(versionPatterns ...schema.GroupVersion) meta.RESTMapper {
unionMapper := meta.MultiRESTMapper{}
unionedGroups := sets.NewString()
for enabledVersion := range m.enabledVersions {
if !unionedGroups.Has(enabledVersion.Group) {
unionedGroups.Insert(enabledVersion.Group)
groupMeta := m.groupMetaMap[enabledVersion.Group]
unionMapper = append(unionMapper, groupMeta.RESTMapper)
}
}
if len(versionPatterns) != 0 {
resourcePriority := []schema.GroupVersionResource{}
kindPriority := []schema.GroupVersionKind{}
for _, versionPriority := range versionPatterns {
resourcePriority = append(resourcePriority, versionPriority.WithResource(meta.AnyResource))
kindPriority = append(kindPriority, versionPriority.WithKind(meta.AnyKind))
}
return meta.PriorityRESTMapper{Delegate: unionMapper, ResourcePriority: resourcePriority, KindPriority: kindPriority}
}
if len(m.envRequestedVersions) != 0 {
resourcePriority := []schema.GroupVersionResource{}
kindPriority := []schema.GroupVersionKind{}
for _, versionPriority := range m.envRequestedVersions {
resourcePriority = append(resourcePriority, versionPriority.WithResource(meta.AnyResource))
kindPriority = append(kindPriority, versionPriority.WithKind(meta.AnyKind))
}
return meta.PriorityRESTMapper{Delegate: unionMapper, ResourcePriority: resourcePriority, KindPriority: kindPriority}
}
prioritizedGroups := []string{"", "extensions", "metrics"}
resourcePriority, kindPriority := m.prioritiesForGroups(prioritizedGroups...)
prioritizedGroupsSet := sets.NewString(prioritizedGroups...)
remainingGroups := sets.String{}
for enabledVersion := range m.enabledVersions {
if !prioritizedGroupsSet.Has(enabledVersion.Group) {
remainingGroups.Insert(enabledVersion.Group)
}
}
remainingResourcePriority, remainingKindPriority := m.prioritiesForGroups(remainingGroups.List()...)
resourcePriority = append(resourcePriority, remainingResourcePriority...)
kindPriority = append(kindPriority, remainingKindPriority...)
return meta.PriorityRESTMapper{Delegate: unionMapper, ResourcePriority: resourcePriority, KindPriority: kindPriority}
}
// prioritiesForGroups returns the resource and kind priorities for a PriorityRESTMapper, preferring the preferred version of each group first,
// then any non-preferred version of the group second.
func (m *APIRegistrationManager) prioritiesForGroups(groups ...string) ([]schema.GroupVersionResource, []schema.GroupVersionKind) {
resourcePriority := []schema.GroupVersionResource{}
kindPriority := []schema.GroupVersionKind{}
for _, group := range groups {
availableVersions := m.EnabledVersionsForGroup(group)
if len(availableVersions) > 0 {
resourcePriority = append(resourcePriority, availableVersions[0].WithResource(meta.AnyResource))
kindPriority = append(kindPriority, availableVersions[0].WithKind(meta.AnyKind))
}
}
for _, group := range groups {
resourcePriority = append(resourcePriority, schema.GroupVersionResource{Group: group, Version: meta.AnyVersion, Resource: meta.AnyResource})
kindPriority = append(kindPriority, schema.GroupVersionKind{Group: group, Version: meta.AnyVersion, Kind: meta.AnyKind})
}
return resourcePriority, kindPriority
}
// AllPreferredGroupVersions returns the preferred versions of all registered
// groups in the form of "group1/version1,group2/version2,..."
func (m *APIRegistrationManager) AllPreferredGroupVersions() string {
if len(m.groupMetaMap) == 0 {
return ""
}
var defaults []string
for _, groupMeta := range m.groupMetaMap {
defaults = append(defaults, groupMeta.GroupVersion.String())
}
sort.Strings(defaults)
return strings.Join(defaults, ",")
}
// ValidateEnvRequestedVersions returns a list of versions that are requested in
// the KUBE_API_VERSIONS environment variable, but not enabled.
func (m *APIRegistrationManager) ValidateEnvRequestedVersions() []schema.GroupVersion {
var missingVersions []schema.GroupVersion
for _, v := range m.envRequestedVersions {
if _, found := m.enabledVersions[v]; !found {
missingVersions = append(missingVersions, v)
}
}
return missingVersions
}

View file

@ -0,0 +1,148 @@
/*
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 registered
import (
"testing"
"k8s.io/apimachinery/pkg/apimachinery"
"k8s.io/apimachinery/pkg/runtime/schema"
)
func TestAddThirdPartyVersionsBasic(t *testing.T) {
m, err := NewAPIRegistrationManager("")
if err != nil {
t.Fatalf("Unexpected failure to make a manager: %v", err)
}
registered := []schema.GroupVersion{
{
Group: "",
Version: "v1",
},
}
skipped := registered
thirdParty := []schema.GroupVersion{
{
Group: "company.com",
Version: "v1",
},
{
Group: "company.com",
Version: "v2",
},
}
gvs := append(registered, thirdParty...)
m.RegisterVersions(registered)
wasSkipped := m.AddThirdPartyAPIGroupVersions(gvs...)
if len(wasSkipped) != len(skipped) {
t.Errorf("Expected %v, found %v", skipped, wasSkipped)
}
for ix := range wasSkipped {
found := false
for _, gv := range skipped {
if gv.String() == wasSkipped[ix].String() {
found = true
break
}
}
if !found {
t.Errorf("Couldn't find %v in %v", wasSkipped[ix], skipped)
}
}
for _, gv := range thirdParty {
if !m.IsThirdPartyAPIGroupVersion(gv) {
t.Errorf("Expected %v to be third party.", gv)
}
}
}
func TestAddThirdPartyVersionsMultiple(t *testing.T) {
thirdParty := []schema.GroupVersion{
{
Group: "company.com",
Version: "v1",
},
{
Group: "company.com",
Version: "v2",
},
}
m, err := NewAPIRegistrationManager("")
if err != nil {
t.Fatalf("Unexpected failure to make a manager: %v", err)
}
for _, gv := range thirdParty {
wasSkipped := m.AddThirdPartyAPIGroupVersions(gv)
if len(wasSkipped) != 0 {
t.Errorf("Expected length 0, found %v", wasSkipped)
}
}
for _, gv := range thirdParty {
if !m.IsThirdPartyAPIGroupVersion(gv) {
t.Errorf("Expected %v to be third party.", gv)
}
}
}
func TestAllPreferredGroupVersions(t *testing.T) {
testCases := []struct {
groupMetas []apimachinery.GroupMeta
expect string
}{
{
groupMetas: []apimachinery.GroupMeta{
{
GroupVersion: schema.GroupVersion{Group: "group1", Version: "v1"},
},
{
GroupVersion: schema.GroupVersion{Group: "group2", Version: "v2"},
},
{
GroupVersion: schema.GroupVersion{Group: "", Version: "v1"},
},
},
expect: "group1/v1,group2/v2,v1",
},
{
groupMetas: []apimachinery.GroupMeta{
{
GroupVersion: schema.GroupVersion{Group: "", Version: "v1"},
},
},
expect: "v1",
},
{
groupMetas: []apimachinery.GroupMeta{},
expect: "",
},
}
for _, testCase := range testCases {
m, err := NewAPIRegistrationManager("")
if err != nil {
t.Fatalf("Unexpected failure to make a manager: %v", err)
}
for _, groupMeta := range testCase.groupMetas {
m.RegisterGroup(groupMeta)
}
output := m.AllPreferredGroupVersions()
if testCase.expect != output {
t.Errorf("Error. expect: %s, got: %s", testCase.expect, output)
}
}
}

87
vendor/k8s.io/apimachinery/pkg/apimachinery/types.go generated vendored Normal file
View file

@ -0,0 +1,87 @@
/*
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 apimachinery
import (
"fmt"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
// GroupMeta stores the metadata of a group.
type GroupMeta struct {
// GroupVersion represents the preferred version of the group.
GroupVersion schema.GroupVersion
// GroupVersions is Group + all versions in that group.
GroupVersions []schema.GroupVersion
// SelfLinker can set or get the SelfLink field of all API types.
// TODO: when versioning changes, make this part of each API definition.
// TODO(lavalamp): Combine SelfLinker & ResourceVersioner interfaces, force all uses
// to go through the InterfacesFor method below.
SelfLinker runtime.SelfLinker
// RESTMapper provides the default mapping between REST paths and the objects declared in api.Scheme and all known
// versions.
RESTMapper meta.RESTMapper
// InterfacesFor returns the default Codec and ResourceVersioner for a given version
// string, or an error if the version is not known.
// TODO: make this stop being a func pointer and always use the default
// function provided below once every place that populates this field has been changed.
InterfacesFor func(version schema.GroupVersion) (*meta.VersionInterfaces, error)
// InterfacesByVersion stores the per-version interfaces.
InterfacesByVersion map[schema.GroupVersion]*meta.VersionInterfaces
}
// DefaultInterfacesFor returns the default Codec and ResourceVersioner for a given version
// string, or an error if the version is not known.
// TODO: Remove the "Default" prefix.
func (gm *GroupMeta) DefaultInterfacesFor(version schema.GroupVersion) (*meta.VersionInterfaces, error) {
if v, ok := gm.InterfacesByVersion[version]; ok {
return v, nil
}
return nil, fmt.Errorf("unsupported storage version: %s (valid: %v)", version, gm.GroupVersions)
}
// AddVersionInterfaces adds the given version to the group. Only call during
// init, after that GroupMeta objects should be immutable. Not thread safe.
// (If you use this, be sure to set .InterfacesFor = .DefaultInterfacesFor)
// TODO: remove the "Interfaces" suffix and make this also maintain the
// .GroupVersions member.
func (gm *GroupMeta) AddVersionInterfaces(version schema.GroupVersion, interfaces *meta.VersionInterfaces) error {
if e, a := gm.GroupVersion.Group, version.Group; a != e {
return fmt.Errorf("got a version in group %v, but am in group %v", a, e)
}
if gm.InterfacesByVersion == nil {
gm.InterfacesByVersion = make(map[schema.GroupVersion]*meta.VersionInterfaces)
}
gm.InterfacesByVersion[version] = interfaces
// TODO: refactor to make the below error not possible, this function
// should *set* GroupVersions rather than depend on it.
for _, v := range gm.GroupVersions {
if v == version {
return nil
}
}
return fmt.Errorf("added a version interface without the corresponding version %v being in the list %#v", version, gm.GroupVersions)
}

View file

@ -0,0 +1,43 @@
/*
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 apimachinery
import (
"testing"
"k8s.io/apimachinery/pkg/runtime/schema"
)
func TestAdd(t *testing.T) {
gm := GroupMeta{
GroupVersion: schema.GroupVersion{
Group: "test",
Version: "v1",
},
GroupVersions: []schema.GroupVersion{{Group: "test", Version: "v1"}},
}
gm.AddVersionInterfaces(schema.GroupVersion{Group: "test", Version: "v1"}, nil)
if e, a := 1, len(gm.InterfacesByVersion); e != a {
t.Errorf("expected %v, got %v", e, a)
}
// GroupVersions is unchanged
if e, a := 1, len(gm.GroupVersions); e != a {
t.Errorf("expected %v, got %v", e, a)
}
}

View file

@ -0,0 +1,106 @@
/*
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 internalversion
import (
"fmt"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/conversion"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/util/validation/field"
)
func Convert_internalversion_ListOptions_To_v1_ListOptions(in *ListOptions, out *metav1.ListOptions, s conversion.Scope) error {
if err := Convert_fields_Selector_To_string(&in.FieldSelector, &out.FieldSelector, s); err != nil {
return err
}
if err := Convert_labels_Selector_To_string(&in.LabelSelector, &out.LabelSelector, s); err != nil {
return err
}
out.ResourceVersion = in.ResourceVersion
out.TimeoutSeconds = in.TimeoutSeconds
out.Watch = in.Watch
return nil
}
func Convert_v1_ListOptions_To_internalversion_ListOptions(in *metav1.ListOptions, out *ListOptions, s conversion.Scope) error {
if err := Convert_string_To_fields_Selector(&in.FieldSelector, &out.FieldSelector, s); err != nil {
return err
}
if err := Convert_string_To_labels_Selector(&in.LabelSelector, &out.LabelSelector, s); err != nil {
return err
}
out.ResourceVersion = in.ResourceVersion
out.TimeoutSeconds = in.TimeoutSeconds
out.Watch = in.Watch
return nil
}
func Convert_string_To_labels_Selector(in *string, out *labels.Selector, s conversion.Scope) error {
selector, err := labels.Parse(*in)
if err != nil {
return err
}
*out = selector
return nil
}
func Convert_string_To_fields_Selector(in *string, out *fields.Selector, s conversion.Scope) error {
selector, err := fields.ParseSelector(*in)
if err != nil {
return err
}
*out = selector
return nil
}
func Convert_labels_Selector_To_string(in *labels.Selector, out *string, s conversion.Scope) error {
if *in == nil {
return nil
}
*out = (*in).String()
return nil
}
func Convert_fields_Selector_To_string(in *fields.Selector, out *string, s conversion.Scope) error {
if *in == nil {
return nil
}
*out = (*in).String()
return nil
}
func Convert_map_to_v1_LabelSelector(in *map[string]string, out *metav1.LabelSelector, s conversion.Scope) error {
if in == nil {
return nil
}
out = new(metav1.LabelSelector)
for labelKey, labelValue := range *in {
metav1.AddLabelToSelector(out, labelKey, labelValue)
}
return nil
}
func Convert_v1_LabelSelector_to_map(in *metav1.LabelSelector, out *map[string]string, s conversion.Scope) error {
var err error
*out, err = metav1.LabelSelectorAsMap(in)
if err != nil {
err = field.Invalid(field.NewPath("labelSelector"), *in, fmt.Sprintf("cannot convert to old selector: %v", err))
}
return err
}

View file

@ -0,0 +1,84 @@
/*
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 internalversion
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer"
)
// GroupName is the group name for this API.
const GroupName = "meta.k8s.io"
// Scheme is the registry for any type that adheres to the meta API spec.
var scheme = runtime.NewScheme()
// Codecs provides access to encoding and decoding for the scheme.
var Codecs = serializer.NewCodecFactory(scheme)
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal}
// ParameterCodec handles versioning of objects that are converted to query parameters.
var ParameterCodec = runtime.NewParameterCodec(scheme)
// Kind takes an unqualified kind and returns a Group qualified GroupKind
func Kind(kind string) schema.GroupKind {
return SchemeGroupVersion.WithKind(kind).GroupKind()
}
// addToGroupVersion registers common meta types into schemas.
func addToGroupVersion(scheme *runtime.Scheme, groupVersion schema.GroupVersion) error {
if err := scheme.AddIgnoredConversionType(&metav1.TypeMeta{}, &metav1.TypeMeta{}); err != nil {
return err
}
scheme.AddConversionFuncs(
Convert_string_To_labels_Selector,
Convert_labels_Selector_To_string,
Convert_string_To_fields_Selector,
Convert_fields_Selector_To_string,
Convert_map_to_v1_LabelSelector,
Convert_v1_LabelSelector_to_map,
Convert_internalversion_ListOptions_To_v1_ListOptions,
Convert_v1_ListOptions_To_internalversion_ListOptions,
)
// ListOptions is the only options struct which needs conversion (it exposes labels and fields
// as selectors for convenience). The other types have only a single representation today.
scheme.AddKnownTypes(SchemeGroupVersion,
&ListOptions{},
&metav1.GetOptions{},
&metav1.ExportOptions{},
&metav1.DeleteOptions{},
)
// Allow delete options to be decoded across all version in this scheme (we may want to be more clever than this)
scheme.AddUnversionedTypes(SchemeGroupVersion, &metav1.DeleteOptions{})
metav1.AddToGroupVersion(scheme, metav1.SchemeGroupVersion)
return nil
}
// Unlike other API groups, meta internal knows about all meta external versions, but keeps
// the logic for conversion private.
func init() {
if err := addToGroupVersion(scheme, SchemeGroupVersion); err != nil {
panic(err)
}
}

View file

@ -0,0 +1,89 @@
/*
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 internalversion
import (
"net/url"
"reflect"
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/diff"
)
func TestListOptions(t *testing.T) {
// verify round trip conversion
ten := int64(10)
in := &metav1.ListOptions{
LabelSelector: "a=1",
FieldSelector: "b=1",
ResourceVersion: "10",
TimeoutSeconds: &ten,
Watch: true,
}
out := &ListOptions{}
if err := scheme.Convert(in, out, nil); err != nil {
t.Fatal(err)
}
actual := &metav1.ListOptions{}
if err := scheme.Convert(out, actual, nil); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(in, actual) {
t.Errorf("unexpected: %s", diff.ObjectReflectDiff(in, actual))
}
// verify failing conversion
for i, failingObject := range []*metav1.ListOptions{
&metav1.ListOptions{LabelSelector: "a!!!"},
&metav1.ListOptions{FieldSelector: "a!!!"},
} {
out = &ListOptions{}
if err := scheme.Convert(failingObject, out, nil); err == nil {
t.Errorf("%d: unexpected conversion: %#v", i, out)
}
}
// verify kind registration
if gvk, unversioned, err := scheme.ObjectKind(in); err != nil || unversioned || gvk != metav1.SchemeGroupVersion.WithKind("ListOptions") {
t.Errorf("unexpected: %v %v %v", gvk, unversioned, err)
}
if gvk, unversioned, err := scheme.ObjectKind(out); err != nil || unversioned || gvk != SchemeGroupVersion.WithKind("ListOptions") {
t.Errorf("unexpected: %v %v %v", gvk, unversioned, err)
}
actual = &metav1.ListOptions{}
if err := ParameterCodec.DecodeParameters(url.Values{"watch": []string{"1"}}, metav1.SchemeGroupVersion, actual); err != nil {
t.Fatal(err)
}
if !actual.Watch {
t.Errorf("unexpected watch decode: %#v", actual)
}
// check ParameterCodec
query, err := ParameterCodec.EncodeParameters(in, metav1.SchemeGroupVersion)
if err != nil {
t.Fatal(err)
}
actual = &metav1.ListOptions{}
if err := ParameterCodec.DecodeParameters(query, metav1.SchemeGroupVersion, actual); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(in, actual) {
t.Errorf("unexpected: %s", diff.ObjectReflectDiff(in, actual))
}
}

View file

@ -0,0 +1,45 @@
/*
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 internalversion
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
)
// ListOptions is the query options to a standard REST list call, and has future support for
// watch calls.
type ListOptions struct {
metav1.TypeMeta
// A selector based on labels
LabelSelector labels.Selector
// A selector based on fields
FieldSelector fields.Selector
// If true, watch for changes to this list
Watch bool
// When specified with a watch call, shows changes that occur after that particular version of a resource.
// Defaults to changes from the beginning of history.
// When specified for list:
// - if unset, then the result is returned from remote storage based on quorum-read flag;
// - if it's 0, then we simply return what we currently have in cache, no guarantee;
// - if set to non zero, then the result is at least as fresh as given rv.
ResourceVersion string
// Timeout for the list/watch call.
TimeoutSeconds *int64
}

33
vendor/k8s.io/apimachinery/pkg/apis/meta/v1/OWNERS generated vendored Executable file
View file

@ -0,0 +1,33 @@
reviewers:
- thockin
- smarterclayton
- wojtek-t
- deads2k
- brendandburns
- caesarxuchao
- liggitt
- nikhiljindal
- gmarek
- erictune
- davidopp
- sttts
- quinton-hoole
- kargakis
- luxas
- janetkuo
- justinsb
- ncdc
- timothysc
- soltysh
- dims
- madhusudancs
- hongchaodeng
- krousey
- mml
- mbohlool
- david-mcmahon
- therc
- mqliang
- kevin-wangzefeng
- jianhuiz
- feihujiang

22
vendor/k8s.io/apimachinery/pkg/apis/meta/v1/doc.go generated vendored Normal file
View file

@ -0,0 +1,22 @@
/*
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.
*/
// +k8s:deepcopy-gen=package
// +k8s:openapi-gen=true
// +k8s:defaulter-gen=TypeMeta
// +groupName=meta.k8s.io
package v1

View file

@ -0,0 +1,47 @@
/*
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 v1
import (
"encoding/json"
"time"
)
// Duration is a wrapper around time.Duration which supports correct
// marshaling to YAML and JSON. In particular, it marshals into strings, which
// can be used as map keys in json.
type Duration struct {
time.Duration `protobuf:"varint,1,opt,name=duration,casttype=time.Duration"`
}
// UnmarshalJSON implements the json.Unmarshaller interface.
func (d *Duration) UnmarshalJSON(b []byte) error {
var str string
json.Unmarshal(b, &str)
pd, err := time.ParseDuration(str)
if err != nil {
return err
}
d.Duration = pd
return nil
}
// MarshalJSON implements the json.Marshaler interface.
func (d Duration) MarshalJSON() ([]byte, error) {
return json.Marshal(d.Duration.String())
}

View file

@ -0,0 +1,153 @@
/*
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 v1
import (
"encoding/json"
"testing"
"time"
"github.com/ghodss/yaml"
)
type DurationHolder struct {
D Duration `json:"d"`
}
func TestDurationMarshalYAML(t *testing.T) {
cases := []struct {
input Duration
result string
}{
{Duration{5 * time.Second}, "d: 5s\n"},
{Duration{2 * time.Minute}, "d: 2m0s\n"},
{Duration{time.Hour + 3*time.Millisecond}, "d: 1h0m0.003s\n"},
}
for _, c := range cases {
input := DurationHolder{c.input}
result, err := yaml.Marshal(&input)
if err != nil {
t.Errorf("Failed to marshal input: %q: %v", input, err)
}
if string(result) != c.result {
t.Errorf("Failed to marshal input: %q: expected %q, got %q", input, c.result, string(result))
}
}
}
func TestDurationUnmarshalYAML(t *testing.T) {
cases := []struct {
input string
result Duration
}{
{"d: 0s\n", Duration{}},
{"d: 5s\n", Duration{5 * time.Second}},
{"d: 2m0s\n", Duration{2 * time.Minute}},
{"d: 1h0m0.003s\n", Duration{time.Hour + 3*time.Millisecond}},
// Units with zero values can optionally be dropped
{"d: 2m\n", Duration{2 * time.Minute}},
{"d: 1h0.003s\n", Duration{time.Hour + 3*time.Millisecond}},
}
for _, c := range cases {
var result DurationHolder
if err := yaml.Unmarshal([]byte(c.input), &result); err != nil {
t.Errorf("Failed to unmarshal input %q: %v", c.input, err)
}
if result.D != c.result {
t.Errorf("Failed to unmarshal input %q: expected %q, got %q", c.input, c.result, result)
}
}
}
func TestDurationMarshalJSON(t *testing.T) {
cases := []struct {
input Duration
result string
}{
{Duration{5 * time.Second}, `{"d":"5s"}`},
{Duration{2 * time.Minute}, `{"d":"2m0s"}`},
{Duration{time.Hour + 3*time.Millisecond}, `{"d":"1h0m0.003s"}`},
}
for _, c := range cases {
input := DurationHolder{c.input}
result, err := json.Marshal(&input)
if err != nil {
t.Errorf("Failed to marshal input: %q: %v", input, err)
}
if string(result) != c.result {
t.Errorf("Failed to marshal input: %q: expected %q, got %q", input, c.result, string(result))
}
}
}
func TestDurationUnmarshalJSON(t *testing.T) {
cases := []struct {
input string
result Duration
}{
{`{"d":"0s"}`, Duration{}},
{`{"d":"5s"}`, Duration{5 * time.Second}},
{`{"d":"2m0s"}`, Duration{2 * time.Minute}},
{`{"d":"1h0m0.003s"}`, Duration{time.Hour + 3*time.Millisecond}},
// Units with zero values can optionally be dropped
{`{"d":"2m"}`, Duration{2 * time.Minute}},
{`{"d":"1h0.003s"}`, Duration{time.Hour + 3*time.Millisecond}},
}
for _, c := range cases {
var result DurationHolder
if err := json.Unmarshal([]byte(c.input), &result); err != nil {
t.Errorf("Failed to unmarshal input %q: %v", c.input, err)
}
if result.D != c.result {
t.Errorf("Failed to unmarshal input %q: expected %q, got %q", c.input, c.result, result)
}
}
}
func TestDurationMarshalJSONUnmarshalYAML(t *testing.T) {
cases := []struct {
input Duration
}{
{Duration{}},
{Duration{5 * time.Second}},
{Duration{2 * time.Minute}},
{Duration{time.Hour + 3*time.Millisecond}},
}
for i, c := range cases {
input := DurationHolder{c.input}
jsonMarshalled, err := json.Marshal(&input)
if err != nil {
t.Errorf("%d-1: Failed to marshal input: '%v': %v", i, input, err)
}
var result DurationHolder
if err := yaml.Unmarshal(jsonMarshalled, &result); err != nil {
t.Errorf("%d-2: Failed to unmarshal '%+v': %v", i, string(jsonMarshalled), err)
}
if input.D != result.D {
t.Errorf("%d-4: Failed to marshal input '%#v': got %#v", i, input, result)
}
}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,671 @@
/*
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.apis.meta.v1;
import "k8s.io/apimachinery/pkg/runtime/generated.proto";
import "k8s.io/apimachinery/pkg/runtime/schema/generated.proto";
import "k8s.io/apimachinery/pkg/util/intstr/generated.proto";
// Package-wide variables from generator "generated".
option go_package = "v1";
// APIGroup contains the name, the supported versions, and the preferred version
// of a group.
message APIGroup {
// name is the name of the group.
optional string name = 1;
// versions are the versions supported in this group.
repeated GroupVersionForDiscovery versions = 2;
// preferredVersion is the version preferred by the API server, which
// probably is the storage version.
// +optional
optional GroupVersionForDiscovery preferredVersion = 3;
// a map of client CIDR to server address that is serving this group.
// This is to help clients reach servers in the most network-efficient way possible.
// Clients can use the appropriate server address as per the CIDR that they match.
// In case of multiple matches, clients should use the longest matching CIDR.
// The server returns only those CIDRs that it thinks that the client can match.
// For example: the master will return an internal IP CIDR only, if the client reaches the server using an internal IP.
// Server looks at X-Forwarded-For header or X-Real-Ip header or request.RemoteAddr (in that order) to get the client IP.
repeated ServerAddressByClientCIDR serverAddressByClientCIDRs = 4;
}
// APIGroupList is a list of APIGroup, to allow clients to discover the API at
// /apis.
message APIGroupList {
// groups is a list of APIGroup.
repeated APIGroup groups = 1;
}
// APIResource specifies the name of a resource and whether it is namespaced.
message APIResource {
// name is the name of the resource.
optional string name = 1;
// namespaced indicates if a resource is namespaced or not.
optional bool namespaced = 2;
// kind is the kind for the resource (e.g. 'Foo' is the kind for a resource 'foo')
optional string kind = 3;
// verbs is a list of supported kube verbs (this includes get, list, watch, create,
// update, patch, delete, deletecollection, and proxy)
optional Verbs verbs = 4;
// shortNames is a list of suggested short names of the resource.
repeated string shortNames = 5;
}
// APIResourceList is a list of APIResource, it is used to expose the name of the
// resources supported in a specific group and version, and if the resource
// is namespaced.
message APIResourceList {
// groupVersion is the group and version this APIResourceList is for.
optional string groupVersion = 1;
// resources contains the name of the resources and if they are namespaced.
repeated APIResource resources = 2;
}
// APIVersions lists the versions that are available, to allow clients to
// discover the API at /api, which is the root path of the legacy v1 API.
//
// +protobuf.options.(gogoproto.goproto_stringer)=false
message APIVersions {
// versions are the api versions that are available.
repeated string versions = 1;
// a map of client CIDR to server address that is serving this group.
// This is to help clients reach servers in the most network-efficient way possible.
// Clients can use the appropriate server address as per the CIDR that they match.
// In case of multiple matches, clients should use the longest matching CIDR.
// The server returns only those CIDRs that it thinks that the client can match.
// For example: the master will return an internal IP CIDR only, if the client reaches the server using an internal IP.
// Server looks at X-Forwarded-For header or X-Real-Ip header or request.RemoteAddr (in that order) to get the client IP.
repeated ServerAddressByClientCIDR serverAddressByClientCIDRs = 2;
}
// DeleteOptions may be provided when deleting an API object.
message DeleteOptions {
// The duration in seconds before the object should be deleted. Value must be non-negative integer.
// The value zero indicates delete immediately. If this value is nil, the default grace period for the
// specified type will be used.
// Defaults to a per object value if not specified. zero means delete immediately.
// +optional
optional int64 gracePeriodSeconds = 1;
// Must be fulfilled before a deletion is carried out. If not possible, a 409 Conflict status will be
// returned.
// +optional
optional Preconditions preconditions = 2;
// Should the dependent objects be orphaned. If true/false, the "orphan"
// finalizer will be added to/removed from the object's finalizers list.
// +optional
optional bool orphanDependents = 3;
}
// Duration is a wrapper around time.Duration which supports correct
// marshaling to YAML and JSON. In particular, it marshals into strings, which
// can be used as map keys in json.
message Duration {
optional int64 duration = 1;
}
// ExportOptions is the query options to the standard REST get call.
message ExportOptions {
// Should this value be exported. Export strips fields that a user can not specify.
optional bool export = 1;
// Should the export be exact. Exact export maintains cluster-specific fields like 'Namespace'.
optional bool exact = 2;
}
// GetOptions is the standard query options to the standard REST get call.
message GetOptions {
// When specified:
// - if unset, then the result is returned from remote storage based on quorum-read flag;
// - if it's 0, then we simply return what we currently have in cache, no guarantee;
// - if set to non zero, then the result is at least as fresh as given rv.
optional string resourceVersion = 1;
}
// GroupKind specifies a Group and a Kind, but does not force a version. This is useful for identifying
// concepts during lookup stages without having partially valid types
//
// +protobuf.options.(gogoproto.goproto_stringer)=false
message GroupKind {
optional string group = 1;
optional string kind = 2;
}
// GroupResource specifies a Group and a Resource, but does not force a version. This is useful for identifying
// concepts during lookup stages without having partially valid types
//
// +protobuf.options.(gogoproto.goproto_stringer)=false
message GroupResource {
optional string group = 1;
optional string resource = 2;
}
// GroupVersion contains the "group" and the "version", which uniquely identifies the API.
//
// +protobuf.options.(gogoproto.goproto_stringer)=false
message GroupVersion {
optional string group = 1;
optional string version = 2;
}
// GroupVersion contains the "group/version" and "version" string of a version.
// It is made a struct to keep extensibility.
message GroupVersionForDiscovery {
// groupVersion specifies the API group and version in the form "group/version"
optional string groupVersion = 1;
// version specifies the version in the form of "version". This is to save
// the clients the trouble of splitting the GroupVersion.
optional string version = 2;
}
// GroupVersionKind unambiguously identifies a kind. It doesn't anonymously include GroupVersion
// to avoid automatic coersion. It doesn't use a GroupVersion to avoid custom marshalling
//
// +protobuf.options.(gogoproto.goproto_stringer)=false
message GroupVersionKind {
optional string group = 1;
optional string version = 2;
optional string kind = 3;
}
// GroupVersionResource unambiguously identifies a resource. It doesn't anonymously include GroupVersion
// to avoid automatic coersion. It doesn't use a GroupVersion to avoid custom marshalling
//
// +protobuf.options.(gogoproto.goproto_stringer)=false
message GroupVersionResource {
optional string group = 1;
optional string version = 2;
optional string resource = 3;
}
// A label selector is a label query over a set of resources. The result of matchLabels and
// matchExpressions are ANDed. An empty label selector matches all objects. A null
// label selector matches no objects.
message LabelSelector {
// matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
// map is equivalent to an element of matchExpressions, whose key field is "key", the
// operator is "In", and the values array contains only "value". The requirements are ANDed.
// +optional
map<string, string> matchLabels = 1;
// matchExpressions is a list of label selector requirements. The requirements are ANDed.
// +optional
repeated LabelSelectorRequirement matchExpressions = 2;
}
// A label selector requirement is a selector that contains values, a key, and an operator that
// relates the key and values.
message LabelSelectorRequirement {
// key is the label key that the selector applies to.
optional string key = 1;
// operator represents a key's relationship to a set of values.
// Valid operators ard In, NotIn, Exists and DoesNotExist.
optional string operator = 2;
// values is an array of string values. If the operator is In or NotIn,
// the values array must be non-empty. If the operator is Exists or DoesNotExist,
// the values array must be empty. This array is replaced during a strategic
// merge patch.
// +optional
repeated string values = 3;
}
// ListMeta describes metadata that synthetic resources must have, including lists and
// various status objects. A resource may have only one of {ObjectMeta, ListMeta}.
message ListMeta {
// SelfLink is a URL representing this object.
// Populated by the system.
// Read-only.
// +optional
optional string selfLink = 1;
// String that identifies the server's internal version of this object that
// can be used by clients to determine when objects have changed.
// Value must be treated as opaque by clients and passed unmodified back to the server.
// Populated by the system.
// Read-only.
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#concurrency-control-and-consistency
// +optional
optional string resourceVersion = 2;
}
// ListOptions is the query options to a standard REST list call.
message ListOptions {
// A selector to restrict the list of returned objects by their labels.
// Defaults to everything.
// +optional
optional string labelSelector = 1;
// A selector to restrict the list of returned objects by their fields.
// Defaults to everything.
// +optional
optional string fieldSelector = 2;
// Watch for changes to the described resources and return them as a stream of
// add, update, and remove notifications. Specify resourceVersion.
// +optional
optional bool watch = 3;
// When specified with a watch call, shows changes that occur after that particular version of a resource.
// Defaults to changes from the beginning of history.
// When specified for list:
// - if unset, then the result is returned from remote storage based on quorum-read flag;
// - if it's 0, then we simply return what we currently have in cache, no guarantee;
// - if set to non zero, then the result is at least as fresh as given rv.
// +optional
optional string resourceVersion = 4;
// Timeout for the list/watch call.
// +optional
optional int64 timeoutSeconds = 5;
}
// ObjectMeta is metadata that all persisted resources must have, which includes all objects
// users must create.
message ObjectMeta {
// Name must be unique within a namespace. Is required when creating resources, although
// some resources may allow a client to request the generation of an appropriate name
// automatically. Name is primarily intended for creation idempotence and configuration
// definition.
// Cannot be updated.
// More info: http://kubernetes.io/docs/user-guide/identifiers#names
// +optional
optional string name = 1;
// GenerateName is an optional prefix, used by the server, to generate a unique
// name ONLY IF the Name field has not been provided.
// If this field is used, the name returned to the client will be different
// than the name passed. This value will also be combined with a unique suffix.
// The provided value has the same validation rules as the Name field,
// and may be truncated by the length of the suffix required to make the value
// unique on the server.
//
// If this field is specified and the generated name exists, the server will
// NOT return a 409 - instead, it will either return 201 Created or 500 with Reason
// ServerTimeout indicating a unique name could not be found in the time allotted, and the client
// should retry (optionally after the time indicated in the Retry-After header).
//
// Applied only if Name is not specified.
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#idempotency
// +optional
optional string generateName = 2;
// Namespace defines the space within each name must be unique. An empty namespace is
// equivalent to the "default" namespace, but "default" is the canonical representation.
// Not all objects are required to be scoped to a namespace - the value of this field for
// those objects will be empty.
//
// Must be a DNS_LABEL.
// Cannot be updated.
// More info: http://kubernetes.io/docs/user-guide/namespaces
// +optional
optional string namespace = 3;
// SelfLink is a URL representing this object.
// Populated by the system.
// Read-only.
// +optional
optional string selfLink = 4;
// UID is the unique in time and space value for this object. It is typically generated by
// the server on successful creation of a resource and is not allowed to change on PUT
// operations.
//
// Populated by the system.
// Read-only.
// More info: http://kubernetes.io/docs/user-guide/identifiers#uids
// +optional
optional string uid = 5;
// An opaque value that represents the internal version of this object that can
// be used by clients to determine when objects have changed. May be used for optimistic
// concurrency, change detection, and the watch operation on a resource or set of resources.
// Clients must treat these values as opaque and passed unmodified back to the server.
// They may only be valid for a particular resource or set of resources.
//
// Populated by the system.
// Read-only.
// Value must be treated as opaque by clients and .
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#concurrency-control-and-consistency
// +optional
optional string resourceVersion = 6;
// A sequence number representing a specific generation of the desired state.
// Populated by the system. Read-only.
// +optional
optional int64 generation = 7;
// CreationTimestamp is a timestamp representing the server time when this object was
// created. It is not guaranteed to be set in happens-before order across separate operations.
// Clients may not set this value. It is represented in RFC3339 form and is in UTC.
//
// Populated by the system.
// Read-only.
// Null for lists.
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata
// +optional
optional Time creationTimestamp = 8;
// DeletionTimestamp is RFC 3339 date and time at which this resource will be deleted. This
// field is set by the server when a graceful deletion is requested by the user, and is not
// directly settable by a client. The resource is expected to be deleted (no longer visible
// from resource lists, and not reachable by name) after the time in this field. Once set,
// this value may not be unset or be set further into the future, although it may be shortened
// or the resource may be deleted prior to this time. For example, a user may request that
// a pod is deleted in 30 seconds. The Kubelet will react by sending a graceful termination
// signal to the containers in the pod. After that 30 seconds, the Kubelet will send a hard
// termination signal (SIGKILL) to the container and after cleanup, remove the pod from the
// API. In the presence of network partitions, this object may still exist after this
// timestamp, until an administrator or automated process can determine the resource is
// fully terminated.
// If not set, graceful deletion of the object has not been requested.
//
// Populated by the system when a graceful deletion is requested.
// Read-only.
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata
// +optional
optional Time deletionTimestamp = 9;
// Number of seconds allowed for this object to gracefully terminate before
// it will be removed from the system. Only set when deletionTimestamp is also set.
// May only be shortened.
// Read-only.
// +optional
optional int64 deletionGracePeriodSeconds = 10;
// Map of string keys and values that can be used to organize and categorize
// (scope and select) objects. May match selectors of replication controllers
// and services.
// More info: http://kubernetes.io/docs/user-guide/labels
// +optional
map<string, string> labels = 11;
// Annotations is an unstructured key value map stored with a resource that may be
// set by external tools to store and retrieve arbitrary metadata. They are not
// queryable and should be preserved when modifying objects.
// More info: http://kubernetes.io/docs/user-guide/annotations
// +optional
map<string, string> annotations = 12;
// List of objects depended by this object. If ALL objects in the list have
// been deleted, this object will be garbage collected. If this object is managed by a controller,
// then an entry in this list will point to this controller, with the controller field set to true.
// There cannot be more than one managing controller.
// +optional
repeated OwnerReference ownerReferences = 13;
// Must be empty before the object is deleted from the registry. Each entry
// is an identifier for the responsible component that will remove the entry
// from the list. If the deletionTimestamp of the object is non-nil, entries
// in this list can only be removed.
// +optional
repeated string finalizers = 14;
// The name of the cluster which the object belongs to.
// This is used to distinguish resources with same name and namespace in different clusters.
// This field is not set anywhere right now and apiserver is going to ignore it if set in create or update request.
// +optional
optional string clusterName = 15;
}
// OwnerReference contains enough information to let you identify an owning
// object. Currently, an owning object must be in the same namespace, so there
// is no namespace field.
message OwnerReference {
// API version of the referent.
optional string apiVersion = 5;
// Kind of the referent.
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds
optional string kind = 1;
// Name of the referent.
// More info: http://kubernetes.io/docs/user-guide/identifiers#names
optional string name = 3;
// UID of the referent.
// More info: http://kubernetes.io/docs/user-guide/identifiers#uids
optional string uid = 4;
// If true, this reference points to the managing controller.
// +optional
optional bool controller = 6;
}
// Preconditions must be fulfilled before an operation (update, delete, etc.) is carried out.
message Preconditions {
// Specifies the target UID.
// +optional
optional string uid = 1;
}
// RootPaths lists the paths available at root.
// For example: "/healthz", "/apis".
message RootPaths {
// paths are the paths available at root.
repeated string paths = 1;
}
// ServerAddressByClientCIDR helps the client to determine the server address that they should use, depending on the clientCIDR that they match.
message ServerAddressByClientCIDR {
// The CIDR with which clients can match their IP to figure out the server address that they should use.
optional string clientCIDR = 1;
// Address of this server, suitable for a client that matches the above CIDR.
// This can be a hostname, hostname:port, IP or IP:port.
optional string serverAddress = 2;
}
// Status is a return value for calls that don't return other objects.
message Status {
// Standard list metadata.
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds
// +optional
optional ListMeta metadata = 1;
// Status of the operation.
// One of: "Success" or "Failure".
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status
// +optional
optional string status = 2;
// A human-readable description of the status of this operation.
// +optional
optional string message = 3;
// A machine-readable description of why this operation is in the
// "Failure" status. If this value is empty there
// is no information available. A Reason clarifies an HTTP status
// code but does not override it.
// +optional
optional string reason = 4;
// Extended data associated with the reason. Each reason may define its
// own extended details. This field is optional and the data returned
// is not guaranteed to conform to any schema except that defined by
// the reason type.
// +optional
optional StatusDetails details = 5;
// Suggested HTTP return code for this status, 0 if not set.
// +optional
optional int32 code = 6;
}
// StatusCause provides more information about an api.Status failure, including
// cases when multiple errors are encountered.
message StatusCause {
// A machine-readable description of the cause of the error. If this value is
// empty there is no information available.
// +optional
optional string reason = 1;
// A human-readable description of the cause of the error. This field may be
// presented as-is to a reader.
// +optional
optional string message = 2;
// The field of the resource that has caused this error, as named by its JSON
// serialization. May include dot and postfix notation for nested attributes.
// Arrays are zero-indexed. Fields may appear more than once in an array of
// causes due to fields having multiple errors.
// Optional.
//
// Examples:
// "name" - the field "name" on the current resource
// "items[0].name" - the field "name" on the first array entry in "items"
// +optional
optional string field = 3;
}
// StatusDetails is a set of additional properties that MAY be set by the
// server to provide additional information about a response. The Reason
// field of a Status object defines what attributes will be set. Clients
// must ignore fields that do not match the defined type of each attribute,
// and should assume that any attribute may be empty, invalid, or under
// defined.
message StatusDetails {
// The name attribute of the resource associated with the status StatusReason
// (when there is a single name which can be described).
// +optional
optional string name = 1;
// The group attribute of the resource associated with the status StatusReason.
// +optional
optional string group = 2;
// The kind attribute of the resource associated with the status StatusReason.
// On some operations may differ from the requested resource Kind.
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds
// +optional
optional string kind = 3;
// The Causes array includes more details associated with the StatusReason
// failure. Not all StatusReasons may provide detailed causes.
// +optional
repeated StatusCause causes = 4;
// If specified, the time in seconds before the operation should be retried.
// +optional
optional int32 retryAfterSeconds = 5;
}
// Time is a wrapper around time.Time which supports correct
// marshaling to YAML and JSON. Wrappers are provided for many
// of the factory methods that the time package offers.
//
// +protobuf.options.marshal=false
// +protobuf.as=Timestamp
// +protobuf.options.(gogoproto.goproto_stringer)=false
message Time {
// Represents seconds of UTC time since Unix epoch
// 1970-01-01T00:00:00Z. Must be from from 0001-01-01T00:00:00Z to
// 9999-12-31T23:59:59Z inclusive.
optional int64 seconds = 1;
// Non-negative fractions of a second at nanosecond resolution. Negative
// second values with fractions must still have non-negative nanos values
// that count forward in time. Must be from 0 to 999,999,999
// inclusive. This field may be limited in precision depending on context.
optional int32 nanos = 2;
}
// Timestamp is a struct that is equivalent to Time, but intended for
// protobuf marshalling/unmarshalling. It is generated into a serialization
// that matches Time. Do not use in Go structs.
message Timestamp {
// Represents seconds of UTC time since Unix epoch
// 1970-01-01T00:00:00Z. Must be from from 0001-01-01T00:00:00Z to
// 9999-12-31T23:59:59Z inclusive.
optional int64 seconds = 1;
// Non-negative fractions of a second at nanosecond resolution. Negative
// second values with fractions must still have non-negative nanos values
// that count forward in time. Must be from 0 to 999,999,999
// inclusive. This field may be limited in precision depending on context.
optional int32 nanos = 2;
}
// TypeMeta describes an individual object in an API response or request
// with strings representing the type of the object and its API schema version.
// Structures that are versioned or persisted should inline TypeMeta.
message TypeMeta {
// Kind is a string value representing the REST resource this object represents.
// Servers may infer this from the endpoint the client submits requests to.
// Cannot be updated.
// In CamelCase.
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds
// +optional
optional string kind = 1;
// APIVersion defines the versioned schema of this representation of an object.
// Servers should convert recognized schemas to the latest internal value, and
// may reject unrecognized values.
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#resources
// +optional
optional string apiVersion = 2;
}
// Verbs masks the value so protobuf can generate
//
// +protobuf.nullable=true
// +protobuf.options.(gogoproto.goproto_stringer)=false
message Verbs {
// items, if empty, will result in an empty slice
repeated string items = 1;
}
// Event represents a single event to a watched resource.
//
// +protobuf=true
message WatchEvent {
optional string type = 1;
// Object is:
// * If Type is Added or Modified: the new state of the object.
// * If Type is Deleted: the state of the object immediately before deletion.
// * If Type is Error: *Status is recommended; other types may make sense
// depending on context.
optional k8s.io.apimachinery.pkg.runtime.RawExtension object = 2;
}

View file

@ -0,0 +1,148 @@
/*
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 v1
import (
"encoding/json"
"fmt"
"strings"
"k8s.io/apimachinery/pkg/runtime/schema"
)
// GroupResource specifies a Group and a Resource, but does not force a version. This is useful for identifying
// concepts during lookup stages without having partially valid types
//
// +protobuf.options.(gogoproto.goproto_stringer)=false
type GroupResource struct {
Group string `protobuf:"bytes,1,opt,name=group"`
Resource string `protobuf:"bytes,2,opt,name=resource"`
}
func (gr *GroupResource) String() string {
if len(gr.Group) == 0 {
return gr.Resource
}
return gr.Resource + "." + gr.Group
}
// GroupVersionResource unambiguously identifies a resource. It doesn't anonymously include GroupVersion
// to avoid automatic coersion. It doesn't use a GroupVersion to avoid custom marshalling
//
// +protobuf.options.(gogoproto.goproto_stringer)=false
type GroupVersionResource struct {
Group string `protobuf:"bytes,1,opt,name=group"`
Version string `protobuf:"bytes,2,opt,name=version"`
Resource string `protobuf:"bytes,3,opt,name=resource"`
}
func (gvr *GroupVersionResource) String() string {
return strings.Join([]string{gvr.Group, "/", gvr.Version, ", Resource=", gvr.Resource}, "")
}
// GroupKind specifies a Group and a Kind, but does not force a version. This is useful for identifying
// concepts during lookup stages without having partially valid types
//
// +protobuf.options.(gogoproto.goproto_stringer)=false
type GroupKind struct {
Group string `protobuf:"bytes,1,opt,name=group"`
Kind string `protobuf:"bytes,2,opt,name=kind"`
}
func (gk *GroupKind) String() string {
if len(gk.Group) == 0 {
return gk.Kind
}
return gk.Kind + "." + gk.Group
}
// GroupVersionKind unambiguously identifies a kind. It doesn't anonymously include GroupVersion
// to avoid automatic coersion. It doesn't use a GroupVersion to avoid custom marshalling
//
// +protobuf.options.(gogoproto.goproto_stringer)=false
type GroupVersionKind struct {
Group string `protobuf:"bytes,1,opt,name=group"`
Version string `protobuf:"bytes,2,opt,name=version"`
Kind string `protobuf:"bytes,3,opt,name=kind"`
}
func (gvk GroupVersionKind) String() string {
return gvk.Group + "/" + gvk.Version + ", Kind=" + gvk.Kind
}
// GroupVersion contains the "group" and the "version", which uniquely identifies the API.
//
// +protobuf.options.(gogoproto.goproto_stringer)=false
type GroupVersion struct {
Group string `protobuf:"bytes,1,opt,name=group"`
Version string `protobuf:"bytes,2,opt,name=version"`
}
// Empty returns true if group and version are empty
func (gv GroupVersion) Empty() bool {
return len(gv.Group) == 0 && len(gv.Version) == 0
}
// String puts "group" and "version" into a single "group/version" string. For the legacy v1
// it returns "v1".
func (gv GroupVersion) String() string {
// special case the internal apiVersion for the legacy kube types
if gv.Empty() {
return ""
}
// special case of "v1" for backward compatibility
if len(gv.Group) == 0 && gv.Version == "v1" {
return gv.Version
}
if len(gv.Group) > 0 {
return gv.Group + "/" + gv.Version
}
return gv.Version
}
// MarshalJSON implements the json.Marshaller interface.
func (gv GroupVersion) MarshalJSON() ([]byte, error) {
s := gv.String()
if strings.Count(s, "/") > 1 {
return []byte{}, fmt.Errorf("illegal GroupVersion %v: contains more than one /", s)
}
return json.Marshal(s)
}
func (gv *GroupVersion) unmarshal(value []byte) error {
var s string
if err := json.Unmarshal(value, &s); err != nil {
return err
}
parsed, err := schema.ParseGroupVersion(s)
if err != nil {
return err
}
gv.Group, gv.Version = parsed.Group, parsed.Version
return nil
}
// UnmarshalJSON implements the json.Unmarshaller interface.
func (gv *GroupVersion) UnmarshalJSON(value []byte) error {
return gv.unmarshal(value)
}
// UnmarshalTEXT implements the Ugorji's encoding.TextUnmarshaler interface.
func (gv *GroupVersion) UnmarshalText(value []byte) error {
return gv.unmarshal(value)
}

View file

@ -0,0 +1,78 @@
/*
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 v1
import (
"encoding/json"
"reflect"
"testing"
"github.com/ugorji/go/codec"
)
type GroupVersionHolder struct {
GV GroupVersion `json:"val"`
}
func TestGroupVersionUnmarshalJSON(t *testing.T) {
cases := []struct {
input []byte
expect GroupVersion
}{
{[]byte(`{"val": "v1"}`), GroupVersion{"", "v1"}},
{[]byte(`{"val": "extensions/v1beta1"}`), GroupVersion{"extensions", "v1beta1"}},
}
for _, c := range cases {
var result GroupVersionHolder
// test golang lib's JSON codec
if err := json.Unmarshal([]byte(c.input), &result); err != nil {
t.Errorf("JSON codec failed to unmarshal input '%v': %v", c.input, err)
}
if !reflect.DeepEqual(result.GV, c.expect) {
t.Errorf("JSON codec failed to unmarshal input '%s': expected %+v, got %+v", c.input, c.expect, result.GV)
}
// test the Ugorji codec
if err := codec.NewDecoderBytes(c.input, new(codec.JsonHandle)).Decode(&result); err != nil {
t.Errorf("Ugorji codec failed to unmarshal input '%v': %v", c.input, err)
}
if !reflect.DeepEqual(result.GV, c.expect) {
t.Errorf("Ugorji codec failed to unmarshal input '%s': expected %+v, got %+v", c.input, c.expect, result.GV)
}
}
}
func TestGroupVersionMarshalJSON(t *testing.T) {
cases := []struct {
input GroupVersion
expect []byte
}{
{GroupVersion{"", "v1"}, []byte(`{"val":"v1"}`)},
{GroupVersion{"extensions", "v1beta1"}, []byte(`{"val":"extensions/v1beta1"}`)},
}
for _, c := range cases {
input := GroupVersionHolder{c.input}
result, err := json.Marshal(&input)
if err != nil {
t.Errorf("Failed to marshal input '%v': %v", input, err)
}
if !reflect.DeepEqual(result, c.expect) {
t.Errorf("Failed to marshal input '%+v': expected: %s, got: %s", input, c.expect, result)
}
}
}

228
vendor/k8s.io/apimachinery/pkg/apis/meta/v1/helpers.go generated vendored Normal file
View file

@ -0,0 +1,228 @@
/*
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 v1
import (
"fmt"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/selection"
"k8s.io/apimachinery/pkg/types"
)
// LabelSelectorAsSelector converts the LabelSelector api type into a struct that implements
// labels.Selector
// Note: This function should be kept in sync with the selector methods in pkg/labels/selector.go
func LabelSelectorAsSelector(ps *LabelSelector) (labels.Selector, error) {
if ps == nil {
return labels.Nothing(), nil
}
if len(ps.MatchLabels)+len(ps.MatchExpressions) == 0 {
return labels.Everything(), nil
}
selector := labels.NewSelector()
for k, v := range ps.MatchLabels {
r, err := labels.NewRequirement(k, selection.Equals, []string{v})
if err != nil {
return nil, err
}
selector = selector.Add(*r)
}
for _, expr := range ps.MatchExpressions {
var op selection.Operator
switch expr.Operator {
case LabelSelectorOpIn:
op = selection.In
case LabelSelectorOpNotIn:
op = selection.NotIn
case LabelSelectorOpExists:
op = selection.Exists
case LabelSelectorOpDoesNotExist:
op = selection.DoesNotExist
default:
return nil, fmt.Errorf("%q is not a valid pod selector operator", expr.Operator)
}
r, err := labels.NewRequirement(expr.Key, op, append([]string(nil), expr.Values...))
if err != nil {
return nil, err
}
selector = selector.Add(*r)
}
return selector, nil
}
// LabelSelectorAsMap converts the LabelSelector api type into a map of strings, ie. the
// original structure of a label selector. Operators that cannot be converted into plain
// labels (Exists, DoesNotExist, NotIn, and In with more than one value) will result in
// an error.
func LabelSelectorAsMap(ps *LabelSelector) (map[string]string, error) {
if ps == nil {
return nil, nil
}
selector := map[string]string{}
for k, v := range ps.MatchLabels {
selector[k] = v
}
for _, expr := range ps.MatchExpressions {
switch expr.Operator {
case LabelSelectorOpIn:
if len(expr.Values) != 1 {
return selector, fmt.Errorf("operator %q without a single value cannot be converted into the old label selector format", expr.Operator)
}
// Should we do anything in case this will override a previous key-value pair?
selector[expr.Key] = expr.Values[0]
case LabelSelectorOpNotIn, LabelSelectorOpExists, LabelSelectorOpDoesNotExist:
return selector, fmt.Errorf("operator %q cannot be converted into the old label selector format", expr.Operator)
default:
return selector, fmt.Errorf("%q is not a valid selector operator", expr.Operator)
}
}
return selector, nil
}
// ParseToLabelSelector parses a string representing a selector into a LabelSelector object.
// Note: This function should be kept in sync with the parser in pkg/labels/selector.go
func ParseToLabelSelector(selector string) (*LabelSelector, error) {
reqs, err := labels.ParseToRequirements(selector)
if err != nil {
return nil, fmt.Errorf("couldn't parse the selector string \"%s\": %v", selector, err)
}
labelSelector := &LabelSelector{
MatchLabels: map[string]string{},
MatchExpressions: []LabelSelectorRequirement{},
}
for _, req := range reqs {
var op LabelSelectorOperator
switch req.Operator() {
case selection.Equals, selection.DoubleEquals:
vals := req.Values()
if vals.Len() != 1 {
return nil, fmt.Errorf("equals operator must have exactly one value")
}
val, ok := vals.PopAny()
if !ok {
return nil, fmt.Errorf("equals operator has exactly one value but it cannot be retrieved")
}
labelSelector.MatchLabels[req.Key()] = val
continue
case selection.In:
op = LabelSelectorOpIn
case selection.NotIn:
op = LabelSelectorOpNotIn
case selection.Exists:
op = LabelSelectorOpExists
case selection.DoesNotExist:
op = LabelSelectorOpDoesNotExist
case selection.GreaterThan, selection.LessThan:
// Adding a separate case for these operators to indicate that this is deliberate
return nil, fmt.Errorf("%q isn't supported in label selectors", req.Operator())
default:
return nil, fmt.Errorf("%q is not a valid label selector operator", req.Operator())
}
labelSelector.MatchExpressions = append(labelSelector.MatchExpressions, LabelSelectorRequirement{
Key: req.Key(),
Operator: op,
Values: req.Values().List(),
})
}
return labelSelector, nil
}
// SetAsLabelSelector converts the labels.Set object into a LabelSelector api object.
func SetAsLabelSelector(ls labels.Set) *LabelSelector {
if ls == nil {
return nil
}
selector := &LabelSelector{
MatchLabels: make(map[string]string),
}
for label, value := range ls {
selector.MatchLabels[label] = value
}
return selector
}
// FormatLabelSelector convert labelSelector into plain string
func FormatLabelSelector(labelSelector *LabelSelector) string {
selector, err := LabelSelectorAsSelector(labelSelector)
if err != nil {
return "<error>"
}
l := selector.String()
if len(l) == 0 {
l = "<none>"
}
return l
}
func ExtractGroupVersions(l *APIGroupList) []string {
var groupVersions []string
for _, g := range l.Groups {
for _, gv := range g.Versions {
groupVersions = append(groupVersions, gv.GroupVersion)
}
}
return groupVersions
}
// HasAnnotation returns a bool if passed in annotation exists
func HasAnnotation(obj ObjectMeta, ann string) bool {
_, found := obj.Annotations[ann]
return found
}
// SetMetaDataAnnotation sets the annotation and value
func SetMetaDataAnnotation(obj *ObjectMeta, ann string, value string) {
if obj.Annotations == nil {
obj.Annotations = make(map[string]string)
}
obj.Annotations[ann] = value
}
// SingleObject returns a ListOptions for watching a single object.
func SingleObject(meta ObjectMeta) ListOptions {
return ListOptions{
FieldSelector: fields.OneTermEqualSelector("metadata.name", meta.Name).String(),
ResourceVersion: meta.ResourceVersion,
}
}
// NewDeleteOptions returns a DeleteOptions indicating the resource should
// be deleted within the specified grace period. Use zero to indicate
// immediate deletion. If you would prefer to use the default grace period,
// use &metav1.DeleteOptions{} directly.
func NewDeleteOptions(grace int64) *DeleteOptions {
return &DeleteOptions{GracePeriodSeconds: &grace}
}
// NewPreconditionDeleteOptions returns a DeleteOptions with a UID precondition set.
func NewPreconditionDeleteOptions(uid string) *DeleteOptions {
u := types.UID(uid)
p := Preconditions{UID: &u}
return &DeleteOptions{Preconditions: &p}
}
// NewUIDPreconditions returns a Preconditions with UID set.
func NewUIDPreconditions(uid string) *Preconditions {
u := types.UID(uid)
return &Preconditions{UID: &u}
}

View file

@ -0,0 +1,154 @@
/*
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 v1
import (
"reflect"
"strings"
"testing"
"k8s.io/apimachinery/pkg/labels"
)
func TestLabelSelectorAsSelector(t *testing.T) {
matchLabels := map[string]string{"foo": "bar"}
matchExpressions := []LabelSelectorRequirement{{
Key: "baz",
Operator: LabelSelectorOpIn,
Values: []string{"qux", "norf"},
}}
mustParse := func(s string) labels.Selector {
out, e := labels.Parse(s)
if e != nil {
panic(e)
}
return out
}
tc := []struct {
in *LabelSelector
out labels.Selector
expectErr bool
}{
{in: nil, out: labels.Nothing()},
{in: &LabelSelector{}, out: labels.Everything()},
{
in: &LabelSelector{MatchLabels: matchLabels},
out: mustParse("foo=bar"),
},
{
in: &LabelSelector{MatchExpressions: matchExpressions},
out: mustParse("baz in (norf,qux)"),
},
{
in: &LabelSelector{MatchLabels: matchLabels, MatchExpressions: matchExpressions},
out: mustParse("baz in (norf,qux),foo=bar"),
},
{
in: &LabelSelector{
MatchExpressions: []LabelSelectorRequirement{{
Key: "baz",
Operator: LabelSelectorOpExists,
Values: []string{"qux", "norf"},
}},
},
expectErr: true,
},
}
for i, tc := range tc {
out, err := LabelSelectorAsSelector(tc.in)
if err == nil && tc.expectErr {
t.Errorf("[%v]expected error but got none.", i)
}
if err != nil && !tc.expectErr {
t.Errorf("[%v]did not expect error but got: %v", i, err)
}
if !reflect.DeepEqual(out, tc.out) {
t.Errorf("[%v]expected:\n\t%+v\nbut got:\n\t%+v", i, tc.out, out)
}
}
}
func TestLabelSelectorAsMap(t *testing.T) {
matchLabels := map[string]string{"foo": "bar"}
matchExpressions := func(operator LabelSelectorOperator, values []string) []LabelSelectorRequirement {
return []LabelSelectorRequirement{{
Key: "baz",
Operator: operator,
Values: values,
}}
}
tests := []struct {
in *LabelSelector
out map[string]string
errString string
}{
{in: nil, out: nil},
{
in: &LabelSelector{MatchLabels: matchLabels},
out: map[string]string{"foo": "bar"},
},
{
in: &LabelSelector{MatchLabels: matchLabels, MatchExpressions: matchExpressions(LabelSelectorOpIn, []string{"norf"})},
out: map[string]string{"foo": "bar", "baz": "norf"},
},
{
in: &LabelSelector{MatchExpressions: matchExpressions(LabelSelectorOpIn, []string{"norf"})},
out: map[string]string{"baz": "norf"},
},
{
in: &LabelSelector{MatchLabels: matchLabels, MatchExpressions: matchExpressions(LabelSelectorOpIn, []string{"norf", "qux"})},
out: map[string]string{"foo": "bar"},
errString: "without a single value cannot be converted",
},
{
in: &LabelSelector{MatchExpressions: matchExpressions(LabelSelectorOpNotIn, []string{"norf", "qux"})},
out: map[string]string{},
errString: "cannot be converted",
},
{
in: &LabelSelector{MatchLabels: matchLabels, MatchExpressions: matchExpressions(LabelSelectorOpExists, []string{})},
out: map[string]string{"foo": "bar"},
errString: "cannot be converted",
},
{
in: &LabelSelector{MatchExpressions: matchExpressions(LabelSelectorOpDoesNotExist, []string{})},
out: map[string]string{},
errString: "cannot be converted",
},
}
for i, tc := range tests {
out, err := LabelSelectorAsMap(tc.in)
if err == nil && len(tc.errString) > 0 {
t.Errorf("[%v]expected error but got none.", i)
continue
}
if err != nil && len(tc.errString) == 0 {
t.Errorf("[%v]did not expect error but got: %v", i, err)
continue
}
if err != nil && len(tc.errString) > 0 && !strings.Contains(err.Error(), tc.errString) {
t.Errorf("[%v]expected error with %q but got: %v", i, tc.errString, err)
continue
}
if !reflect.DeepEqual(out, tc.out) {
t.Errorf("[%v]expected:\n\t%+v\nbut got:\n\t%+v", i, tc.out, out)
}
}
}

75
vendor/k8s.io/apimachinery/pkg/apis/meta/v1/labels.go generated vendored Normal file
View file

@ -0,0 +1,75 @@
/*
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 v1
// Clones the given selector and returns a new selector with the given key and value added.
// Returns the given selector, if labelKey is empty.
func CloneSelectorAndAddLabel(selector *LabelSelector, labelKey, labelValue string) *LabelSelector {
if labelKey == "" {
// Don't need to add a label.
return selector
}
// Clone.
newSelector := new(LabelSelector)
// TODO(madhusudancs): Check if you can use deepCopy_extensions_LabelSelector here.
newSelector.MatchLabels = make(map[string]string)
if selector.MatchLabels != nil {
for key, val := range selector.MatchLabels {
newSelector.MatchLabels[key] = val
}
}
newSelector.MatchLabels[labelKey] = labelValue
if selector.MatchExpressions != nil {
newMExps := make([]LabelSelectorRequirement, len(selector.MatchExpressions))
for i, me := range selector.MatchExpressions {
newMExps[i].Key = me.Key
newMExps[i].Operator = me.Operator
if me.Values != nil {
newMExps[i].Values = make([]string, len(me.Values))
copy(newMExps[i].Values, me.Values)
} else {
newMExps[i].Values = nil
}
}
newSelector.MatchExpressions = newMExps
} else {
newSelector.MatchExpressions = nil
}
return newSelector
}
// AddLabelToSelector returns a selector with the given key and value added to the given selector's MatchLabels.
func AddLabelToSelector(selector *LabelSelector, labelKey, labelValue string) *LabelSelector {
if labelKey == "" {
// Don't need to add a label.
return selector
}
if selector.MatchLabels == nil {
selector.MatchLabels = make(map[string]string)
}
selector.MatchLabels[labelKey] = labelValue
return selector
}
// SelectorHasLabel checks if the given selector contains the given label key in its MatchLabels
func SelectorHasLabel(selector *LabelSelector, labelKey string) bool {
return len(selector.MatchLabels[labelKey]) > 0
}

View file

@ -0,0 +1,120 @@
/*
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 v1
import (
"reflect"
"testing"
)
func TestCloneSelectorAndAddLabel(t *testing.T) {
labels := map[string]string{
"foo1": "bar1",
"foo2": "bar2",
"foo3": "bar3",
}
cases := []struct {
labels map[string]string
labelKey string
labelValue string
want map[string]string
}{
{
labels: labels,
want: labels,
},
{
labels: labels,
labelKey: "foo4",
labelValue: "89",
want: map[string]string{
"foo1": "bar1",
"foo2": "bar2",
"foo3": "bar3",
"foo4": "89",
},
},
{
labels: nil,
labelKey: "foo4",
labelValue: "12",
want: map[string]string{
"foo4": "12",
},
},
}
for _, tc := range cases {
ls_in := LabelSelector{MatchLabels: tc.labels}
ls_out := LabelSelector{MatchLabels: tc.want}
got := CloneSelectorAndAddLabel(&ls_in, tc.labelKey, tc.labelValue)
if !reflect.DeepEqual(got, &ls_out) {
t.Errorf("got %v, want %v", got, tc.want)
}
}
}
func TestAddLabelToSelector(t *testing.T) {
labels := map[string]string{
"foo1": "bar1",
"foo2": "bar2",
"foo3": "bar3",
}
cases := []struct {
labels map[string]string
labelKey string
labelValue string
want map[string]string
}{
{
labels: labels,
want: labels,
},
{
labels: labels,
labelKey: "foo4",
labelValue: "89",
want: map[string]string{
"foo1": "bar1",
"foo2": "bar2",
"foo3": "bar3",
"foo4": "89",
},
},
{
labels: nil,
labelKey: "foo4",
labelValue: "12",
want: map[string]string{
"foo4": "12",
},
},
}
for _, tc := range cases {
ls_in := LabelSelector{MatchLabels: tc.labels}
ls_out := LabelSelector{MatchLabels: tc.want}
got := AddLabelToSelector(&ls_in, tc.labelKey, tc.labelValue)
if !reflect.DeepEqual(got, &ls_out) {
t.Errorf("got %v, want %v", got, tc.want)
}
}
}

201
vendor/k8s.io/apimachinery/pkg/apis/meta/v1/meta.go generated vendored Normal file
View file

@ -0,0 +1,201 @@
/*
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 v1
import (
"k8s.io/apimachinery/pkg/conversion"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
)
// ObjectMetaFor returns a pointer to a provided object's ObjectMeta.
// TODO: allow runtime.Unknown to extract this object
// TODO: Remove this function and use meta.ObjectMetaAccessor() instead.
func ObjectMetaFor(obj runtime.Object) (*ObjectMeta, error) {
v, err := conversion.EnforcePtr(obj)
if err != nil {
return nil, err
}
var meta *ObjectMeta
err = runtime.FieldPtr(v, "ObjectMeta", &meta)
return meta, err
}
// ListMetaFor returns a pointer to a provided object's ListMeta,
// or an error if the object does not have that pointer.
// TODO: allow runtime.Unknown to extract this object
// TODO: Remove this function and use meta.ObjectMetaAccessor() instead.
func ListMetaFor(obj runtime.Object) (*ListMeta, error) {
v, err := conversion.EnforcePtr(obj)
if err != nil {
return nil, err
}
var meta *ListMeta
err = runtime.FieldPtr(v, "ListMeta", &meta)
return meta, err
}
// TODO: move this, Object, List, and Type to a different package
type ObjectMetaAccessor interface {
GetObjectMeta() Object
}
// Object lets you work with object 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.
type Object interface {
GetNamespace() string
SetNamespace(namespace string)
GetName() string
SetName(name string)
GetGenerateName() string
SetGenerateName(name string)
GetUID() types.UID
SetUID(uid types.UID)
GetResourceVersion() string
SetResourceVersion(version string)
GetSelfLink() string
SetSelfLink(selfLink string)
GetCreationTimestamp() Time
SetCreationTimestamp(timestamp Time)
GetDeletionTimestamp() *Time
SetDeletionTimestamp(timestamp *Time)
GetLabels() map[string]string
SetLabels(labels map[string]string)
GetAnnotations() map[string]string
SetAnnotations(annotations map[string]string)
GetFinalizers() []string
SetFinalizers(finalizers []string)
GetOwnerReferences() []OwnerReference
SetOwnerReferences([]OwnerReference)
GetClusterName() string
SetClusterName(clusterName string)
}
// ListMetaAccessor retrieves the list interface from an object
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.
// TODO: move this, and TypeMeta and ListMeta, to a different package
type List interface {
GetResourceVersion() string
SetResourceVersion(version string)
GetSelfLink() string
SetSelfLink(selfLink string)
}
// Type exposes the type and APIVersion of versioned or internal API objects.
// TODO: move this, and TypeMeta and ListMeta, to a different package
type Type interface {
GetAPIVersion() string
SetAPIVersion(version string)
GetKind() string
SetKind(kind string)
}
func (meta *ListMeta) GetResourceVersion() string { return meta.ResourceVersion }
func (meta *ListMeta) SetResourceVersion(version string) { meta.ResourceVersion = version }
func (meta *ListMeta) GetSelfLink() string { return meta.SelfLink }
func (meta *ListMeta) SetSelfLink(selfLink string) { meta.SelfLink = selfLink }
func (obj *TypeMeta) GetObjectKind() schema.ObjectKind { return obj }
// SetGroupVersionKind satisfies the ObjectKind interface for all objects that embed TypeMeta
func (obj *TypeMeta) SetGroupVersionKind(gvk schema.GroupVersionKind) {
obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
}
// GroupVersionKind satisfies the ObjectKind interface for all objects that embed TypeMeta
func (obj *TypeMeta) GroupVersionKind() schema.GroupVersionKind {
return schema.FromAPIVersionAndKind(obj.APIVersion, obj.Kind)
}
func (obj *ListMeta) GetListMeta() List { return obj }
func (obj *ObjectMeta) GetObjectMeta() Object { return obj }
// Namespace implements metav1.Object for any object with an ObjectMeta typed field. Allows
// fast, direct access to metadata fields for API objects.
func (meta *ObjectMeta) GetNamespace() string { return meta.Namespace }
func (meta *ObjectMeta) SetNamespace(namespace string) { meta.Namespace = namespace }
func (meta *ObjectMeta) GetName() string { return meta.Name }
func (meta *ObjectMeta) SetName(name string) { meta.Name = name }
func (meta *ObjectMeta) GetGenerateName() string { return meta.GenerateName }
func (meta *ObjectMeta) SetGenerateName(generateName string) { meta.GenerateName = generateName }
func (meta *ObjectMeta) GetUID() types.UID { return meta.UID }
func (meta *ObjectMeta) SetUID(uid types.UID) { meta.UID = uid }
func (meta *ObjectMeta) GetResourceVersion() string { return meta.ResourceVersion }
func (meta *ObjectMeta) SetResourceVersion(version string) { meta.ResourceVersion = version }
func (meta *ObjectMeta) GetSelfLink() string { return meta.SelfLink }
func (meta *ObjectMeta) SetSelfLink(selfLink string) { meta.SelfLink = selfLink }
func (meta *ObjectMeta) GetCreationTimestamp() Time { return meta.CreationTimestamp }
func (meta *ObjectMeta) SetCreationTimestamp(creationTimestamp Time) {
meta.CreationTimestamp = creationTimestamp
}
func (meta *ObjectMeta) GetDeletionTimestamp() *Time { return meta.DeletionTimestamp }
func (meta *ObjectMeta) SetDeletionTimestamp(deletionTimestamp *Time) {
meta.DeletionTimestamp = deletionTimestamp
}
func (meta *ObjectMeta) GetLabels() map[string]string { return meta.Labels }
func (meta *ObjectMeta) SetLabels(labels map[string]string) { meta.Labels = labels }
func (meta *ObjectMeta) GetAnnotations() map[string]string { return meta.Annotations }
func (meta *ObjectMeta) SetAnnotations(annotations map[string]string) { meta.Annotations = annotations }
func (meta *ObjectMeta) GetFinalizers() []string { return meta.Finalizers }
func (meta *ObjectMeta) SetFinalizers(finalizers []string) { meta.Finalizers = finalizers }
func (meta *ObjectMeta) GetOwnerReferences() []OwnerReference {
ret := make([]OwnerReference, len(meta.OwnerReferences))
for i := 0; i < len(meta.OwnerReferences); i++ {
ret[i].Kind = meta.OwnerReferences[i].Kind
ret[i].Name = meta.OwnerReferences[i].Name
ret[i].UID = meta.OwnerReferences[i].UID
ret[i].APIVersion = meta.OwnerReferences[i].APIVersion
if meta.OwnerReferences[i].Controller != nil {
value := *meta.OwnerReferences[i].Controller
ret[i].Controller = &value
}
}
return ret
}
func (meta *ObjectMeta) SetOwnerReferences(references []OwnerReference) {
newReferences := make([]OwnerReference, len(references))
for i := 0; i < len(references); i++ {
newReferences[i].Kind = references[i].Kind
newReferences[i].Name = references[i].Name
newReferences[i].UID = references[i].UID
newReferences[i].APIVersion = references[i].APIVersion
if references[i].Controller != nil {
value := *references[i].Controller
newReferences[i].Controller = &value
}
}
meta.OwnerReferences = newReferences
}
func (meta *ObjectMeta) GetClusterName() string {
return meta.ClusterName
}
func (meta *ObjectMeta) SetClusterName(clusterName string) {
meta.ClusterName = clusterName
}

View file

@ -0,0 +1,73 @@
/*
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 v1
import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
// GroupName is the group name for this API.
const GroupName = "meta.k8s.io"
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: "v1"}
// WatchEventKind is name reserved for serializing watch events.
const WatchEventKind = "WatchEvent"
// Kind takes an unqualified kind and returns a Group qualified GroupKind
func Kind(kind string) schema.GroupKind {
return SchemeGroupVersion.WithKind(kind).GroupKind()
}
// AddToGroupVersion registers common meta types into schemas.
func AddToGroupVersion(scheme *runtime.Scheme, groupVersion schema.GroupVersion) {
scheme.AddKnownTypeWithName(groupVersion.WithKind(WatchEventKind), &WatchEvent{})
scheme.AddKnownTypeWithName(
schema.GroupVersion{Group: groupVersion.Group, Version: runtime.APIVersionInternal}.WithKind(WatchEventKind),
&InternalEvent{},
)
// Supports legacy code paths, most callers should use metav1.ParameterCodec for now
scheme.AddKnownTypes(groupVersion,
&ListOptions{},
&ExportOptions{},
&GetOptions{},
&DeleteOptions{},
)
scheme.AddConversionFuncs(
Convert_versioned_Event_to_watch_Event,
Convert_versioned_InternalEvent_to_versioned_Event,
Convert_watch_Event_to_versioned_Event,
Convert_versioned_Event_to_versioned_InternalEvent,
)
}
// scheme is the registry for the common types that adhere to the meta v1 API spec.
var scheme = runtime.NewScheme()
// ParameterCodec knows about query parameters used with the meta v1 API spec.
var ParameterCodec = runtime.NewParameterCodec(scheme)
func init() {
scheme.AddUnversionedTypes(SchemeGroupVersion,
&ListOptions{},
&ExportOptions{},
&GetOptions{},
&DeleteOptions{},
)
}

180
vendor/k8s.io/apimachinery/pkg/apis/meta/v1/time.go generated vendored Normal file
View file

@ -0,0 +1,180 @@
/*
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 v1
import (
"encoding/json"
"time"
"k8s.io/apimachinery/pkg/openapi"
"github.com/go-openapi/spec"
"github.com/google/gofuzz"
)
// Time is a wrapper around time.Time which supports correct
// marshaling to YAML and JSON. Wrappers are provided for many
// of the factory methods that the time package offers.
//
// +protobuf.options.marshal=false
// +protobuf.as=Timestamp
// +protobuf.options.(gogoproto.goproto_stringer)=false
type Time struct {
time.Time `protobuf:"-"`
}
// DeepCopy returns a deep-copy of the Time value. The underlying time.Time
// type is effectively immutable in the time API, so it is safe to
// copy-by-assign, despite the presence of (unexported) Pointer fields.
func (t Time) DeepCopy() Time {
return t
}
// String returns the representation of the time.
func (t Time) String() string {
return t.Time.String()
}
// NewTime returns a wrapped instance of the provided time
func NewTime(time time.Time) Time {
return Time{time}
}
// Date returns the Time corresponding to the supplied parameters
// by wrapping time.Date.
func Date(year int, month time.Month, day, hour, min, sec, nsec int, loc *time.Location) Time {
return Time{time.Date(year, month, day, hour, min, sec, nsec, loc)}
}
// Now returns the current local time.
func Now() Time {
return Time{time.Now()}
}
// IsZero returns true if the value is nil or time is zero.
func (t *Time) IsZero() bool {
if t == nil {
return true
}
return t.Time.IsZero()
}
// Before reports whether the time instant t is before u.
func (t Time) Before(u Time) bool {
return t.Time.Before(u.Time)
}
// Equal reports whether the time instant t is equal to u.
func (t Time) Equal(u Time) bool {
return t.Time.Equal(u.Time)
}
// Unix returns the local time corresponding to the given Unix time
// by wrapping time.Unix.
func Unix(sec int64, nsec int64) Time {
return Time{time.Unix(sec, nsec)}
}
// Rfc3339Copy returns a copy of the Time at second-level precision.
func (t Time) Rfc3339Copy() Time {
copied, _ := time.Parse(time.RFC3339, t.Format(time.RFC3339))
return Time{copied}
}
// UnmarshalJSON implements the json.Unmarshaller interface.
func (t *Time) UnmarshalJSON(b []byte) error {
if len(b) == 4 && string(b) == "null" {
t.Time = time.Time{}
return nil
}
var str string
json.Unmarshal(b, &str)
pt, err := time.Parse(time.RFC3339, str)
if err != nil {
return err
}
t.Time = pt.Local()
return nil
}
// UnmarshalQueryParameter converts from a URL query parameter value to an object
func (t *Time) UnmarshalQueryParameter(str string) error {
if len(str) == 0 {
t.Time = time.Time{}
return nil
}
// Tolerate requests from older clients that used JSON serialization to build query params
if len(str) == 4 && str == "null" {
t.Time = time.Time{}
return nil
}
pt, err := time.Parse(time.RFC3339, str)
if err != nil {
return err
}
t.Time = pt.Local()
return nil
}
// MarshalJSON implements the json.Marshaler interface.
func (t Time) MarshalJSON() ([]byte, error) {
if t.IsZero() {
// Encode unset/nil objects as JSON's "null".
return []byte("null"), nil
}
return json.Marshal(t.UTC().Format(time.RFC3339))
}
func (_ Time) OpenAPIDefinition() openapi.OpenAPIDefinition {
return openapi.OpenAPIDefinition{
Schema: spec.Schema{
SchemaProps: spec.SchemaProps{
Type: []string{"string"},
Format: "date-time",
},
},
}
}
// MarshalQueryParameter converts to a URL query parameter value
func (t Time) MarshalQueryParameter() (string, error) {
if t.IsZero() {
// Encode unset/nil objects as an empty string
return "", nil
}
return t.UTC().Format(time.RFC3339), nil
}
// Fuzz satisfies fuzz.Interface.
func (t *Time) Fuzz(c fuzz.Continue) {
if t == nil {
return
}
// Allow for about 1000 years of randomness. Leave off nanoseconds
// because JSON doesn't represent them so they can't round-trip
// properly.
t.Time = time.Unix(c.Rand.Int63n(1000*365*24*60*60), 0)
}
var _ fuzz.Interface = &Time{}

View file

@ -0,0 +1,85 @@
/*
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 v1
import (
"time"
)
// Timestamp is a struct that is equivalent to Time, but intended for
// protobuf marshalling/unmarshalling. It is generated into a serialization
// that matches Time. Do not use in Go structs.
type Timestamp struct {
// Represents seconds of UTC time since Unix epoch
// 1970-01-01T00:00:00Z. Must be from from 0001-01-01T00:00:00Z to
// 9999-12-31T23:59:59Z inclusive.
Seconds int64 `json:"seconds" protobuf:"varint,1,opt,name=seconds"`
// Non-negative fractions of a second at nanosecond resolution. Negative
// second values with fractions must still have non-negative nanos values
// that count forward in time. Must be from 0 to 999,999,999
// inclusive. This field may be limited in precision depending on context.
Nanos int32 `json:"nanos" protobuf:"varint,2,opt,name=nanos"`
}
// Timestamp returns the Time as a new Timestamp value.
func (m *Time) ProtoTime() *Timestamp {
if m == nil {
return &Timestamp{}
}
return &Timestamp{
Seconds: m.Time.Unix(),
Nanos: int32(m.Time.Nanosecond()),
}
}
// Size implements the protobuf marshalling interface.
func (m *Time) Size() (n int) {
if m == nil || m.Time.IsZero() {
return 0
}
return m.ProtoTime().Size()
}
// Reset implements the protobuf marshalling interface.
func (m *Time) Unmarshal(data []byte) error {
if len(data) == 0 {
m.Time = time.Time{}
return nil
}
p := Timestamp{}
if err := p.Unmarshal(data); err != nil {
return err
}
m.Time = time.Unix(p.Seconds, int64(p.Nanos)).Local()
return nil
}
// Marshal implements the protobuf marshalling interface.
func (m *Time) Marshal() (data []byte, err error) {
if m == nil || m.Time.IsZero() {
return nil, nil
}
return m.ProtoTime().Marshal()
}
// MarshalTo implements the protobuf marshalling interface.
func (m *Time) MarshalTo(data []byte) (int, error) {
if m == nil || m.Time.IsZero() {
return 0, nil
}
return m.ProtoTime().MarshalTo(data)
}

View file

@ -0,0 +1,173 @@
/*
Copyright 2014 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1
import (
"encoding/json"
"reflect"
"testing"
"time"
"github.com/ghodss/yaml"
)
type TimeHolder struct {
T Time `json:"t"`
}
func TestTimeMarshalYAML(t *testing.T) {
cases := []struct {
input Time
result string
}{
{Time{}, "t: null\n"},
{Date(1998, time.May, 5, 1, 5, 5, 50, time.FixedZone("test", -4*60*60)), "t: 1998-05-05T05:05:05Z\n"},
{Date(1998, time.May, 5, 5, 5, 5, 0, time.UTC), "t: 1998-05-05T05:05:05Z\n"},
}
for _, c := range cases {
input := TimeHolder{c.input}
result, err := yaml.Marshal(&input)
if err != nil {
t.Errorf("Failed to marshal input: '%v': %v", input, err)
}
if string(result) != c.result {
t.Errorf("Failed to marshal input: '%v': expected %+v, got %q", input, c.result, string(result))
}
}
}
func TestTimeUnmarshalYAML(t *testing.T) {
cases := []struct {
input string
result Time
}{
{"t: null\n", Time{}},
{"t: 1998-05-05T05:05:05Z\n", Time{Date(1998, time.May, 5, 5, 5, 5, 0, time.UTC).Local()}},
}
for _, c := range cases {
var result TimeHolder
if err := yaml.Unmarshal([]byte(c.input), &result); err != nil {
t.Errorf("Failed to unmarshal input '%v': %v", c.input, err)
}
if result.T != c.result {
t.Errorf("Failed to unmarshal input '%v': expected %+v, got %+v", c.input, c.result, result)
}
}
}
func TestTimeMarshalJSON(t *testing.T) {
cases := []struct {
input Time
result string
}{
{Time{}, "{\"t\":null}"},
{Date(1998, time.May, 5, 5, 5, 5, 50, time.UTC), "{\"t\":\"1998-05-05T05:05:05Z\"}"},
{Date(1998, time.May, 5, 5, 5, 5, 0, time.UTC), "{\"t\":\"1998-05-05T05:05:05Z\"}"},
}
for _, c := range cases {
input := TimeHolder{c.input}
result, err := json.Marshal(&input)
if err != nil {
t.Errorf("Failed to marshal input: '%v': %v", input, err)
}
if string(result) != c.result {
t.Errorf("Failed to marshal input: '%v': expected %+v, got %q", input, c.result, string(result))
}
}
}
func TestTimeUnmarshalJSON(t *testing.T) {
cases := []struct {
input string
result Time
}{
{"{\"t\":null}", Time{}},
{"{\"t\":\"1998-05-05T05:05:05Z\"}", Time{Date(1998, time.May, 5, 5, 5, 5, 0, time.UTC).Local()}},
}
for _, c := range cases {
var result TimeHolder
if err := json.Unmarshal([]byte(c.input), &result); err != nil {
t.Errorf("Failed to unmarshal input '%v': %v", c.input, err)
}
if result.T != c.result {
t.Errorf("Failed to unmarshal input '%v': expected %+v, got %+v", c.input, c.result, result)
}
}
}
func TestTimeMarshalJSONUnmarshalYAML(t *testing.T) {
cases := []struct {
input Time
}{
{Time{}},
{Date(1998, time.May, 5, 5, 5, 5, 50, time.Local).Rfc3339Copy()},
{Date(1998, time.May, 5, 5, 5, 5, 0, time.Local).Rfc3339Copy()},
}
for i, c := range cases {
input := TimeHolder{c.input}
jsonMarshalled, err := json.Marshal(&input)
if err != nil {
t.Errorf("%d-1: Failed to marshal input: '%v': %v", i, input, err)
}
var result TimeHolder
err = yaml.Unmarshal(jsonMarshalled, &result)
if err != nil {
t.Errorf("%d-2: Failed to unmarshal '%+v': %v", i, string(jsonMarshalled), err)
}
iN, iO := input.T.Zone()
oN, oO := result.T.Zone()
if iN != oN || iO != oO {
t.Errorf("%d-3: Time zones differ before and after serialization %s:%d %s:%d", i, iN, iO, oN, oO)
}
if input.T.UnixNano() != result.T.UnixNano() {
t.Errorf("%d-4: Failed to marshal input '%#v': got %#v", i, input, result)
}
}
}
func TestTimeProto(t *testing.T) {
cases := []struct {
input Time
}{
{Time{}},
{Date(1998, time.May, 5, 1, 5, 5, 50, time.Local)},
{Date(1998, time.May, 5, 5, 5, 5, 0, time.Local)},
}
for _, c := range cases {
input := c.input
data, err := input.Marshal()
if err != nil {
t.Fatalf("Failed to marshal input: '%v': %v", input, err)
}
time := Time{}
if err := time.Unmarshal(data); err != nil {
t.Fatalf("Failed to unmarshal output: '%v': %v", input, err)
}
if !reflect.DeepEqual(input, time) {
t.Errorf("Marshal->Unmarshal is not idempotent: '%v' vs '%v'", input, time)
}
}
}

749
vendor/k8s.io/apimachinery/pkg/apis/meta/v1/types.go generated vendored Normal file
View file

@ -0,0 +1,749 @@
/*
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 unversioned contains API types that are common to all versions.
//
// The package contains two categories of types:
// - external (serialized) types that lack their own version (e.g TypeMeta)
// - internal (never-serialized) types that are needed by several different
// api groups, and so live here, to avoid duplication and/or import loops
// (e.g. LabelSelector).
// In the future, we will probably move these categories of objects into
// separate packages.
package v1
import (
"fmt"
"strings"
"k8s.io/apimachinery/pkg/types"
)
// TypeMeta describes an individual object in an API response or request
// with strings representing the type of the object and its API schema version.
// Structures that are versioned or persisted should inline TypeMeta.
type TypeMeta struct {
// Kind is a string value representing the REST resource this object represents.
// Servers may infer this from the endpoint the client submits requests to.
// Cannot be updated.
// In CamelCase.
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds
// +optional
Kind string `json:"kind,omitempty" protobuf:"bytes,1,opt,name=kind"`
// APIVersion defines the versioned schema of this representation of an object.
// Servers should convert recognized schemas to the latest internal value, and
// may reject unrecognized values.
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#resources
// +optional
APIVersion string `json:"apiVersion,omitempty" protobuf:"bytes,2,opt,name=apiVersion"`
}
// ListMeta describes metadata that synthetic resources must have, including lists and
// various status objects. A resource may have only one of {ObjectMeta, ListMeta}.
type ListMeta struct {
// SelfLink is a URL representing this object.
// Populated by the system.
// Read-only.
// +optional
SelfLink string `json:"selfLink,omitempty" protobuf:"bytes,1,opt,name=selfLink"`
// String that identifies the server's internal version of this object that
// can be used by clients to determine when objects have changed.
// Value must be treated as opaque by clients and passed unmodified back to the server.
// Populated by the system.
// Read-only.
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#concurrency-control-and-consistency
// +optional
ResourceVersion string `json:"resourceVersion,omitempty" protobuf:"bytes,2,opt,name=resourceVersion"`
}
// ObjectMeta is metadata that all persisted resources must have, which includes all objects
// users must create.
type ObjectMeta struct {
// Name must be unique within a namespace. Is required when creating resources, although
// some resources may allow a client to request the generation of an appropriate name
// automatically. Name is primarily intended for creation idempotence and configuration
// definition.
// Cannot be updated.
// More info: http://kubernetes.io/docs/user-guide/identifiers#names
// +optional
Name string `json:"name,omitempty" protobuf:"bytes,1,opt,name=name"`
// GenerateName is an optional prefix, used by the server, to generate a unique
// name ONLY IF the Name field has not been provided.
// If this field is used, the name returned to the client will be different
// than the name passed. This value will also be combined with a unique suffix.
// The provided value has the same validation rules as the Name field,
// and may be truncated by the length of the suffix required to make the value
// unique on the server.
//
// If this field is specified and the generated name exists, the server will
// NOT return a 409 - instead, it will either return 201 Created or 500 with Reason
// ServerTimeout indicating a unique name could not be found in the time allotted, and the client
// should retry (optionally after the time indicated in the Retry-After header).
//
// Applied only if Name is not specified.
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#idempotency
// +optional
GenerateName string `json:"generateName,omitempty" protobuf:"bytes,2,opt,name=generateName"`
// Namespace defines the space within each name must be unique. An empty namespace is
// equivalent to the "default" namespace, but "default" is the canonical representation.
// Not all objects are required to be scoped to a namespace - the value of this field for
// those objects will be empty.
//
// Must be a DNS_LABEL.
// Cannot be updated.
// More info: http://kubernetes.io/docs/user-guide/namespaces
// +optional
Namespace string `json:"namespace,omitempty" protobuf:"bytes,3,opt,name=namespace"`
// SelfLink is a URL representing this object.
// Populated by the system.
// Read-only.
// +optional
SelfLink string `json:"selfLink,omitempty" protobuf:"bytes,4,opt,name=selfLink"`
// UID is the unique in time and space value for this object. It is typically generated by
// the server on successful creation of a resource and is not allowed to change on PUT
// operations.
//
// Populated by the system.
// Read-only.
// More info: http://kubernetes.io/docs/user-guide/identifiers#uids
// +optional
UID types.UID `json:"uid,omitempty" protobuf:"bytes,5,opt,name=uid,casttype=k8s.io/kubernetes/pkg/types.UID"`
// An opaque value that represents the internal version of this object that can
// be used by clients to determine when objects have changed. May be used for optimistic
// concurrency, change detection, and the watch operation on a resource or set of resources.
// Clients must treat these values as opaque and passed unmodified back to the server.
// They may only be valid for a particular resource or set of resources.
//
// Populated by the system.
// Read-only.
// Value must be treated as opaque by clients and .
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#concurrency-control-and-consistency
// +optional
ResourceVersion string `json:"resourceVersion,omitempty" protobuf:"bytes,6,opt,name=resourceVersion"`
// A sequence number representing a specific generation of the desired state.
// Populated by the system. Read-only.
// +optional
Generation int64 `json:"generation,omitempty" protobuf:"varint,7,opt,name=generation"`
// CreationTimestamp is a timestamp representing the server time when this object was
// created. It is not guaranteed to be set in happens-before order across separate operations.
// Clients may not set this value. It is represented in RFC3339 form and is in UTC.
//
// Populated by the system.
// Read-only.
// Null for lists.
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata
// +optional
CreationTimestamp Time `json:"creationTimestamp,omitempty" protobuf:"bytes,8,opt,name=creationTimestamp"`
// DeletionTimestamp is RFC 3339 date and time at which this resource will be deleted. This
// field is set by the server when a graceful deletion is requested by the user, and is not
// directly settable by a client. The resource is expected to be deleted (no longer visible
// from resource lists, and not reachable by name) after the time in this field. Once set,
// this value may not be unset or be set further into the future, although it may be shortened
// or the resource may be deleted prior to this time. For example, a user may request that
// a pod is deleted in 30 seconds. The Kubelet will react by sending a graceful termination
// signal to the containers in the pod. After that 30 seconds, the Kubelet will send a hard
// termination signal (SIGKILL) to the container and after cleanup, remove the pod from the
// API. In the presence of network partitions, this object may still exist after this
// timestamp, until an administrator or automated process can determine the resource is
// fully terminated.
// If not set, graceful deletion of the object has not been requested.
//
// Populated by the system when a graceful deletion is requested.
// Read-only.
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata
// +optional
DeletionTimestamp *Time `json:"deletionTimestamp,omitempty" protobuf:"bytes,9,opt,name=deletionTimestamp"`
// Number of seconds allowed for this object to gracefully terminate before
// it will be removed from the system. Only set when deletionTimestamp is also set.
// May only be shortened.
// Read-only.
// +optional
DeletionGracePeriodSeconds *int64 `json:"deletionGracePeriodSeconds,omitempty" protobuf:"varint,10,opt,name=deletionGracePeriodSeconds"`
// Map of string keys and values that can be used to organize and categorize
// (scope and select) objects. May match selectors of replication controllers
// and services.
// More info: http://kubernetes.io/docs/user-guide/labels
// +optional
Labels map[string]string `json:"labels,omitempty" protobuf:"bytes,11,rep,name=labels"`
// Annotations is an unstructured key value map stored with a resource that may be
// set by external tools to store and retrieve arbitrary metadata. They are not
// queryable and should be preserved when modifying objects.
// More info: http://kubernetes.io/docs/user-guide/annotations
// +optional
Annotations map[string]string `json:"annotations,omitempty" protobuf:"bytes,12,rep,name=annotations"`
// List of objects depended by this object. If ALL objects in the list have
// been deleted, this object will be garbage collected. If this object is managed by a controller,
// then an entry in this list will point to this controller, with the controller field set to true.
// There cannot be more than one managing controller.
// +optional
OwnerReferences []OwnerReference `json:"ownerReferences,omitempty" patchStrategy:"merge" patchMergeKey:"uid" protobuf:"bytes,13,rep,name=ownerReferences"`
// Must be empty before the object is deleted from the registry. Each entry
// is an identifier for the responsible component that will remove the entry
// from the list. If the deletionTimestamp of the object is non-nil, entries
// in this list can only be removed.
// +optional
Finalizers []string `json:"finalizers,omitempty" patchStrategy:"merge" protobuf:"bytes,14,rep,name=finalizers"`
// The name of the cluster which the object belongs to.
// This is used to distinguish resources with same name and namespace in different clusters.
// This field is not set anywhere right now and apiserver is going to ignore it if set in create or update request.
// +optional
ClusterName string `json:"clusterName,omitempty" protobuf:"bytes,15,opt,name=clusterName"`
}
const (
// NamespaceDefault means the object is in the default namespace which is applied when not specified by clients
NamespaceDefault string = "default"
// NamespaceAll is the default argument to specify on a context when you want to list or filter resources across all namespaces
NamespaceAll string = ""
// NamespaceNone is the argument for a context when there is no namespace.
NamespaceNone string = ""
// NamespaceSystem is the system namespace where we place system components.
NamespaceSystem string = "kube-system"
)
// OwnerReference contains enough information to let you identify an owning
// object. Currently, an owning object must be in the same namespace, so there
// is no namespace field.
type OwnerReference struct {
// API version of the referent.
APIVersion string `json:"apiVersion" protobuf:"bytes,5,opt,name=apiVersion"`
// Kind of the referent.
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds
Kind string `json:"kind" protobuf:"bytes,1,opt,name=kind"`
// Name of the referent.
// More info: http://kubernetes.io/docs/user-guide/identifiers#names
Name string `json:"name" protobuf:"bytes,3,opt,name=name"`
// UID of the referent.
// More info: http://kubernetes.io/docs/user-guide/identifiers#uids
UID types.UID `json:"uid" protobuf:"bytes,4,opt,name=uid,casttype=k8s.io/apimachinery/pkg/types.UID"`
// If true, this reference points to the managing controller.
// +optional
Controller *bool `json:"controller,omitempty" protobuf:"varint,6,opt,name=controller"`
}
// ListOptions is the query options to a standard REST list call.
type ListOptions struct {
TypeMeta `json:",inline"`
// A selector to restrict the list of returned objects by their labels.
// Defaults to everything.
// +optional
LabelSelector string `json:"labelSelector,omitempty" protobuf:"bytes,1,opt,name=labelSelector"`
// A selector to restrict the list of returned objects by their fields.
// Defaults to everything.
// +optional
FieldSelector string `json:"fieldSelector,omitempty" protobuf:"bytes,2,opt,name=fieldSelector"`
// Watch for changes to the described resources and return them as a stream of
// add, update, and remove notifications. Specify resourceVersion.
// +optional
Watch bool `json:"watch,omitempty" protobuf:"varint,3,opt,name=watch"`
// When specified with a watch call, shows changes that occur after that particular version of a resource.
// Defaults to changes from the beginning of history.
// When specified for list:
// - if unset, then the result is returned from remote storage based on quorum-read flag;
// - if it's 0, then we simply return what we currently have in cache, no guarantee;
// - if set to non zero, then the result is at least as fresh as given rv.
// +optional
ResourceVersion string `json:"resourceVersion,omitempty" protobuf:"bytes,4,opt,name=resourceVersion"`
// Timeout for the list/watch call.
// +optional
TimeoutSeconds *int64 `json:"timeoutSeconds,omitempty" protobuf:"varint,5,opt,name=timeoutSeconds"`
}
// ExportOptions is the query options to the standard REST get call.
type ExportOptions struct {
TypeMeta `json:",inline"`
// Should this value be exported. Export strips fields that a user can not specify.
Export bool `json:"export" protobuf:"varint,1,opt,name=export"`
// Should the export be exact. Exact export maintains cluster-specific fields like 'Namespace'.
Exact bool `json:"exact" protobuf:"varint,2,opt,name=exact"`
}
// GetOptions is the standard query options to the standard REST get call.
type GetOptions struct {
TypeMeta `json:",inline"`
// When specified:
// - if unset, then the result is returned from remote storage based on quorum-read flag;
// - if it's 0, then we simply return what we currently have in cache, no guarantee;
// - if set to non zero, then the result is at least as fresh as given rv.
ResourceVersion string `json:"resourceVersion,omitempty" protobuf:"bytes,1,opt,name=resourceVersion"`
}
// DeleteOptions may be provided when deleting an API object.
type DeleteOptions struct {
TypeMeta `json:",inline"`
// The duration in seconds before the object should be deleted. Value must be non-negative integer.
// The value zero indicates delete immediately. If this value is nil, the default grace period for the
// specified type will be used.
// Defaults to a per object value if not specified. zero means delete immediately.
// +optional
GracePeriodSeconds *int64 `json:"gracePeriodSeconds,omitempty" protobuf:"varint,1,opt,name=gracePeriodSeconds"`
// Must be fulfilled before a deletion is carried out. If not possible, a 409 Conflict status will be
// returned.
// +optional
Preconditions *Preconditions `json:"preconditions,omitempty" protobuf:"bytes,2,opt,name=preconditions"`
// Should the dependent objects be orphaned. If true/false, the "orphan"
// finalizer will be added to/removed from the object's finalizers list.
// +optional
OrphanDependents *bool `json:"orphanDependents,omitempty" protobuf:"varint,3,opt,name=orphanDependents"`
}
// Preconditions must be fulfilled before an operation (update, delete, etc.) is carried out.
type Preconditions struct {
// Specifies the target UID.
// +optional
UID *types.UID `json:"uid,omitempty" protobuf:"bytes,1,opt,name=uid,casttype=k8s.io/apimachinery/pkg/types.UID"`
}
// Status is a return value for calls that don't return other objects.
type Status struct {
TypeMeta `json:",inline"`
// Standard list metadata.
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds
// +optional
ListMeta `json:"metadata,omitempty" protobuf:"bytes,1,opt,name=metadata"`
// Status of the operation.
// One of: "Success" or "Failure".
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status
// +optional
Status string `json:"status,omitempty" protobuf:"bytes,2,opt,name=status"`
// A human-readable description of the status of this operation.
// +optional
Message string `json:"message,omitempty" protobuf:"bytes,3,opt,name=message"`
// A machine-readable description of why this operation is in the
// "Failure" status. If this value is empty there
// is no information available. A Reason clarifies an HTTP status
// code but does not override it.
// +optional
Reason StatusReason `json:"reason,omitempty" protobuf:"bytes,4,opt,name=reason,casttype=StatusReason"`
// Extended data associated with the reason. Each reason may define its
// own extended details. This field is optional and the data returned
// is not guaranteed to conform to any schema except that defined by
// the reason type.
// +optional
Details *StatusDetails `json:"details,omitempty" protobuf:"bytes,5,opt,name=details"`
// Suggested HTTP return code for this status, 0 if not set.
// +optional
Code int32 `json:"code,omitempty" protobuf:"varint,6,opt,name=code"`
}
// StatusDetails is a set of additional properties that MAY be set by the
// server to provide additional information about a response. The Reason
// field of a Status object defines what attributes will be set. Clients
// must ignore fields that do not match the defined type of each attribute,
// and should assume that any attribute may be empty, invalid, or under
// defined.
type StatusDetails struct {
// The name attribute of the resource associated with the status StatusReason
// (when there is a single name which can be described).
// +optional
Name string `json:"name,omitempty" protobuf:"bytes,1,opt,name=name"`
// The group attribute of the resource associated with the status StatusReason.
// +optional
Group string `json:"group,omitempty" protobuf:"bytes,2,opt,name=group"`
// The kind attribute of the resource associated with the status StatusReason.
// On some operations may differ from the requested resource Kind.
// More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds
// +optional
Kind string `json:"kind,omitempty" protobuf:"bytes,3,opt,name=kind"`
// The Causes array includes more details associated with the StatusReason
// failure. Not all StatusReasons may provide detailed causes.
// +optional
Causes []StatusCause `json:"causes,omitempty" protobuf:"bytes,4,rep,name=causes"`
// If specified, the time in seconds before the operation should be retried.
// +optional
RetryAfterSeconds int32 `json:"retryAfterSeconds,omitempty" protobuf:"varint,5,opt,name=retryAfterSeconds"`
}
// Values of Status.Status
const (
StatusSuccess = "Success"
StatusFailure = "Failure"
)
// StatusReason is an enumeration of possible failure causes. Each StatusReason
// must map to a single HTTP status code, but multiple reasons may map
// to the same HTTP status code.
// TODO: move to apiserver
type StatusReason string
const (
// StatusReasonUnknown means the server has declined to indicate a specific reason.
// The details field may contain other information about this error.
// Status code 500.
StatusReasonUnknown StatusReason = ""
// StatusReasonUnauthorized means the server can be reached and understood the request, but requires
// the user to present appropriate authorization credentials (identified by the WWW-Authenticate header)
// in order for the action to be completed. If the user has specified credentials on the request, the
// server considers them insufficient.
// Status code 401
StatusReasonUnauthorized StatusReason = "Unauthorized"
// StatusReasonForbidden means the server can be reached and understood the request, but refuses
// to take any further action. It is the result of the server being configured to deny access for some reason
// to the requested resource by the client.
// Details (optional):
// "kind" string - the kind attribute of the forbidden resource
// on some operations may differ from the requested
// resource.
// "id" string - the identifier of the forbidden resource
// Status code 403
StatusReasonForbidden StatusReason = "Forbidden"
// StatusReasonNotFound means one or more resources required for this operation
// could not be found.
// Details (optional):
// "kind" string - the kind attribute of the missing resource
// on some operations may differ from the requested
// resource.
// "id" string - the identifier of the missing resource
// Status code 404
StatusReasonNotFound StatusReason = "NotFound"
// StatusReasonAlreadyExists means the resource you are creating already exists.
// Details (optional):
// "kind" string - the kind attribute of the conflicting resource
// "id" string - the identifier of the conflicting resource
// Status code 409
StatusReasonAlreadyExists StatusReason = "AlreadyExists"
// StatusReasonConflict means the requested operation cannot be completed
// due to a conflict in the operation. The client may need to alter the
// request. Each resource may define custom details that indicate the
// nature of the conflict.
// Status code 409
StatusReasonConflict StatusReason = "Conflict"
// StatusReasonGone means the item is no longer available at the server and no
// forwarding address is known.
// Status code 410
StatusReasonGone StatusReason = "Gone"
// StatusReasonInvalid means the requested create or update operation cannot be
// completed due to invalid data provided as part of the request. The client may
// need to alter the request. When set, the client may use the StatusDetails
// message field as a summary of the issues encountered.
// Details (optional):
// "kind" string - the kind attribute of the invalid resource
// "id" string - the identifier of the invalid resource
// "causes" - one or more StatusCause entries indicating the data in the
// provided resource that was invalid. The code, message, and
// field attributes will be set.
// Status code 422
StatusReasonInvalid StatusReason = "Invalid"
// StatusReasonServerTimeout means the server can be reached and understood the request,
// but cannot complete the action in a reasonable time. The client should retry the request.
// This is may be due to temporary server load or a transient communication issue with
// another server. Status code 500 is used because the HTTP spec provides no suitable
// server-requested client retry and the 5xx class represents actionable errors.
// Details (optional):
// "kind" string - the kind attribute of the resource being acted on.
// "id" string - the operation that is being attempted.
// "retryAfterSeconds" int32 - the number of seconds before the operation should be retried
// Status code 500
StatusReasonServerTimeout StatusReason = "ServerTimeout"
// StatusReasonTimeout means that the request could not be completed within the given time.
// Clients can get this response only when they specified a timeout param in the request,
// or if the server cannot complete the operation within a reasonable amount of time.
// The request might succeed with an increased value of timeout param. The client *should*
// wait at least the number of seconds specified by the retryAfterSeconds field.
// Details (optional):
// "retryAfterSeconds" int32 - the number of seconds before the operation should be retried
// Status code 504
StatusReasonTimeout StatusReason = "Timeout"
// StatusReasonBadRequest means that the request itself was invalid, because the request
// doesn't make any sense, for example deleting a read-only object. This is different than
// StatusReasonInvalid above which indicates that the API call could possibly succeed, but the
// data was invalid. API calls that return BadRequest can never succeed.
StatusReasonBadRequest StatusReason = "BadRequest"
// StatusReasonMethodNotAllowed means that the action the client attempted to perform on the
// resource was not supported by the code - for instance, attempting to delete a resource that
// can only be created. API calls that return MethodNotAllowed can never succeed.
StatusReasonMethodNotAllowed StatusReason = "MethodNotAllowed"
// StatusReasonInternalError indicates that an internal error occurred, it is unexpected
// and the outcome of the call is unknown.
// Details (optional):
// "causes" - The original error
// Status code 500
StatusReasonInternalError StatusReason = "InternalError"
// StatusReasonExpired indicates that the request is invalid because the content you are requesting
// has expired and is no longer available. It is typically associated with watches that can't be
// serviced.
// Status code 410 (gone)
StatusReasonExpired StatusReason = "Expired"
// StatusReasonServiceUnavailable means that the request itself was valid,
// but the requested service is unavailable at this time.
// Retrying the request after some time might succeed.
// Status code 503
StatusReasonServiceUnavailable StatusReason = "ServiceUnavailable"
)
// StatusCause provides more information about an api.Status failure, including
// cases when multiple errors are encountered.
type StatusCause struct {
// A machine-readable description of the cause of the error. If this value is
// empty there is no information available.
// +optional
Type CauseType `json:"reason,omitempty" protobuf:"bytes,1,opt,name=reason,casttype=CauseType"`
// A human-readable description of the cause of the error. This field may be
// presented as-is to a reader.
// +optional
Message string `json:"message,omitempty" protobuf:"bytes,2,opt,name=message"`
// The field of the resource that has caused this error, as named by its JSON
// serialization. May include dot and postfix notation for nested attributes.
// Arrays are zero-indexed. Fields may appear more than once in an array of
// causes due to fields having multiple errors.
// Optional.
//
// Examples:
// "name" - the field "name" on the current resource
// "items[0].name" - the field "name" on the first array entry in "items"
// +optional
Field string `json:"field,omitempty" protobuf:"bytes,3,opt,name=field"`
}
// CauseType is a machine readable value providing more detail about what
// occurred in a status response. An operation may have multiple causes for a
// status (whether Failure or Success).
type CauseType string
const (
// CauseTypeFieldValueNotFound is used to report failure to find a requested value
// (e.g. looking up an ID).
CauseTypeFieldValueNotFound CauseType = "FieldValueNotFound"
// CauseTypeFieldValueRequired is used to report required values that are not
// provided (e.g. empty strings, null values, or empty arrays).
CauseTypeFieldValueRequired CauseType = "FieldValueRequired"
// CauseTypeFieldValueDuplicate is used to report collisions of values that must be
// unique (e.g. unique IDs).
CauseTypeFieldValueDuplicate CauseType = "FieldValueDuplicate"
// CauseTypeFieldValueInvalid is used to report malformed values (e.g. failed regex
// match).
CauseTypeFieldValueInvalid CauseType = "FieldValueInvalid"
// CauseTypeFieldValueNotSupported is used to report valid (as per formatting rules)
// values that can not be handled (e.g. an enumerated string).
CauseTypeFieldValueNotSupported CauseType = "FieldValueNotSupported"
// CauseTypeUnexpectedServerResponse is used to report when the server responded to the client
// without the expected return type. The presence of this cause indicates the error may be
// due to an intervening proxy or the server software malfunctioning.
CauseTypeUnexpectedServerResponse CauseType = "UnexpectedServerResponse"
)
// APIVersions lists the versions that are available, to allow clients to
// discover the API at /api, which is the root path of the legacy v1 API.
//
// +protobuf.options.(gogoproto.goproto_stringer)=false
type APIVersions struct {
TypeMeta `json:",inline"`
// versions are the api versions that are available.
Versions []string `json:"versions" protobuf:"bytes,1,rep,name=versions"`
// a map of client CIDR to server address that is serving this group.
// This is to help clients reach servers in the most network-efficient way possible.
// Clients can use the appropriate server address as per the CIDR that they match.
// In case of multiple matches, clients should use the longest matching CIDR.
// The server returns only those CIDRs that it thinks that the client can match.
// For example: the master will return an internal IP CIDR only, if the client reaches the server using an internal IP.
// Server looks at X-Forwarded-For header or X-Real-Ip header or request.RemoteAddr (in that order) to get the client IP.
ServerAddressByClientCIDRs []ServerAddressByClientCIDR `json:"serverAddressByClientCIDRs" protobuf:"bytes,2,rep,name=serverAddressByClientCIDRs"`
}
// APIGroupList is a list of APIGroup, to allow clients to discover the API at
// /apis.
type APIGroupList struct {
TypeMeta `json:",inline"`
// groups is a list of APIGroup.
Groups []APIGroup `json:"groups" protobuf:"bytes,1,rep,name=groups"`
}
// APIGroup contains the name, the supported versions, and the preferred version
// of a group.
type APIGroup struct {
TypeMeta `json:",inline"`
// name is the name of the group.
Name string `json:"name" protobuf:"bytes,1,opt,name=name"`
// versions are the versions supported in this group.
Versions []GroupVersionForDiscovery `json:"versions" protobuf:"bytes,2,rep,name=versions"`
// preferredVersion is the version preferred by the API server, which
// probably is the storage version.
// +optional
PreferredVersion GroupVersionForDiscovery `json:"preferredVersion,omitempty" protobuf:"bytes,3,opt,name=preferredVersion"`
// a map of client CIDR to server address that is serving this group.
// This is to help clients reach servers in the most network-efficient way possible.
// Clients can use the appropriate server address as per the CIDR that they match.
// In case of multiple matches, clients should use the longest matching CIDR.
// The server returns only those CIDRs that it thinks that the client can match.
// For example: the master will return an internal IP CIDR only, if the client reaches the server using an internal IP.
// Server looks at X-Forwarded-For header or X-Real-Ip header or request.RemoteAddr (in that order) to get the client IP.
ServerAddressByClientCIDRs []ServerAddressByClientCIDR `json:"serverAddressByClientCIDRs" protobuf:"bytes,4,rep,name=serverAddressByClientCIDRs"`
}
// ServerAddressByClientCIDR helps the client to determine the server address that they should use, depending on the clientCIDR that they match.
type ServerAddressByClientCIDR struct {
// The CIDR with which clients can match their IP to figure out the server address that they should use.
ClientCIDR string `json:"clientCIDR" protobuf:"bytes,1,opt,name=clientCIDR"`
// Address of this server, suitable for a client that matches the above CIDR.
// This can be a hostname, hostname:port, IP or IP:port.
ServerAddress string `json:"serverAddress" protobuf:"bytes,2,opt,name=serverAddress"`
}
// GroupVersion contains the "group/version" and "version" string of a version.
// It is made a struct to keep extensibility.
type GroupVersionForDiscovery struct {
// groupVersion specifies the API group and version in the form "group/version"
GroupVersion string `json:"groupVersion" protobuf:"bytes,1,opt,name=groupVersion"`
// version specifies the version in the form of "version". This is to save
// the clients the trouble of splitting the GroupVersion.
Version string `json:"version" protobuf:"bytes,2,opt,name=version"`
}
// APIResource specifies the name of a resource and whether it is namespaced.
type APIResource struct {
// name is the name of the resource.
Name string `json:"name" protobuf:"bytes,1,opt,name=name"`
// namespaced indicates if a resource is namespaced or not.
Namespaced bool `json:"namespaced" protobuf:"varint,2,opt,name=namespaced"`
// kind is the kind for the resource (e.g. 'Foo' is the kind for a resource 'foo')
Kind string `json:"kind" protobuf:"bytes,3,opt,name=kind"`
// verbs is a list of supported kube verbs (this includes get, list, watch, create,
// update, patch, delete, deletecollection, and proxy)
Verbs Verbs `json:"verbs" protobuf:"bytes,4,opt,name=verbs"`
// shortNames is a list of suggested short names of the resource.
ShortNames []string `json:"shortNames,omitempty" protobuf:"bytes,5,rep,name=shortNames"`
}
// Verbs masks the value so protobuf can generate
//
// +protobuf.nullable=true
// +protobuf.options.(gogoproto.goproto_stringer)=false
type Verbs []string
func (vs Verbs) String() string {
return fmt.Sprintf("%v", []string(vs))
}
// APIResourceList is a list of APIResource, it is used to expose the name of the
// resources supported in a specific group and version, and if the resource
// is namespaced.
type APIResourceList struct {
TypeMeta `json:",inline"`
// groupVersion is the group and version this APIResourceList is for.
GroupVersion string `json:"groupVersion" protobuf:"bytes,1,opt,name=groupVersion"`
// resources contains the name of the resources and if they are namespaced.
APIResources []APIResource `json:"resources" protobuf:"bytes,2,rep,name=resources"`
}
// RootPaths lists the paths available at root.
// For example: "/healthz", "/apis".
type RootPaths struct {
// paths are the paths available at root.
Paths []string `json:"paths" protobuf:"bytes,1,rep,name=paths"`
}
// TODO: remove me when watch is refactored
func LabelSelectorQueryParam(version string) string {
return "labelSelector"
}
// TODO: remove me when watch is refactored
func FieldSelectorQueryParam(version string) string {
return "fieldSelector"
}
// String returns available api versions as a human-friendly version string.
func (apiVersions APIVersions) String() string {
return strings.Join(apiVersions.Versions, ",")
}
func (apiVersions APIVersions) GoString() string {
return apiVersions.String()
}
// Patch is provided to give a concrete name and type to the Kubernetes PATCH request body.
type Patch struct{}
// Note:
// There are two different styles of label selectors used in versioned types:
// an older style which is represented as just a string in versioned types, and a
// newer style that is structured. LabelSelector is an internal representation for the
// latter style.
// A label selector is a label query over a set of resources. The result of matchLabels and
// matchExpressions are ANDed. An empty label selector matches all objects. A null
// label selector matches no objects.
type LabelSelector struct {
// matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels
// map is equivalent to an element of matchExpressions, whose key field is "key", the
// operator is "In", and the values array contains only "value". The requirements are ANDed.
// +optional
MatchLabels map[string]string `json:"matchLabels,omitempty" protobuf:"bytes,1,rep,name=matchLabels"`
// matchExpressions is a list of label selector requirements. The requirements are ANDed.
// +optional
MatchExpressions []LabelSelectorRequirement `json:"matchExpressions,omitempty" protobuf:"bytes,2,rep,name=matchExpressions"`
}
// A label selector requirement is a selector that contains values, a key, and an operator that
// relates the key and values.
type LabelSelectorRequirement struct {
// key is the label key that the selector applies to.
Key string `json:"key" patchStrategy:"merge" patchMergeKey:"key" protobuf:"bytes,1,opt,name=key"`
// operator represents a key's relationship to a set of values.
// Valid operators ard In, NotIn, Exists and DoesNotExist.
Operator LabelSelectorOperator `json:"operator" protobuf:"bytes,2,opt,name=operator,casttype=LabelSelectorOperator"`
// values is an array of string values. If the operator is In or NotIn,
// the values array must be non-empty. If the operator is Exists or DoesNotExist,
// the values array must be empty. This array is replaced during a strategic
// merge patch.
// +optional
Values []string `json:"values,omitempty" protobuf:"bytes,3,rep,name=values"`
}
// A label selector operator is the set of operators that can be used in a selector requirement.
type LabelSelectorOperator string
const (
LabelSelectorOpIn LabelSelectorOperator = "In"
LabelSelectorOpNotIn LabelSelectorOperator = "NotIn"
LabelSelectorOpExists LabelSelectorOperator = "Exists"
LabelSelectorOpDoesNotExist LabelSelectorOperator = "DoesNotExist"
)

View file

@ -0,0 +1,288 @@
/*
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 v1
// This file contains a collection of methods that can be used from go-restful to
// generate Swagger API documentation for its models. Please read this PR for more
// information on the implementation: https://github.com/emicklei/go-restful/pull/215
//
// TODOs are ignored from the parser (e.g. TODO(andronat):... || TODO:...) if and only if
// they are on one line! For multiple line or blocks that you want to ignore use ---.
// Any context after a --- is ignored.
//
// Those methods can be generated by using hack/update-generated-swagger-docs.sh
// AUTO-GENERATED FUNCTIONS START HERE
var map_APIGroup = map[string]string{
"": "APIGroup contains the name, the supported versions, and the preferred version of a group.",
"name": "name is the name of the group.",
"versions": "versions are the versions supported in this group.",
"preferredVersion": "preferredVersion is the version preferred by the API server, which probably is the storage version.",
"serverAddressByClientCIDRs": "a map of client CIDR to server address that is serving this group. This is to help clients reach servers in the most network-efficient way possible. Clients can use the appropriate server address as per the CIDR that they match. In case of multiple matches, clients should use the longest matching CIDR. The server returns only those CIDRs that it thinks that the client can match. For example: the master will return an internal IP CIDR only, if the client reaches the server using an internal IP. Server looks at X-Forwarded-For header or X-Real-Ip header or request.RemoteAddr (in that order) to get the client IP.",
}
func (APIGroup) SwaggerDoc() map[string]string {
return map_APIGroup
}
var map_APIGroupList = map[string]string{
"": "APIGroupList is a list of APIGroup, to allow clients to discover the API at /apis.",
"groups": "groups is a list of APIGroup.",
}
func (APIGroupList) SwaggerDoc() map[string]string {
return map_APIGroupList
}
var map_APIResource = map[string]string{
"": "APIResource specifies the name of a resource and whether it is namespaced.",
"name": "name is the name of the resource.",
"namespaced": "namespaced indicates if a resource is namespaced or not.",
"kind": "kind is the kind for the resource (e.g. 'Foo' is the kind for a resource 'foo')",
"verbs": "verbs is a list of supported kube verbs (this includes get, list, watch, create, update, patch, delete, deletecollection, and proxy)",
"shortNames": "shortNames is a list of suggested short names of the resource.",
}
func (APIResource) SwaggerDoc() map[string]string {
return map_APIResource
}
var map_APIResourceList = map[string]string{
"": "APIResourceList is a list of APIResource, it is used to expose the name of the resources supported in a specific group and version, and if the resource is namespaced.",
"groupVersion": "groupVersion is the group and version this APIResourceList is for.",
"resources": "resources contains the name of the resources and if they are namespaced.",
}
func (APIResourceList) SwaggerDoc() map[string]string {
return map_APIResourceList
}
var map_APIVersions = map[string]string{
"": "APIVersions lists the versions that are available, to allow clients to discover the API at /api, which is the root path of the legacy v1 API.",
"versions": "versions are the api versions that are available.",
"serverAddressByClientCIDRs": "a map of client CIDR to server address that is serving this group. This is to help clients reach servers in the most network-efficient way possible. Clients can use the appropriate server address as per the CIDR that they match. In case of multiple matches, clients should use the longest matching CIDR. The server returns only those CIDRs that it thinks that the client can match. For example: the master will return an internal IP CIDR only, if the client reaches the server using an internal IP. Server looks at X-Forwarded-For header or X-Real-Ip header or request.RemoteAddr (in that order) to get the client IP.",
}
func (APIVersions) SwaggerDoc() map[string]string {
return map_APIVersions
}
var map_DeleteOptions = map[string]string{
"": "DeleteOptions may be provided when deleting an API object.",
"gracePeriodSeconds": "The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately.",
"preconditions": "Must be fulfilled before a deletion is carried out. If not possible, a 409 Conflict status will be returned.",
"orphanDependents": "Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list.",
}
func (DeleteOptions) SwaggerDoc() map[string]string {
return map_DeleteOptions
}
var map_ExportOptions = map[string]string{
"": "ExportOptions is the query options to the standard REST get call.",
"export": "Should this value be exported. Export strips fields that a user can not specify.",
"exact": "Should the export be exact. Exact export maintains cluster-specific fields like 'Namespace'.",
}
func (ExportOptions) SwaggerDoc() map[string]string {
return map_ExportOptions
}
var map_GetOptions = map[string]string{
"": "GetOptions is the standard query options to the standard REST get call.",
"resourceVersion": "When specified: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it's 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.",
}
func (GetOptions) SwaggerDoc() map[string]string {
return map_GetOptions
}
var map_GroupVersionForDiscovery = map[string]string{
"": "GroupVersion contains the \"group/version\" and \"version\" string of a version. It is made a struct to keep extensibility.",
"groupVersion": "groupVersion specifies the API group and version in the form \"group/version\"",
"version": "version specifies the version in the form of \"version\". This is to save the clients the trouble of splitting the GroupVersion.",
}
func (GroupVersionForDiscovery) SwaggerDoc() map[string]string {
return map_GroupVersionForDiscovery
}
var map_LabelSelector = map[string]string{
"": "A label selector is a label query over a set of resources. The result of matchLabels and matchExpressions are ANDed. An empty label selector matches all objects. A null label selector matches no objects.",
"matchLabels": "matchLabels is a map of {key,value} pairs. A single {key,value} in the matchLabels map is equivalent to an element of matchExpressions, whose key field is \"key\", the operator is \"In\", and the values array contains only \"value\". The requirements are ANDed.",
"matchExpressions": "matchExpressions is a list of label selector requirements. The requirements are ANDed.",
}
func (LabelSelector) SwaggerDoc() map[string]string {
return map_LabelSelector
}
var map_LabelSelectorRequirement = map[string]string{
"": "A label selector requirement is a selector that contains values, a key, and an operator that relates the key and values.",
"key": "key is the label key that the selector applies to.",
"operator": "operator represents a key's relationship to a set of values. Valid operators ard In, NotIn, Exists and DoesNotExist.",
"values": "values is an array of string values. If the operator is In or NotIn, the values array must be non-empty. If the operator is Exists or DoesNotExist, the values array must be empty. This array is replaced during a strategic merge patch.",
}
func (LabelSelectorRequirement) SwaggerDoc() map[string]string {
return map_LabelSelectorRequirement
}
var map_ListMeta = map[string]string{
"": "ListMeta describes metadata that synthetic resources must have, including lists and various status objects. A resource may have only one of {ObjectMeta, ListMeta}.",
"selfLink": "SelfLink is a URL representing this object. Populated by the system. Read-only.",
"resourceVersion": "String that identifies the server's internal version of this object that can be used by clients to determine when objects have changed. Value must be treated as opaque by clients and passed unmodified back to the server. Populated by the system. Read-only. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#concurrency-control-and-consistency",
}
func (ListMeta) SwaggerDoc() map[string]string {
return map_ListMeta
}
var map_ListOptions = map[string]string{
"": "ListOptions is the query options to a standard REST list call.",
"labelSelector": "A selector to restrict the list of returned objects by their labels. Defaults to everything.",
"fieldSelector": "A selector to restrict the list of returned objects by their fields. Defaults to everything.",
"watch": "Watch for changes to the described resources and return them as a stream of add, update, and remove notifications. Specify resourceVersion.",
"resourceVersion": "When specified with a watch call, shows changes that occur after that particular version of a resource. Defaults to changes from the beginning of history. When specified for list: - if unset, then the result is returned from remote storage based on quorum-read flag; - if it's 0, then we simply return what we currently have in cache, no guarantee; - if set to non zero, then the result is at least as fresh as given rv.",
"timeoutSeconds": "Timeout for the list/watch call.",
}
func (ListOptions) SwaggerDoc() map[string]string {
return map_ListOptions
}
var map_ObjectMeta = map[string]string{
"": "ObjectMeta is metadata that all persisted resources must have, which includes all objects users must create.",
"name": "Name must be unique within a namespace. Is required when creating resources, although some resources may allow a client to request the generation of an appropriate name automatically. Name is primarily intended for creation idempotence and configuration definition. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/identifiers#names",
"generateName": "GenerateName is an optional prefix, used by the server, to generate a unique name ONLY IF the Name field has not been provided. If this field is used, the name returned to the client will be different than the name passed. This value will also be combined with a unique suffix. The provided value has the same validation rules as the Name field, and may be truncated by the length of the suffix required to make the value unique on the server.\n\nIf this field is specified and the generated name exists, the server will NOT return a 409 - instead, it will either return 201 Created or 500 with Reason ServerTimeout indicating a unique name could not be found in the time allotted, and the client should retry (optionally after the time indicated in the Retry-After header).\n\nApplied only if Name is not specified. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#idempotency",
"namespace": "Namespace defines the space within each name must be unique. An empty namespace is equivalent to the \"default\" namespace, but \"default\" is the canonical representation. Not all objects are required to be scoped to a namespace - the value of this field for those objects will be empty.\n\nMust be a DNS_LABEL. Cannot be updated. More info: http://kubernetes.io/docs/user-guide/namespaces",
"selfLink": "SelfLink is a URL representing this object. Populated by the system. Read-only.",
"uid": "UID is the unique in time and space value for this object. It is typically generated by the server on successful creation of a resource and is not allowed to change on PUT operations.\n\nPopulated by the system. Read-only. More info: http://kubernetes.io/docs/user-guide/identifiers#uids",
"resourceVersion": "An opaque value that represents the internal version of this object that can be used by clients to determine when objects have changed. May be used for optimistic concurrency, change detection, and the watch operation on a resource or set of resources. Clients must treat these values as opaque and passed unmodified back to the server. They may only be valid for a particular resource or set of resources.\n\nPopulated by the system. Read-only. Value must be treated as opaque by clients and . More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#concurrency-control-and-consistency",
"generation": "A sequence number representing a specific generation of the desired state. Populated by the system. Read-only.",
"creationTimestamp": "CreationTimestamp is a timestamp representing the server time when this object was created. It is not guaranteed to be set in happens-before order across separate operations. Clients may not set this value. It is represented in RFC3339 form and is in UTC.\n\nPopulated by the system. Read-only. Null for lists. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata",
"deletionTimestamp": "DeletionTimestamp is RFC 3339 date and time at which this resource will be deleted. This field is set by the server when a graceful deletion is requested by the user, and is not directly settable by a client. The resource is expected to be deleted (no longer visible from resource lists, and not reachable by name) after the time in this field. Once set, this value may not be unset or be set further into the future, although it may be shortened or the resource may be deleted prior to this time. For example, a user may request that a pod is deleted in 30 seconds. The Kubelet will react by sending a graceful termination signal to the containers in the pod. After that 30 seconds, the Kubelet will send a hard termination signal (SIGKILL) to the container and after cleanup, remove the pod from the API. In the presence of network partitions, this object may still exist after this timestamp, until an administrator or automated process can determine the resource is fully terminated. If not set, graceful deletion of the object has not been requested.\n\nPopulated by the system when a graceful deletion is requested. Read-only. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#metadata",
"deletionGracePeriodSeconds": "Number of seconds allowed for this object to gracefully terminate before it will be removed from the system. Only set when deletionTimestamp is also set. May only be shortened. Read-only.",
"labels": "Map of string keys and values that can be used to organize and categorize (scope and select) objects. May match selectors of replication controllers and services. More info: http://kubernetes.io/docs/user-guide/labels",
"annotations": "Annotations is an unstructured key value map stored with a resource that may be set by external tools to store and retrieve arbitrary metadata. They are not queryable and should be preserved when modifying objects. More info: http://kubernetes.io/docs/user-guide/annotations",
"ownerReferences": "List of objects depended by this object. If ALL objects in the list have been deleted, this object will be garbage collected. If this object is managed by a controller, then an entry in this list will point to this controller, with the controller field set to true. There cannot be more than one managing controller.",
"finalizers": "Must be empty before the object is deleted from the registry. Each entry is an identifier for the responsible component that will remove the entry from the list. If the deletionTimestamp of the object is non-nil, entries in this list can only be removed.",
"clusterName": "The name of the cluster which the object belongs to. This is used to distinguish resources with same name and namespace in different clusters. This field is not set anywhere right now and apiserver is going to ignore it if set in create or update request.",
}
func (ObjectMeta) SwaggerDoc() map[string]string {
return map_ObjectMeta
}
var map_OwnerReference = map[string]string{
"": "OwnerReference contains enough information to let you identify an owning object. Currently, an owning object must be in the same namespace, so there is no namespace field.",
"apiVersion": "API version of the referent.",
"kind": "Kind of the referent. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds",
"name": "Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names",
"uid": "UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids",
"controller": "If true, this reference points to the managing controller.",
}
func (OwnerReference) SwaggerDoc() map[string]string {
return map_OwnerReference
}
var map_Patch = map[string]string{
"": "Patch is provided to give a concrete name and type to the Kubernetes PATCH request body.",
}
func (Patch) SwaggerDoc() map[string]string {
return map_Patch
}
var map_Preconditions = map[string]string{
"": "Preconditions must be fulfilled before an operation (update, delete, etc.) is carried out.",
"uid": "Specifies the target UID.",
}
func (Preconditions) SwaggerDoc() map[string]string {
return map_Preconditions
}
var map_RootPaths = map[string]string{
"": "RootPaths lists the paths available at root. For example: \"/healthz\", \"/apis\".",
"paths": "paths are the paths available at root.",
}
func (RootPaths) SwaggerDoc() map[string]string {
return map_RootPaths
}
var map_ServerAddressByClientCIDR = map[string]string{
"": "ServerAddressByClientCIDR helps the client to determine the server address that they should use, depending on the clientCIDR that they match.",
"clientCIDR": "The CIDR with which clients can match their IP to figure out the server address that they should use.",
"serverAddress": "Address of this server, suitable for a client that matches the above CIDR. This can be a hostname, hostname:port, IP or IP:port.",
}
func (ServerAddressByClientCIDR) SwaggerDoc() map[string]string {
return map_ServerAddressByClientCIDR
}
var map_Status = map[string]string{
"": "Status is a return value for calls that don't return other objects.",
"metadata": "Standard list metadata. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds",
"status": "Status of the operation. One of: \"Success\" or \"Failure\". More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#spec-and-status",
"message": "A human-readable description of the status of this operation.",
"reason": "A machine-readable description of why this operation is in the \"Failure\" status. If this value is empty there is no information available. A Reason clarifies an HTTP status code but does not override it.",
"details": "Extended data associated with the reason. Each reason may define its own extended details. This field is optional and the data returned is not guaranteed to conform to any schema except that defined by the reason type.",
"code": "Suggested HTTP return code for this status, 0 if not set.",
}
func (Status) SwaggerDoc() map[string]string {
return map_Status
}
var map_StatusCause = map[string]string{
"": "StatusCause provides more information about an api.Status failure, including cases when multiple errors are encountered.",
"reason": "A machine-readable description of the cause of the error. If this value is empty there is no information available.",
"message": "A human-readable description of the cause of the error. This field may be presented as-is to a reader.",
"field": "The field of the resource that has caused this error, as named by its JSON serialization. May include dot and postfix notation for nested attributes. Arrays are zero-indexed. Fields may appear more than once in an array of causes due to fields having multiple errors. Optional.\n\nExamples:\n \"name\" - the field \"name\" on the current resource\n \"items[0].name\" - the field \"name\" on the first array entry in \"items\"",
}
func (StatusCause) SwaggerDoc() map[string]string {
return map_StatusCause
}
var map_StatusDetails = map[string]string{
"": "StatusDetails is a set of additional properties that MAY be set by the server to provide additional information about a response. The Reason field of a Status object defines what attributes will be set. Clients must ignore fields that do not match the defined type of each attribute, and should assume that any attribute may be empty, invalid, or under defined.",
"name": "The name attribute of the resource associated with the status StatusReason (when there is a single name which can be described).",
"group": "The group attribute of the resource associated with the status StatusReason.",
"kind": "The kind attribute of the resource associated with the status StatusReason. On some operations may differ from the requested resource Kind. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds",
"causes": "The Causes array includes more details associated with the StatusReason failure. Not all StatusReasons may provide detailed causes.",
"retryAfterSeconds": "If specified, the time in seconds before the operation should be retried.",
}
func (StatusDetails) SwaggerDoc() map[string]string {
return map_StatusDetails
}
var map_TypeMeta = map[string]string{
"": "TypeMeta describes an individual object in an API response or request with strings representing the type of the object and its API schema version. Structures that are versioned or persisted should inline TypeMeta.",
"kind": "Kind is a string value representing the REST resource this object represents. Servers may infer this from the endpoint the client submits requests to. Cannot be updated. In CamelCase. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds",
"apiVersion": "APIVersion defines the versioned schema of this representation of an object. Servers should convert recognized schemas to the latest internal value, and may reject unrecognized values. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#resources",
}
func (TypeMeta) SwaggerDoc() map[string]string {
return map_TypeMeta
}
// AUTO-GENERATED FUNCTIONS END HERE

View file

@ -0,0 +1,134 @@
/*
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 v1
import (
"encoding/json"
"reflect"
"testing"
"github.com/ugorji/go/codec"
)
func TestVerbsUgorjiMarshalJSON(t *testing.T) {
cases := []struct {
input APIResource
result string
}{
{APIResource{}, `{"name":"","namespaced":false,"kind":"","verbs":null}`},
{APIResource{Verbs: Verbs([]string{})}, `{"name":"","namespaced":false,"kind":"","verbs":[]}`},
{APIResource{Verbs: Verbs([]string{"delete"})}, `{"name":"","namespaced":false,"kind":"","verbs":["delete"]}`},
}
for i, c := range cases {
result, err := json.Marshal(&c.input)
if err != nil {
t.Errorf("[%d] Failed to marshal input: '%v': %v", i, c.input, err)
}
if string(result) != c.result {
t.Errorf("[%d] Failed to marshal input: '%v': expected '%v', got '%v'", i, c.input, c.result, string(result))
}
}
}
func TestVerbsUgorjiUnmarshalJSON(t *testing.T) {
cases := []struct {
input string
result APIResource
}{
{`{}`, APIResource{}},
{`{"verbs":null}`, APIResource{}},
{`{"verbs":[]}`, APIResource{Verbs: Verbs([]string{})}},
{`{"verbs":["delete"]}`, APIResource{Verbs: Verbs([]string{"delete"})}},
}
for i, c := range cases {
var result APIResource
if err := codec.NewDecoderBytes([]byte(c.input), new(codec.JsonHandle)).Decode(&result); err != nil {
t.Errorf("[%d] Failed to unmarshal input '%v': %v", i, c.input, err)
}
if !reflect.DeepEqual(result, c.result) {
t.Errorf("[%d] Failed to unmarshal input '%v': expected %+v, got %+v", i, c.input, c.result, result)
}
}
}
// TestUgorjiMarshalJSONWithOmit tests that we don't have regressions regarding nil and empty slices with "omit"
func TestUgorjiMarshalJSONWithOmit(t *testing.T) {
cases := []struct {
input LabelSelector
result string
}{
{LabelSelector{}, `{}`},
{LabelSelector{MatchExpressions: []LabelSelectorRequirement{}}, `{}`},
{LabelSelector{MatchExpressions: []LabelSelectorRequirement{{}}}, `{"matchExpressions":[{"key":"","operator":""}]}`},
}
for i, c := range cases {
result, err := json.Marshal(&c.input)
if err != nil {
t.Errorf("[%d] Failed to marshal input: '%v': %v", i, c.input, err)
}
if string(result) != c.result {
t.Errorf("[%d] Failed to marshal input: '%v': expected '%v', got '%v'", i, c.input, c.result, string(result))
}
}
}
func TestVerbsUnmarshalJSON(t *testing.T) {
cases := []struct {
input string
result APIResource
}{
{`{}`, APIResource{}},
{`{"verbs":null}`, APIResource{}},
{`{"verbs":[]}`, APIResource{Verbs: Verbs([]string{})}},
{`{"verbs":["delete"]}`, APIResource{Verbs: Verbs([]string{"delete"})}},
}
for i, c := range cases {
var result APIResource
if err := json.Unmarshal([]byte(c.input), &result); err != nil {
t.Errorf("[%d] Failed to unmarshal input '%v': %v", i, c.input, err)
}
if !reflect.DeepEqual(result, c.result) {
t.Errorf("[%d] Failed to unmarshal input '%v': expected %+v, got %+v", i, c.input, c.result, result)
}
}
}
func TestVerbsProto(t *testing.T) {
cases := []APIResource{
{},
{Verbs: Verbs([]string{})},
{Verbs: Verbs([]string{"delete"})},
}
for _, input := range cases {
data, err := input.Marshal()
if err != nil {
t.Fatalf("Failed to marshal input: '%v': %v", input, err)
}
resource := APIResource{}
if err := resource.Unmarshal(data); err != nil {
t.Fatalf("Failed to unmarshal output: '%v': %v", input, err)
}
if !reflect.DeepEqual(input, resource) {
t.Errorf("Marshal->Unmarshal is not idempotent: '%v' vs '%v'", input, resource)
}
}
}

View file

@ -0,0 +1,660 @@
/*
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 unstructured
import (
"bytes"
gojson "encoding/json"
"errors"
"fmt"
"io"
"strings"
"github.com/golang/glog"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/json"
)
// Unstructured allows objects that do not have Golang structs registered to be manipulated
// generically. This can be used to deal with the API objects from a plug-in. Unstructured
// objects still have functioning TypeMeta features-- kind, version, etc.
//
// WARNING: This object has accessors for the v1 standard metadata. You *MUST NOT* use this
// type if you are dealing with objects that are not in the server meta v1 schema.
//
// TODO: make the serialization part of this type distinct from the field accessors.
type Unstructured struct {
// Object is a JSON compatible map with string, float, int, bool, []interface{}, or
// map[string]interface{}
// children.
Object map[string]interface{}
}
var _ metav1.Object = &Unstructured{}
var _ runtime.Unstructured = &Unstructured{}
var _ runtime.Unstructured = &UnstructuredList{}
func (obj *Unstructured) GetObjectKind() schema.ObjectKind { return obj }
func (obj *UnstructuredList) GetObjectKind() schema.ObjectKind { return obj }
func (obj *Unstructured) IsUnstructuredObject() {}
func (obj *UnstructuredList) IsUnstructuredObject() {}
func (obj *Unstructured) IsList() bool {
if obj.Object != nil {
_, ok := obj.Object["items"]
return ok
}
return false
}
func (obj *UnstructuredList) IsList() bool { return true }
func (obj *Unstructured) UnstructuredContent() map[string]interface{} {
if obj.Object == nil {
obj.Object = make(map[string]interface{})
}
return obj.Object
}
func (obj *UnstructuredList) UnstructuredContent() map[string]interface{} {
if obj.Object == nil {
obj.Object = make(map[string]interface{})
}
return obj.Object
}
// MarshalJSON ensures that the unstructured object produces proper
// JSON when passed to Go's standard JSON library.
func (u *Unstructured) MarshalJSON() ([]byte, error) {
var buf bytes.Buffer
err := UnstructuredJSONScheme.Encode(u, &buf)
return buf.Bytes(), err
}
// UnmarshalJSON ensures that the unstructured object properly decodes
// JSON when passed to Go's standard JSON library.
func (u *Unstructured) UnmarshalJSON(b []byte) error {
_, _, err := UnstructuredJSONScheme.Decode(b, nil, u)
return err
}
func getNestedField(obj map[string]interface{}, fields ...string) interface{} {
var val interface{} = obj
for _, field := range fields {
if _, ok := val.(map[string]interface{}); !ok {
return nil
}
val = val.(map[string]interface{})[field]
}
return val
}
func getNestedString(obj map[string]interface{}, fields ...string) string {
if str, ok := getNestedField(obj, fields...).(string); ok {
return str
}
return ""
}
func getNestedSlice(obj map[string]interface{}, fields ...string) []string {
if m, ok := getNestedField(obj, fields...).([]interface{}); ok {
strSlice := make([]string, 0, len(m))
for _, v := range m {
if str, ok := v.(string); ok {
strSlice = append(strSlice, str)
}
}
return strSlice
}
return nil
}
func getNestedMap(obj map[string]interface{}, fields ...string) map[string]string {
if m, ok := getNestedField(obj, fields...).(map[string]interface{}); ok {
strMap := make(map[string]string, len(m))
for k, v := range m {
if str, ok := v.(string); ok {
strMap[k] = str
}
}
return strMap
}
return nil
}
func setNestedField(obj map[string]interface{}, value interface{}, fields ...string) {
m := obj
if len(fields) > 1 {
for _, field := range fields[0 : len(fields)-1] {
if _, ok := m[field].(map[string]interface{}); !ok {
m[field] = make(map[string]interface{})
}
m = m[field].(map[string]interface{})
}
}
m[fields[len(fields)-1]] = value
}
func setNestedSlice(obj map[string]interface{}, value []string, fields ...string) {
m := make([]interface{}, 0, len(value))
for _, v := range value {
m = append(m, v)
}
setNestedField(obj, m, fields...)
}
func setNestedMap(obj map[string]interface{}, value map[string]string, fields ...string) {
m := make(map[string]interface{}, len(value))
for k, v := range value {
m[k] = v
}
setNestedField(obj, m, fields...)
}
func (u *Unstructured) setNestedField(value interface{}, fields ...string) {
if u.Object == nil {
u.Object = make(map[string]interface{})
}
setNestedField(u.Object, value, fields...)
}
func (u *Unstructured) setNestedSlice(value []string, fields ...string) {
if u.Object == nil {
u.Object = make(map[string]interface{})
}
setNestedSlice(u.Object, value, fields...)
}
func (u *Unstructured) setNestedMap(value map[string]string, fields ...string) {
if u.Object == nil {
u.Object = make(map[string]interface{})
}
setNestedMap(u.Object, value, fields...)
}
func extractOwnerReference(src interface{}) metav1.OwnerReference {
v := src.(map[string]interface{})
controllerPtr, ok := (getNestedField(v, "controller")).(*bool)
if !ok {
controllerPtr = nil
} else {
if controllerPtr != nil {
controller := *controllerPtr
controllerPtr = &controller
}
}
return metav1.OwnerReference{
Kind: getNestedString(v, "kind"),
Name: getNestedString(v, "name"),
APIVersion: getNestedString(v, "apiVersion"),
UID: (types.UID)(getNestedString(v, "uid")),
Controller: controllerPtr,
}
}
func setOwnerReference(src metav1.OwnerReference) map[string]interface{} {
ret := make(map[string]interface{})
controllerPtr := src.Controller
if controllerPtr != nil {
controller := *controllerPtr
controllerPtr = &controller
}
setNestedField(ret, src.Kind, "kind")
setNestedField(ret, src.Name, "name")
setNestedField(ret, src.APIVersion, "apiVersion")
setNestedField(ret, string(src.UID), "uid")
setNestedField(ret, controllerPtr, "controller")
return ret
}
func getOwnerReferences(object map[string]interface{}) ([]map[string]interface{}, error) {
field := getNestedField(object, "metadata", "ownerReferences")
if field == nil {
return nil, fmt.Errorf("cannot find field metadata.ownerReferences in %v", object)
}
ownerReferences, ok := field.([]map[string]interface{})
if ok {
return ownerReferences, nil
}
// TODO: This is hacky...
interfaces, ok := field.([]interface{})
if !ok {
return nil, fmt.Errorf("expect metadata.ownerReferences to be a slice in %#v", object)
}
ownerReferences = make([]map[string]interface{}, 0, len(interfaces))
for i := 0; i < len(interfaces); i++ {
r, ok := interfaces[i].(map[string]interface{})
if !ok {
return nil, fmt.Errorf("expect element metadata.ownerReferences to be a map[string]interface{} in %#v", object)
}
ownerReferences = append(ownerReferences, r)
}
return ownerReferences, nil
}
func (u *Unstructured) GetOwnerReferences() []metav1.OwnerReference {
original, err := getOwnerReferences(u.Object)
if err != nil {
glog.V(6).Info(err)
return nil
}
ret := make([]metav1.OwnerReference, 0, len(original))
for i := 0; i < len(original); i++ {
ret = append(ret, extractOwnerReference(original[i]))
}
return ret
}
func (u *Unstructured) SetOwnerReferences(references []metav1.OwnerReference) {
var newReferences = make([]map[string]interface{}, 0, len(references))
for i := 0; i < len(references); i++ {
newReferences = append(newReferences, setOwnerReference(references[i]))
}
u.setNestedField(newReferences, "metadata", "ownerReferences")
}
func (u *Unstructured) GetAPIVersion() string {
return getNestedString(u.Object, "apiVersion")
}
func (u *Unstructured) SetAPIVersion(version string) {
u.setNestedField(version, "apiVersion")
}
func (u *Unstructured) GetKind() string {
return getNestedString(u.Object, "kind")
}
func (u *Unstructured) SetKind(kind string) {
u.setNestedField(kind, "kind")
}
func (u *Unstructured) GetNamespace() string {
return getNestedString(u.Object, "metadata", "namespace")
}
func (u *Unstructured) SetNamespace(namespace string) {
u.setNestedField(namespace, "metadata", "namespace")
}
func (u *Unstructured) GetName() string {
return getNestedString(u.Object, "metadata", "name")
}
func (u *Unstructured) SetName(name string) {
u.setNestedField(name, "metadata", "name")
}
func (u *Unstructured) GetGenerateName() string {
return getNestedString(u.Object, "metadata", "generateName")
}
func (u *Unstructured) SetGenerateName(name string) {
u.setNestedField(name, "metadata", "generateName")
}
func (u *Unstructured) GetUID() types.UID {
return types.UID(getNestedString(u.Object, "metadata", "uid"))
}
func (u *Unstructured) SetUID(uid types.UID) {
u.setNestedField(string(uid), "metadata", "uid")
}
func (u *Unstructured) GetResourceVersion() string {
return getNestedString(u.Object, "metadata", "resourceVersion")
}
func (u *Unstructured) SetResourceVersion(version string) {
u.setNestedField(version, "metadata", "resourceVersion")
}
func (u *Unstructured) GetSelfLink() string {
return getNestedString(u.Object, "metadata", "selfLink")
}
func (u *Unstructured) SetSelfLink(selfLink string) {
u.setNestedField(selfLink, "metadata", "selfLink")
}
func (u *Unstructured) GetCreationTimestamp() metav1.Time {
var timestamp metav1.Time
timestamp.UnmarshalQueryParameter(getNestedString(u.Object, "metadata", "creationTimestamp"))
return timestamp
}
func (u *Unstructured) SetCreationTimestamp(timestamp metav1.Time) {
ts, _ := timestamp.MarshalQueryParameter()
u.setNestedField(ts, "metadata", "creationTimestamp")
}
func (u *Unstructured) GetDeletionTimestamp() *metav1.Time {
var timestamp metav1.Time
timestamp.UnmarshalQueryParameter(getNestedString(u.Object, "metadata", "deletionTimestamp"))
if timestamp.IsZero() {
return nil
}
return &timestamp
}
func (u *Unstructured) SetDeletionTimestamp(timestamp *metav1.Time) {
ts, _ := timestamp.MarshalQueryParameter()
u.setNestedField(ts, "metadata", "deletionTimestamp")
}
func (u *Unstructured) GetLabels() map[string]string {
return getNestedMap(u.Object, "metadata", "labels")
}
func (u *Unstructured) SetLabels(labels map[string]string) {
u.setNestedMap(labels, "metadata", "labels")
}
func (u *Unstructured) GetAnnotations() map[string]string {
return getNestedMap(u.Object, "metadata", "annotations")
}
func (u *Unstructured) SetAnnotations(annotations map[string]string) {
u.setNestedMap(annotations, "metadata", "annotations")
}
func (u *Unstructured) SetGroupVersionKind(gvk schema.GroupVersionKind) {
u.SetAPIVersion(gvk.GroupVersion().String())
u.SetKind(gvk.Kind)
}
func (u *Unstructured) GroupVersionKind() schema.GroupVersionKind {
gv, err := schema.ParseGroupVersion(u.GetAPIVersion())
if err != nil {
return schema.GroupVersionKind{}
}
gvk := gv.WithKind(u.GetKind())
return gvk
}
func (u *Unstructured) GetFinalizers() []string {
return getNestedSlice(u.Object, "metadata", "finalizers")
}
func (u *Unstructured) SetFinalizers(finalizers []string) {
u.setNestedSlice(finalizers, "metadata", "finalizers")
}
func (u *Unstructured) GetClusterName() string {
return getNestedString(u.Object, "metadata", "clusterName")
}
func (u *Unstructured) SetClusterName(clusterName string) {
u.setNestedField(clusterName, "metadata", "clusterName")
}
// UnstructuredList allows lists that do not have Golang structs
// registered to be manipulated generically. This can be used to deal
// with the API lists from a plug-in.
type UnstructuredList struct {
Object map[string]interface{}
// Items is a list of unstructured objects.
Items []*Unstructured `json:"items"`
}
// MarshalJSON ensures that the unstructured list object produces proper
// JSON when passed to Go's standard JSON library.
func (u *UnstructuredList) MarshalJSON() ([]byte, error) {
var buf bytes.Buffer
err := UnstructuredJSONScheme.Encode(u, &buf)
return buf.Bytes(), err
}
// UnmarshalJSON ensures that the unstructured list object properly
// decodes JSON when passed to Go's standard JSON library.
func (u *UnstructuredList) UnmarshalJSON(b []byte) error {
_, _, err := UnstructuredJSONScheme.Decode(b, nil, u)
return err
}
func (u *UnstructuredList) setNestedField(value interface{}, fields ...string) {
if u.Object == nil {
u.Object = make(map[string]interface{})
}
setNestedField(u.Object, value, fields...)
}
func (u *UnstructuredList) GetAPIVersion() string {
return getNestedString(u.Object, "apiVersion")
}
func (u *UnstructuredList) SetAPIVersion(version string) {
u.setNestedField(version, "apiVersion")
}
func (u *UnstructuredList) GetKind() string {
return getNestedString(u.Object, "kind")
}
func (u *UnstructuredList) SetKind(kind string) {
u.setNestedField(kind, "kind")
}
func (u *UnstructuredList) GetResourceVersion() string {
return getNestedString(u.Object, "metadata", "resourceVersion")
}
func (u *UnstructuredList) SetResourceVersion(version string) {
u.setNestedField(version, "metadata", "resourceVersion")
}
func (u *UnstructuredList) GetSelfLink() string {
return getNestedString(u.Object, "metadata", "selfLink")
}
func (u *UnstructuredList) SetSelfLink(selfLink string) {
u.setNestedField(selfLink, "metadata", "selfLink")
}
func (u *UnstructuredList) SetGroupVersionKind(gvk schema.GroupVersionKind) {
u.SetAPIVersion(gvk.GroupVersion().String())
u.SetKind(gvk.Kind)
}
func (u *UnstructuredList) GroupVersionKind() schema.GroupVersionKind {
gv, err := schema.ParseGroupVersion(u.GetAPIVersion())
if err != nil {
return schema.GroupVersionKind{}
}
gvk := gv.WithKind(u.GetKind())
return gvk
}
// UnstructuredJSONScheme is capable of converting JSON data into the Unstructured
// type, which can be used for generic access to objects without a predefined scheme.
// TODO: move into serializer/json.
var UnstructuredJSONScheme runtime.Codec = unstructuredJSONScheme{}
type unstructuredJSONScheme struct{}
func (s unstructuredJSONScheme) Decode(data []byte, _ *schema.GroupVersionKind, obj runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) {
var err error
if obj != nil {
err = s.decodeInto(data, obj)
} else {
obj, err = s.decode(data)
}
if err != nil {
return nil, nil, err
}
gvk := obj.GetObjectKind().GroupVersionKind()
if len(gvk.Kind) == 0 {
return nil, &gvk, runtime.NewMissingKindErr(string(data))
}
return obj, &gvk, nil
}
func (unstructuredJSONScheme) Encode(obj runtime.Object, w io.Writer) error {
switch t := obj.(type) {
case *Unstructured:
return json.NewEncoder(w).Encode(t.Object)
case *UnstructuredList:
items := make([]map[string]interface{}, 0, len(t.Items))
for _, i := range t.Items {
items = append(items, i.Object)
}
t.Object["items"] = items
defer func() { delete(t.Object, "items") }()
return json.NewEncoder(w).Encode(t.Object)
case *runtime.Unknown:
// TODO: Unstructured needs to deal with ContentType.
_, err := w.Write(t.Raw)
return err
default:
return json.NewEncoder(w).Encode(t)
}
}
func (s unstructuredJSONScheme) decode(data []byte) (runtime.Object, error) {
type detector struct {
Items gojson.RawMessage
}
var det detector
if err := json.Unmarshal(data, &det); err != nil {
return nil, err
}
if det.Items != nil {
list := &UnstructuredList{}
err := s.decodeToList(data, list)
return list, err
}
// No Items field, so it wasn't a list.
unstruct := &Unstructured{}
err := s.decodeToUnstructured(data, unstruct)
return unstruct, err
}
func (s unstructuredJSONScheme) decodeInto(data []byte, obj runtime.Object) error {
switch x := obj.(type) {
case *Unstructured:
return s.decodeToUnstructured(data, x)
case *UnstructuredList:
return s.decodeToList(data, x)
case *runtime.VersionedObjects:
o, err := s.decode(data)
if err == nil {
x.Objects = []runtime.Object{o}
}
return err
default:
return json.Unmarshal(data, x)
}
}
func (unstructuredJSONScheme) decodeToUnstructured(data []byte, unstruct *Unstructured) error {
m := make(map[string]interface{})
if err := json.Unmarshal(data, &m); err != nil {
return err
}
unstruct.Object = m
return nil
}
func (s unstructuredJSONScheme) decodeToList(data []byte, list *UnstructuredList) error {
type decodeList struct {
Items []gojson.RawMessage
}
var dList decodeList
if err := json.Unmarshal(data, &dList); err != nil {
return err
}
if err := json.Unmarshal(data, &list.Object); err != nil {
return err
}
// For typed lists, e.g., a PodList, API server doesn't set each item's
// APIVersion and Kind. We need to set it.
listAPIVersion := list.GetAPIVersion()
listKind := list.GetKind()
itemKind := strings.TrimSuffix(listKind, "List")
delete(list.Object, "items")
list.Items = nil
for _, i := range dList.Items {
unstruct := &Unstructured{}
if err := s.decodeToUnstructured([]byte(i), unstruct); err != nil {
return err
}
// This is hacky. Set the item's Kind and APIVersion to those inferred
// from the List.
if len(unstruct.GetKind()) == 0 && len(unstruct.GetAPIVersion()) == 0 {
unstruct.SetKind(itemKind)
unstruct.SetAPIVersion(listAPIVersion)
}
list.Items = append(list.Items, unstruct)
}
return nil
}
// UnstructuredObjectConverter is an ObjectConverter for use with
// Unstructured objects. Since it has no schema or type information,
// it will only succeed for no-op conversions. This is provided as a
// sane implementation for APIs that require an object converter.
type UnstructuredObjectConverter struct{}
func (UnstructuredObjectConverter) Convert(in, out, context interface{}) error {
unstructIn, ok := in.(*Unstructured)
if !ok {
return fmt.Errorf("input type %T in not valid for unstructured conversion", in)
}
unstructOut, ok := out.(*Unstructured)
if !ok {
return fmt.Errorf("output type %T in not valid for unstructured conversion", out)
}
// maybe deep copy the map? It is documented in the
// ObjectConverter interface that this function is not
// guaranteeed to not mutate the input. Or maybe set the input
// object to nil.
unstructOut.Object = unstructIn.Object
return nil
}
func (UnstructuredObjectConverter) ConvertToVersion(in runtime.Object, target runtime.GroupVersioner) (runtime.Object, error) {
if kind := in.GetObjectKind().GroupVersionKind(); !kind.Empty() {
gvk, ok := target.KindForGroupVersionKinds([]schema.GroupVersionKind{kind})
if !ok {
// TODO: should this be a typed error?
return nil, fmt.Errorf("%v is unstructured and is not suitable for converting to %q", kind, target)
}
in.GetObjectKind().SetGroupVersionKind(gvk)
}
return in, nil
}
func (UnstructuredObjectConverter) ConvertFieldLabel(version, kind, label, value string) (string, string, error) {
return "", "", errors.New("unstructured cannot convert field labels")
}

View file

@ -0,0 +1,74 @@
/*
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 validation
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/validation"
"k8s.io/apimachinery/pkg/util/validation/field"
)
func ValidateLabelSelector(ps *metav1.LabelSelector, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if ps == nil {
return allErrs
}
allErrs = append(allErrs, ValidateLabels(ps.MatchLabels, fldPath.Child("matchLabels"))...)
for i, expr := range ps.MatchExpressions {
allErrs = append(allErrs, ValidateLabelSelectorRequirement(expr, fldPath.Child("matchExpressions").Index(i))...)
}
return allErrs
}
func ValidateLabelSelectorRequirement(sr metav1.LabelSelectorRequirement, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
switch sr.Operator {
case metav1.LabelSelectorOpIn, metav1.LabelSelectorOpNotIn:
if len(sr.Values) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("values"), "must be specified when `operator` is 'In' or 'NotIn'"))
}
case metav1.LabelSelectorOpExists, metav1.LabelSelectorOpDoesNotExist:
if len(sr.Values) > 0 {
allErrs = append(allErrs, field.Forbidden(fldPath.Child("values"), "may not be specified when `operator` is 'Exists' or 'DoesNotExist'"))
}
default:
allErrs = append(allErrs, field.Invalid(fldPath.Child("operator"), sr.Operator, "not a valid selector operator"))
}
allErrs = append(allErrs, ValidateLabelName(sr.Key, fldPath.Child("key"))...)
return allErrs
}
// ValidateLabelName validates that the label name is correctly defined.
func ValidateLabelName(labelName string, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
for _, msg := range validation.IsQualifiedName(labelName) {
allErrs = append(allErrs, field.Invalid(fldPath, labelName, msg))
}
return allErrs
}
// ValidateLabels validates that a set of labels are correctly defined.
func ValidateLabels(labels map[string]string, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
for k, v := range labels {
allErrs = append(allErrs, ValidateLabelName(k, fldPath)...)
for _, msg := range validation.IsValidLabelValue(v) {
allErrs = append(allErrs, field.Invalid(fldPath, v, msg))
}
}
return allErrs
}

View file

@ -0,0 +1,94 @@
/*
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 validation
import (
"strings"
"testing"
"k8s.io/apimachinery/pkg/util/validation/field"
)
func TestValidateLabels(t *testing.T) {
successCases := []map[string]string{
{"simple": "bar"},
{"now-with-dashes": "bar"},
{"1-starts-with-num": "bar"},
{"1234": "bar"},
{"simple/simple": "bar"},
{"now-with-dashes/simple": "bar"},
{"now-with-dashes/now-with-dashes": "bar"},
{"now.with.dots/simple": "bar"},
{"now-with.dashes-and.dots/simple": "bar"},
{"1-num.2-num/3-num": "bar"},
{"1234/5678": "bar"},
{"1.2.3.4/5678": "bar"},
{"UpperCaseAreOK123": "bar"},
{"goodvalue": "123_-.BaR"},
}
for i := range successCases {
errs := ValidateLabels(successCases[i], field.NewPath("field"))
if len(errs) != 0 {
t.Errorf("case[%d] expected success, got %#v", i, errs)
}
}
namePartErrMsg := "name part must consist of"
nameErrMsg := "a qualified name must consist of"
labelErrMsg := "a valid label must be an empty string or consist of"
maxLengthErrMsg := "must be no more than"
labelNameErrorCases := []struct {
labels map[string]string
expect string
}{
{map[string]string{"nospecialchars^=@": "bar"}, namePartErrMsg},
{map[string]string{"cantendwithadash-": "bar"}, namePartErrMsg},
{map[string]string{"only/one/slash": "bar"}, nameErrMsg},
{map[string]string{strings.Repeat("a", 254): "bar"}, maxLengthErrMsg},
}
for i := range labelNameErrorCases {
errs := ValidateLabels(labelNameErrorCases[i].labels, field.NewPath("field"))
if len(errs) != 1 {
t.Errorf("case[%d]: expected failure", i)
} else {
if !strings.Contains(errs[0].Detail, labelNameErrorCases[i].expect) {
t.Errorf("case[%d]: error details do not include %q: %q", i, labelNameErrorCases[i].expect, errs[0].Detail)
}
}
}
labelValueErrorCases := []struct {
labels map[string]string
expect string
}{
{map[string]string{"toolongvalue": strings.Repeat("a", 64)}, maxLengthErrMsg},
{map[string]string{"backslashesinvalue": "some\\bad\\value"}, labelErrMsg},
{map[string]string{"nocommasallowed": "bad,value"}, labelErrMsg},
{map[string]string{"strangecharsinvalue": "?#$notsogood"}, labelErrMsg},
}
for i := range labelValueErrorCases {
errs := ValidateLabels(labelValueErrorCases[i].labels, field.NewPath("field"))
if len(errs) != 1 {
t.Errorf("case[%d]: expected failure", i)
} else {
if !strings.Contains(errs[0].Detail, labelValueErrorCases[i].expect) {
t.Errorf("case[%d]: error details do not include %q: %q", i, labelValueErrorCases[i].expect, errs[0].Detail)
}
}
}
}

80
vendor/k8s.io/apimachinery/pkg/apis/meta/v1/watch.go generated vendored Normal file
View file

@ -0,0 +1,80 @@
/*
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 v1
import (
"k8s.io/apimachinery/pkg/conversion"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/watch"
)
// Event represents a single event to a watched resource.
//
// +protobuf=true
type WatchEvent struct {
Type string `json:"type" protobuf:"bytes,1,opt,name=type"`
// Object is:
// * If Type is Added or Modified: the new state of the object.
// * If Type is Deleted: the state of the object immediately before deletion.
// * If Type is Error: *Status is recommended; other types may make sense
// depending on context.
Object runtime.RawExtension `json:"object" protobuf:"bytes,2,opt,name=object"`
}
func Convert_watch_Event_to_versioned_Event(in *watch.Event, out *WatchEvent, s conversion.Scope) error {
out.Type = string(in.Type)
switch t := in.Object.(type) {
case *runtime.Unknown:
// TODO: handle other fields on Unknown and detect type
out.Object.Raw = t.Raw
case nil:
default:
out.Object.Object = in.Object
}
return nil
}
func Convert_versioned_InternalEvent_to_versioned_Event(in *InternalEvent, out *WatchEvent, s conversion.Scope) error {
return Convert_watch_Event_to_versioned_Event((*watch.Event)(in), out, s)
}
func Convert_versioned_Event_to_watch_Event(in *WatchEvent, out *watch.Event, s conversion.Scope) error {
out.Type = watch.EventType(in.Type)
if in.Object.Object != nil {
out.Object = in.Object.Object
} else if in.Object.Raw != nil {
// TODO: handle other fields on Unknown and detect type
out.Object = &runtime.Unknown{
Raw: in.Object.Raw,
ContentType: runtime.ContentTypeJSON,
}
}
return nil
}
func Convert_versioned_Event_to_versioned_InternalEvent(in *WatchEvent, out *InternalEvent, s conversion.Scope) error {
return Convert_versioned_Event_to_watch_Event(in, (*watch.Event)(out), s)
}
// InternalEvent makes watch.Event versioned
// +protobuf=false
type InternalEvent watch.Event
func (e *InternalEvent) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind }
func (e *WatchEvent) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind }

View file

@ -0,0 +1,84 @@
/*
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 v1
const (
// If you add a new topology domain here, also consider adding it to the set of default values
// for the scheduler's --failure-domain command-line argument.
LabelHostname = "kubernetes.io/hostname"
LabelZoneFailureDomain = "failure-domain.beta.kubernetes.io/zone"
LabelZoneRegion = "failure-domain.beta.kubernetes.io/region"
LabelInstanceType = "beta.kubernetes.io/instance-type"
LabelOS = "beta.kubernetes.io/os"
LabelArch = "beta.kubernetes.io/arch"
// Historically fluentd was a manifest pod the was migrated to DaemonSet.
// To avoid situation during cluster upgrade when there are two instances
// of fluentd running on a node, kubelet need to mark node on which
// fluentd in not running as a manifest pod with LabelFluentdDsReady.
LabelFluentdDsReady = "alpha.kubernetes.io/fluentd-ds-ready"
// When the --use-taint-based-evictions flag is enabled,
// TaintNodeNotReady would be automatically added by node controller
// when node is not ready, and removed when node becomes ready.
TaintNodeNotReady = "node.alpha.kubernetes.io/notReady"
// When the --use-taint-based-evictions flag is enabled,
// TaintNodeUnreachable would be automatically added by node controller
// when node becomes unreachable (corresponding to NodeReady status ConditionUnknown)
// and removed when node becomes reachable (NodeReady status ConditionTrue).
TaintNodeUnreachable = "node.alpha.kubernetes.io/unreachable"
)
// Role labels are applied to Nodes to mark their purpose. In particular, we
// usually want to distinguish the master, so that we can isolate privileged
// pods and operations.
//
// Originally we relied on not registering the master, on the fact that the
// master was Unschedulable, and on static manifests for master components.
// But we now do register masters in many environments, are generally moving
// away from static manifests (for better manageability), and working towards
// deprecating the unschedulable field (replacing it with taints & tolerations
// instead).
//
// Even with tainting, a label remains the easiest way of making a positive
// selection, so that pods can schedule only to master nodes for example, and
// thus installations will likely define a label for their master nodes.
//
// So that we can recognize master nodes in consequent places though (such as
// kubectl get nodes), we encourage installations to use the well-known labels.
// We define NodeLabelRole, which is the preferred form, but we will also recognize
// other forms that are known to be in widespread use (NodeLabelKubeadmAlphaRole).
const (
// NodeLabelRole is the preferred label applied to a Node as a hint that it has a particular purpose (defined by the value).
NodeLabelRole = "kubernetes.io/role"
// NodeLabelKubeadmAlphaRole is a label that kubeadm applies to a Node as a hint that it has a particular purpose.
// Use of NodeLabelRole is preferred.
NodeLabelKubeadmAlphaRole = "kubeadm.alpha.kubernetes.io/role"
// NodeLabelRoleMaster is the value of a NodeLabelRole or NodeLabelKubeadmAlphaRole label, indicating a master node.
// A master node typically runs kubernetes system components and will not typically run user workloads.
NodeLabelRoleMaster = "master"
// NodeLabelRoleNode is the value of a NodeLabelRole or NodeLabelKubeadmAlphaRole label, indicating a "normal" node,
// as opposed to a RoleMaster node.
NodeLabelRoleNode = "node"
)

View file

@ -0,0 +1,544 @@
// +build !ignore_autogenerated
/*
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 deepcopy-gen. Do not edit it manually!
package v1
import (
conversion "k8s.io/apimachinery/pkg/conversion"
runtime "k8s.io/apimachinery/pkg/runtime"
types "k8s.io/apimachinery/pkg/types"
reflect "reflect"
)
// GetGeneratedDeepCopyFuncs returns the generated funcs, since we aren't registering them.
func GetGeneratedDeepCopyFuncs() []conversion.GeneratedDeepCopyFunc {
return []conversion.GeneratedDeepCopyFunc{
{Fn: DeepCopy_v1_APIGroup, InType: reflect.TypeOf(&APIGroup{})},
{Fn: DeepCopy_v1_APIGroupList, InType: reflect.TypeOf(&APIGroupList{})},
{Fn: DeepCopy_v1_APIResource, InType: reflect.TypeOf(&APIResource{})},
{Fn: DeepCopy_v1_APIResourceList, InType: reflect.TypeOf(&APIResourceList{})},
{Fn: DeepCopy_v1_APIVersions, InType: reflect.TypeOf(&APIVersions{})},
{Fn: DeepCopy_v1_DeleteOptions, InType: reflect.TypeOf(&DeleteOptions{})},
{Fn: DeepCopy_v1_Duration, InType: reflect.TypeOf(&Duration{})},
{Fn: DeepCopy_v1_ExportOptions, InType: reflect.TypeOf(&ExportOptions{})},
{Fn: DeepCopy_v1_GetOptions, InType: reflect.TypeOf(&GetOptions{})},
{Fn: DeepCopy_v1_GroupKind, InType: reflect.TypeOf(&GroupKind{})},
{Fn: DeepCopy_v1_GroupResource, InType: reflect.TypeOf(&GroupResource{})},
{Fn: DeepCopy_v1_GroupVersion, InType: reflect.TypeOf(&GroupVersion{})},
{Fn: DeepCopy_v1_GroupVersionForDiscovery, InType: reflect.TypeOf(&GroupVersionForDiscovery{})},
{Fn: DeepCopy_v1_GroupVersionKind, InType: reflect.TypeOf(&GroupVersionKind{})},
{Fn: DeepCopy_v1_GroupVersionResource, InType: reflect.TypeOf(&GroupVersionResource{})},
{Fn: DeepCopy_v1_InternalEvent, InType: reflect.TypeOf(&InternalEvent{})},
{Fn: DeepCopy_v1_LabelSelector, InType: reflect.TypeOf(&LabelSelector{})},
{Fn: DeepCopy_v1_LabelSelectorRequirement, InType: reflect.TypeOf(&LabelSelectorRequirement{})},
{Fn: DeepCopy_v1_ListMeta, InType: reflect.TypeOf(&ListMeta{})},
{Fn: DeepCopy_v1_ListOptions, InType: reflect.TypeOf(&ListOptions{})},
{Fn: DeepCopy_v1_ObjectMeta, InType: reflect.TypeOf(&ObjectMeta{})},
{Fn: DeepCopy_v1_OwnerReference, InType: reflect.TypeOf(&OwnerReference{})},
{Fn: DeepCopy_v1_Patch, InType: reflect.TypeOf(&Patch{})},
{Fn: DeepCopy_v1_Preconditions, InType: reflect.TypeOf(&Preconditions{})},
{Fn: DeepCopy_v1_RootPaths, InType: reflect.TypeOf(&RootPaths{})},
{Fn: DeepCopy_v1_ServerAddressByClientCIDR, InType: reflect.TypeOf(&ServerAddressByClientCIDR{})},
{Fn: DeepCopy_v1_Status, InType: reflect.TypeOf(&Status{})},
{Fn: DeepCopy_v1_StatusCause, InType: reflect.TypeOf(&StatusCause{})},
{Fn: DeepCopy_v1_StatusDetails, InType: reflect.TypeOf(&StatusDetails{})},
{Fn: DeepCopy_v1_Time, InType: reflect.TypeOf(&Time{})},
{Fn: DeepCopy_v1_Timestamp, InType: reflect.TypeOf(&Timestamp{})},
{Fn: DeepCopy_v1_TypeMeta, InType: reflect.TypeOf(&TypeMeta{})},
{Fn: DeepCopy_v1_WatchEvent, InType: reflect.TypeOf(&WatchEvent{})},
}
}
func DeepCopy_v1_APIGroup(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*APIGroup)
out := out.(*APIGroup)
*out = *in
if in.Versions != nil {
in, out := &in.Versions, &out.Versions
*out = make([]GroupVersionForDiscovery, len(*in))
copy(*out, *in)
}
if in.ServerAddressByClientCIDRs != nil {
in, out := &in.ServerAddressByClientCIDRs, &out.ServerAddressByClientCIDRs
*out = make([]ServerAddressByClientCIDR, len(*in))
copy(*out, *in)
}
return nil
}
}
func DeepCopy_v1_APIGroupList(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*APIGroupList)
out := out.(*APIGroupList)
*out = *in
if in.Groups != nil {
in, out := &in.Groups, &out.Groups
*out = make([]APIGroup, len(*in))
for i := range *in {
if newVal, err := c.DeepCopy(&(*in)[i]); err != nil {
return err
} else {
(*out)[i] = *newVal.(*APIGroup)
}
}
}
return nil
}
}
func DeepCopy_v1_APIResource(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*APIResource)
out := out.(*APIResource)
*out = *in
if in.Verbs != nil {
in, out := &in.Verbs, &out.Verbs
*out = make(Verbs, len(*in))
copy(*out, *in)
}
if in.ShortNames != nil {
in, out := &in.ShortNames, &out.ShortNames
*out = make([]string, len(*in))
copy(*out, *in)
}
return nil
}
}
func DeepCopy_v1_APIResourceList(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*APIResourceList)
out := out.(*APIResourceList)
*out = *in
if in.APIResources != nil {
in, out := &in.APIResources, &out.APIResources
*out = make([]APIResource, len(*in))
for i := range *in {
if newVal, err := c.DeepCopy(&(*in)[i]); err != nil {
return err
} else {
(*out)[i] = *newVal.(*APIResource)
}
}
}
return nil
}
}
func DeepCopy_v1_APIVersions(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*APIVersions)
out := out.(*APIVersions)
*out = *in
if in.Versions != nil {
in, out := &in.Versions, &out.Versions
*out = make([]string, len(*in))
copy(*out, *in)
}
if in.ServerAddressByClientCIDRs != nil {
in, out := &in.ServerAddressByClientCIDRs, &out.ServerAddressByClientCIDRs
*out = make([]ServerAddressByClientCIDR, len(*in))
copy(*out, *in)
}
return nil
}
}
func DeepCopy_v1_DeleteOptions(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*DeleteOptions)
out := out.(*DeleteOptions)
*out = *in
if in.GracePeriodSeconds != nil {
in, out := &in.GracePeriodSeconds, &out.GracePeriodSeconds
*out = new(int64)
**out = **in
}
if in.Preconditions != nil {
in, out := &in.Preconditions, &out.Preconditions
if newVal, err := c.DeepCopy(*in); err != nil {
return err
} else {
*out = newVal.(*Preconditions)
}
}
if in.OrphanDependents != nil {
in, out := &in.OrphanDependents, &out.OrphanDependents
*out = new(bool)
**out = **in
}
return nil
}
}
func DeepCopy_v1_Duration(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*Duration)
out := out.(*Duration)
*out = *in
return nil
}
}
func DeepCopy_v1_ExportOptions(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*ExportOptions)
out := out.(*ExportOptions)
*out = *in
return nil
}
}
func DeepCopy_v1_GetOptions(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*GetOptions)
out := out.(*GetOptions)
*out = *in
return nil
}
}
func DeepCopy_v1_GroupKind(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*GroupKind)
out := out.(*GroupKind)
*out = *in
return nil
}
}
func DeepCopy_v1_GroupResource(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*GroupResource)
out := out.(*GroupResource)
*out = *in
return nil
}
}
func DeepCopy_v1_GroupVersion(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*GroupVersion)
out := out.(*GroupVersion)
*out = *in
return nil
}
}
func DeepCopy_v1_GroupVersionForDiscovery(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*GroupVersionForDiscovery)
out := out.(*GroupVersionForDiscovery)
*out = *in
return nil
}
}
func DeepCopy_v1_GroupVersionKind(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*GroupVersionKind)
out := out.(*GroupVersionKind)
*out = *in
return nil
}
}
func DeepCopy_v1_GroupVersionResource(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*GroupVersionResource)
out := out.(*GroupVersionResource)
*out = *in
return nil
}
}
func DeepCopy_v1_InternalEvent(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*InternalEvent)
out := out.(*InternalEvent)
*out = *in
// in.Object is kind 'Interface'
if in.Object != nil {
if newVal, err := c.DeepCopy(&in.Object); err != nil {
return err
} else {
out.Object = *newVal.(*runtime.Object)
}
}
return nil
}
}
func DeepCopy_v1_LabelSelector(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*LabelSelector)
out := out.(*LabelSelector)
*out = *in
if in.MatchLabels != nil {
in, out := &in.MatchLabels, &out.MatchLabels
*out = make(map[string]string)
for key, val := range *in {
(*out)[key] = val
}
}
if in.MatchExpressions != nil {
in, out := &in.MatchExpressions, &out.MatchExpressions
*out = make([]LabelSelectorRequirement, len(*in))
for i := range *in {
if newVal, err := c.DeepCopy(&(*in)[i]); err != nil {
return err
} else {
(*out)[i] = *newVal.(*LabelSelectorRequirement)
}
}
}
return nil
}
}
func DeepCopy_v1_LabelSelectorRequirement(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*LabelSelectorRequirement)
out := out.(*LabelSelectorRequirement)
*out = *in
if in.Values != nil {
in, out := &in.Values, &out.Values
*out = make([]string, len(*in))
copy(*out, *in)
}
return nil
}
}
func DeepCopy_v1_ListMeta(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*ListMeta)
out := out.(*ListMeta)
*out = *in
return nil
}
}
func DeepCopy_v1_ListOptions(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*ListOptions)
out := out.(*ListOptions)
*out = *in
if in.TimeoutSeconds != nil {
in, out := &in.TimeoutSeconds, &out.TimeoutSeconds
*out = new(int64)
**out = **in
}
return nil
}
}
func DeepCopy_v1_ObjectMeta(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*ObjectMeta)
out := out.(*ObjectMeta)
*out = *in
out.CreationTimestamp = in.CreationTimestamp.DeepCopy()
if in.DeletionTimestamp != nil {
in, out := &in.DeletionTimestamp, &out.DeletionTimestamp
*out = new(Time)
**out = (*in).DeepCopy()
}
if in.DeletionGracePeriodSeconds != nil {
in, out := &in.DeletionGracePeriodSeconds, &out.DeletionGracePeriodSeconds
*out = new(int64)
**out = **in
}
if in.Labels != nil {
in, out := &in.Labels, &out.Labels
*out = make(map[string]string)
for key, val := range *in {
(*out)[key] = val
}
}
if in.Annotations != nil {
in, out := &in.Annotations, &out.Annotations
*out = make(map[string]string)
for key, val := range *in {
(*out)[key] = val
}
}
if in.OwnerReferences != nil {
in, out := &in.OwnerReferences, &out.OwnerReferences
*out = make([]OwnerReference, len(*in))
for i := range *in {
if newVal, err := c.DeepCopy(&(*in)[i]); err != nil {
return err
} else {
(*out)[i] = *newVal.(*OwnerReference)
}
}
}
if in.Finalizers != nil {
in, out := &in.Finalizers, &out.Finalizers
*out = make([]string, len(*in))
copy(*out, *in)
}
return nil
}
}
func DeepCopy_v1_OwnerReference(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*OwnerReference)
out := out.(*OwnerReference)
*out = *in
if in.Controller != nil {
in, out := &in.Controller, &out.Controller
*out = new(bool)
**out = **in
}
return nil
}
}
func DeepCopy_v1_Patch(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*Patch)
out := out.(*Patch)
*out = *in
return nil
}
}
func DeepCopy_v1_Preconditions(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*Preconditions)
out := out.(*Preconditions)
*out = *in
if in.UID != nil {
in, out := &in.UID, &out.UID
*out = new(types.UID)
**out = **in
}
return nil
}
}
func DeepCopy_v1_RootPaths(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*RootPaths)
out := out.(*RootPaths)
*out = *in
if in.Paths != nil {
in, out := &in.Paths, &out.Paths
*out = make([]string, len(*in))
copy(*out, *in)
}
return nil
}
}
func DeepCopy_v1_ServerAddressByClientCIDR(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*ServerAddressByClientCIDR)
out := out.(*ServerAddressByClientCIDR)
*out = *in
return nil
}
}
func DeepCopy_v1_Status(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*Status)
out := out.(*Status)
*out = *in
if in.Details != nil {
in, out := &in.Details, &out.Details
if newVal, err := c.DeepCopy(*in); err != nil {
return err
} else {
*out = newVal.(*StatusDetails)
}
}
return nil
}
}
func DeepCopy_v1_StatusCause(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*StatusCause)
out := out.(*StatusCause)
*out = *in
return nil
}
}
func DeepCopy_v1_StatusDetails(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*StatusDetails)
out := out.(*StatusDetails)
*out = *in
if in.Causes != nil {
in, out := &in.Causes, &out.Causes
*out = make([]StatusCause, len(*in))
copy(*out, *in)
}
return nil
}
}
func DeepCopy_v1_Time(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*Time)
out := out.(*Time)
*out = in.DeepCopy()
return nil
}
}
func DeepCopy_v1_Timestamp(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*Timestamp)
out := out.(*Timestamp)
*out = *in
return nil
}
}
func DeepCopy_v1_TypeMeta(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*TypeMeta)
out := out.(*TypeMeta)
*out = *in
return nil
}
}
func DeepCopy_v1_WatchEvent(in interface{}, out interface{}, c *conversion.Cloner) error {
{
in := in.(*WatchEvent)
out := out.(*WatchEvent)
*out = *in
if newVal, err := c.DeepCopy(&in.Object); err != nil {
return err
} else {
out.Object = *newVal.(*runtime.RawExtension)
}
return nil
}
}

View file

@ -0,0 +1,32 @@
// +build !ignore_autogenerated
/*
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 defaulter-gen. Do not edit it manually!
package v1
import (
runtime "k8s.io/apimachinery/pkg/runtime"
)
// RegisterDefaults adds defaulters functions to the given scheme.
// Public to allow building arbitrary schemes.
// All generated defaulters are covering - they call all nested defaulters.
func RegisterDefaults(scheme *runtime.Scheme) error {
return nil
}

10
vendor/k8s.io/apimachinery/pkg/conversion/OWNERS generated vendored Normal file
View file

@ -0,0 +1,10 @@
approvers:
- derekwaynecarr
- lavalamp
- smarterclayton
- wojtek-t
reviewers:
- derekwaynecarr
- lavalamp
- smarterclayton
- wojtek-t

249
vendor/k8s.io/apimachinery/pkg/conversion/cloner.go generated vendored Normal file
View file

@ -0,0 +1,249 @@
/*
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 conversion
import (
"fmt"
"reflect"
)
// Cloner knows how to copy one type to another.
type Cloner struct {
// Map from the type to a function which can do the deep copy.
deepCopyFuncs map[reflect.Type]reflect.Value
generatedDeepCopyFuncs map[reflect.Type]func(in interface{}, out interface{}, c *Cloner) error
}
// NewCloner creates a new Cloner object.
func NewCloner() *Cloner {
c := &Cloner{
deepCopyFuncs: map[reflect.Type]reflect.Value{},
generatedDeepCopyFuncs: map[reflect.Type]func(in interface{}, out interface{}, c *Cloner) error{},
}
if err := c.RegisterDeepCopyFunc(byteSliceDeepCopy); err != nil {
// If one of the deep-copy functions is malformed, detect it immediately.
panic(err)
}
return c
}
// Prevent recursing into every byte...
func byteSliceDeepCopy(in *[]byte, out *[]byte, c *Cloner) error {
if *in != nil {
*out = make([]byte, len(*in))
copy(*out, *in)
} else {
*out = nil
}
return nil
}
// Verifies whether a deep-copy function has a correct signature.
func verifyDeepCopyFunctionSignature(ft reflect.Type) error {
if ft.Kind() != reflect.Func {
return fmt.Errorf("expected func, got: %v", ft)
}
if ft.NumIn() != 3 {
return fmt.Errorf("expected three 'in' params, got %v", ft)
}
if ft.NumOut() != 1 {
return fmt.Errorf("expected one 'out' param, got %v", ft)
}
if ft.In(0).Kind() != reflect.Ptr {
return fmt.Errorf("expected pointer arg for 'in' param 0, got: %v", ft)
}
if ft.In(1) != ft.In(0) {
return fmt.Errorf("expected 'in' param 0 the same as param 1, got: %v", ft)
}
var forClonerType Cloner
if expected := reflect.TypeOf(&forClonerType); ft.In(2) != expected {
return fmt.Errorf("expected '%v' arg for 'in' param 2, got: '%v'", expected, ft.In(2))
}
var forErrorType error
// This convolution is necessary, otherwise TypeOf picks up on the fact
// that forErrorType is nil
errorType := reflect.TypeOf(&forErrorType).Elem()
if ft.Out(0) != errorType {
return fmt.Errorf("expected error return, got: %v", ft)
}
return nil
}
// RegisterGeneratedDeepCopyFunc registers a copying func with the Cloner.
// deepCopyFunc must take three parameters: a type input, a pointer to a
// type output, and a pointer to Cloner. It should return an error.
//
// Example:
// c.RegisterGeneratedDeepCopyFunc(
// func(in Pod, out *Pod, c *Cloner) error {
// // deep copy logic...
// return nil
// })
func (c *Cloner) RegisterDeepCopyFunc(deepCopyFunc interface{}) error {
fv := reflect.ValueOf(deepCopyFunc)
ft := fv.Type()
if err := verifyDeepCopyFunctionSignature(ft); err != nil {
return err
}
c.deepCopyFuncs[ft.In(0)] = fv
return nil
}
// GeneratedDeepCopyFunc bundles an untyped generated deep-copy function of a type
// with a reflection type object used as a key to lookup the deep-copy function.
type GeneratedDeepCopyFunc struct {
Fn func(in interface{}, out interface{}, c *Cloner) error
InType reflect.Type
}
// Similar to RegisterDeepCopyFunc, but registers deep copy function that were
// automatically generated.
func (c *Cloner) RegisterGeneratedDeepCopyFunc(fn GeneratedDeepCopyFunc) error {
c.generatedDeepCopyFuncs[fn.InType] = fn.Fn
return nil
}
// DeepCopy will perform a deep copy of a given object.
func (c *Cloner) DeepCopy(in interface{}) (interface{}, error) {
// Can be invalid if we run DeepCopy(X) where X is a nil interface type.
// For example, we get an invalid value when someone tries to deep-copy
// a nil labels.Selector.
// This does not occur if X is nil and is a pointer to a concrete type.
if in == nil {
return nil, nil
}
inValue := reflect.ValueOf(in)
outValue, err := c.deepCopy(inValue)
if err != nil {
return nil, err
}
return outValue.Interface(), nil
}
func (c *Cloner) deepCopy(src reflect.Value) (reflect.Value, error) {
inType := src.Type()
switch src.Kind() {
case reflect.Interface, reflect.Ptr, reflect.Map, reflect.Slice:
if src.IsNil() {
return src, nil
}
}
if fv, ok := c.deepCopyFuncs[inType]; ok {
return c.customDeepCopy(src, fv)
}
if fv, ok := c.generatedDeepCopyFuncs[inType]; ok {
var outValue reflect.Value
outValue = reflect.New(inType.Elem())
err := fv(src.Interface(), outValue.Interface(), c)
return outValue, err
}
return c.defaultDeepCopy(src)
}
func (c *Cloner) customDeepCopy(src, fv reflect.Value) (reflect.Value, error) {
outValue := reflect.New(src.Type().Elem())
args := []reflect.Value{src, outValue, reflect.ValueOf(c)}
result := fv.Call(args)[0].Interface()
// This convolution is necessary because nil interfaces won't convert
// to error.
if result == nil {
return outValue, nil
}
return outValue, result.(error)
}
func (c *Cloner) defaultDeepCopy(src reflect.Value) (reflect.Value, error) {
switch src.Kind() {
case reflect.Chan, reflect.Func, reflect.UnsafePointer, reflect.Uintptr:
return src, fmt.Errorf("cannot deep copy kind: %s", src.Kind())
case reflect.Array:
dst := reflect.New(src.Type())
for i := 0; i < src.Len(); i++ {
copyVal, err := c.deepCopy(src.Index(i))
if err != nil {
return src, err
}
dst.Elem().Index(i).Set(copyVal)
}
return dst.Elem(), nil
case reflect.Interface:
if src.IsNil() {
return src, nil
}
return c.deepCopy(src.Elem())
case reflect.Map:
if src.IsNil() {
return src, nil
}
dst := reflect.MakeMap(src.Type())
for _, k := range src.MapKeys() {
copyVal, err := c.deepCopy(src.MapIndex(k))
if err != nil {
return src, err
}
dst.SetMapIndex(k, copyVal)
}
return dst, nil
case reflect.Ptr:
if src.IsNil() {
return src, nil
}
dst := reflect.New(src.Type().Elem())
copyVal, err := c.deepCopy(src.Elem())
if err != nil {
return src, err
}
dst.Elem().Set(copyVal)
return dst, nil
case reflect.Slice:
if src.IsNil() {
return src, nil
}
dst := reflect.MakeSlice(src.Type(), 0, src.Len())
for i := 0; i < src.Len(); i++ {
copyVal, err := c.deepCopy(src.Index(i))
if err != nil {
return src, err
}
dst = reflect.Append(dst, copyVal)
}
return dst, nil
case reflect.Struct:
dst := reflect.New(src.Type())
for i := 0; i < src.NumField(); i++ {
if !dst.Elem().Field(i).CanSet() {
// Can't set private fields. At this point, the
// best we can do is a shallow copy. For
// example, time.Time is a value type with
// private members that can be shallow copied.
return src, nil
}
copyVal, err := c.deepCopy(src.Field(i))
if err != nil {
return src, err
}
dst.Elem().Field(i).Set(copyVal)
}
return dst.Elem(), nil
default:
// Value types like numbers, booleans, and strings.
return src, nil
}
}

953
vendor/k8s.io/apimachinery/pkg/conversion/converter.go generated vendored Normal file
View file

@ -0,0 +1,953 @@
/*
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 conversion
import (
"fmt"
"reflect"
)
type typePair struct {
source reflect.Type
dest reflect.Type
}
type typeNamePair struct {
fieldType reflect.Type
fieldName string
}
// DebugLogger allows you to get debugging messages if necessary.
type DebugLogger interface {
Logf(format string, args ...interface{})
}
type NameFunc func(t reflect.Type) string
var DefaultNameFunc = func(t reflect.Type) string { return t.Name() }
type GenericConversionFunc func(a, b interface{}, scope Scope) (bool, error)
// Converter knows how to convert one type to another.
type Converter struct {
// Map from the conversion pair to a function which can
// do the conversion.
conversionFuncs ConversionFuncs
generatedConversionFuncs ConversionFuncs
// genericConversions are called during normal conversion to offer a "fast-path"
// that avoids all reflection. These methods are not called outside of the .Convert()
// method.
genericConversions []GenericConversionFunc
// Set of conversions that should be treated as a no-op
ignoredConversions map[typePair]struct{}
// This is a map from a source field type and name, to a list of destination
// field type and name.
structFieldDests map[typeNamePair][]typeNamePair
// Allows for the opposite lookup of structFieldDests. So that SourceFromDest
// copy flag also works. So this is a map of destination field name, to potential
// source field name and type to look for.
structFieldSources map[typeNamePair][]typeNamePair
// Map from a type to a function which applies defaults.
defaultingFuncs map[reflect.Type]reflect.Value
// Similar to above, but function is stored as interface{}.
defaultingInterfaces map[reflect.Type]interface{}
// Map from an input type to a function which can apply a key name mapping
inputFieldMappingFuncs map[reflect.Type]FieldMappingFunc
// Map from an input type to a set of default conversion flags.
inputDefaultFlags map[reflect.Type]FieldMatchingFlags
// If non-nil, will be called to print helpful debugging info. Quite verbose.
Debug DebugLogger
// nameFunc is called to retrieve the name of a type; this name is used for the
// purpose of deciding whether two types match or not (i.e., will we attempt to
// do a conversion). The default returns the go type name.
nameFunc func(t reflect.Type) string
}
// NewConverter creates a new Converter object.
func NewConverter(nameFn NameFunc) *Converter {
c := &Converter{
conversionFuncs: NewConversionFuncs(),
generatedConversionFuncs: NewConversionFuncs(),
ignoredConversions: make(map[typePair]struct{}),
defaultingFuncs: make(map[reflect.Type]reflect.Value),
defaultingInterfaces: make(map[reflect.Type]interface{}),
nameFunc: nameFn,
structFieldDests: make(map[typeNamePair][]typeNamePair),
structFieldSources: make(map[typeNamePair][]typeNamePair),
inputFieldMappingFuncs: make(map[reflect.Type]FieldMappingFunc),
inputDefaultFlags: make(map[reflect.Type]FieldMatchingFlags),
}
c.RegisterConversionFunc(Convert_Slice_byte_To_Slice_byte)
return c
}
// AddGenericConversionFunc adds a function that accepts the ConversionFunc call pattern
// (for two conversion types) to the converter. These functions are checked first during
// a normal conversion, but are otherwise not called. Use AddConversionFuncs when registering
// typed conversions.
func (c *Converter) AddGenericConversionFunc(fn GenericConversionFunc) {
c.genericConversions = append(c.genericConversions, fn)
}
// WithConversions returns a Converter that is a copy of c but with the additional
// fns merged on top.
func (c *Converter) WithConversions(fns ConversionFuncs) *Converter {
copied := *c
copied.conversionFuncs = c.conversionFuncs.Merge(fns)
return &copied
}
// DefaultMeta returns the conversion FieldMappingFunc and meta for a given type.
func (c *Converter) DefaultMeta(t reflect.Type) (FieldMatchingFlags, *Meta) {
return c.inputDefaultFlags[t], &Meta{
KeyNameMapping: c.inputFieldMappingFuncs[t],
}
}
// Convert_Slice_byte_To_Slice_byte prevents recursing into every byte
func Convert_Slice_byte_To_Slice_byte(in *[]byte, out *[]byte, s Scope) error {
if *in == nil {
*out = nil
return nil
}
*out = make([]byte, len(*in))
copy(*out, *in)
return nil
}
// Scope is passed to conversion funcs to allow them to continue an ongoing conversion.
// If multiple converters exist in the system, Scope will allow you to use the correct one
// from a conversion function--that is, the one your conversion function was called by.
type Scope interface {
// Call Convert to convert sub-objects. Note that if you call it with your own exact
// parameters, you'll run out of stack space before anything useful happens.
Convert(src, dest interface{}, flags FieldMatchingFlags) error
// DefaultConvert performs the default conversion, without calling a conversion func
// on the current stack frame. This makes it safe to call from a conversion func.
DefaultConvert(src, dest interface{}, flags FieldMatchingFlags) error
// If registered, returns a function applying defaults for objects of a given type.
// Used for automatically generating conversion functions.
DefaultingInterface(inType reflect.Type) (interface{}, bool)
// SrcTags and DestTags contain the struct tags that src and dest had, respectively.
// If the enclosing object was not a struct, then these will contain no tags, of course.
SrcTag() reflect.StructTag
DestTag() reflect.StructTag
// Flags returns the flags with which the conversion was started.
Flags() FieldMatchingFlags
// Meta returns any information originally passed to Convert.
Meta() *Meta
}
// FieldMappingFunc can convert an input field value into different values, depending on
// the value of the source or destination struct tags.
type FieldMappingFunc func(key string, sourceTag, destTag reflect.StructTag) (source string, dest string)
func NewConversionFuncs() ConversionFuncs {
return ConversionFuncs{fns: make(map[typePair]reflect.Value)}
}
type ConversionFuncs struct {
fns map[typePair]reflect.Value
}
// Add adds the provided conversion functions to the lookup table - they must have the signature
// `func(type1, type2, Scope) error`. Functions are added in the order passed and will override
// previously registered pairs.
func (c ConversionFuncs) Add(fns ...interface{}) error {
for _, fn := range fns {
fv := reflect.ValueOf(fn)
ft := fv.Type()
if err := verifyConversionFunctionSignature(ft); err != nil {
return err
}
c.fns[typePair{ft.In(0).Elem(), ft.In(1).Elem()}] = fv
}
return nil
}
// Merge returns a new ConversionFuncs that contains all conversions from
// both other and c, with other conversions taking precedence.
func (c ConversionFuncs) Merge(other ConversionFuncs) ConversionFuncs {
merged := NewConversionFuncs()
for k, v := range c.fns {
merged.fns[k] = v
}
for k, v := range other.fns {
merged.fns[k] = v
}
return merged
}
// Meta is supplied by Scheme, when it calls Convert.
type Meta struct {
// KeyNameMapping is an optional function which may map the listed key (field name)
// into a source and destination value.
KeyNameMapping FieldMappingFunc
// Context is an optional field that callers may use to pass info to conversion functions.
Context interface{}
}
// scope contains information about an ongoing conversion.
type scope struct {
converter *Converter
meta *Meta
flags FieldMatchingFlags
// srcStack & destStack are separate because they may not have a 1:1
// relationship.
srcStack scopeStack
destStack scopeStack
}
type scopeStackElem struct {
tag reflect.StructTag
value reflect.Value
key string
}
type scopeStack []scopeStackElem
func (s *scopeStack) pop() {
n := len(*s)
*s = (*s)[:n-1]
}
func (s *scopeStack) push(e scopeStackElem) {
*s = append(*s, e)
}
func (s *scopeStack) top() *scopeStackElem {
return &(*s)[len(*s)-1]
}
func (s scopeStack) describe() string {
desc := ""
if len(s) > 1 {
desc = "(" + s[1].value.Type().String() + ")"
}
for i, v := range s {
if i < 2 {
// First layer on stack is not real; second is handled specially above.
continue
}
if v.key == "" {
desc += fmt.Sprintf(".%v", v.value.Type())
} else {
desc += fmt.Sprintf(".%v", v.key)
}
}
return desc
}
func (s *scope) DefaultingInterface(inType reflect.Type) (interface{}, bool) {
value, found := s.converter.defaultingInterfaces[inType]
return value, found
}
// Formats src & dest as indices for printing.
func (s *scope) setIndices(src, dest int) {
s.srcStack.top().key = fmt.Sprintf("[%v]", src)
s.destStack.top().key = fmt.Sprintf("[%v]", dest)
}
// Formats src & dest as map keys for printing.
func (s *scope) setKeys(src, dest interface{}) {
s.srcStack.top().key = fmt.Sprintf(`["%v"]`, src)
s.destStack.top().key = fmt.Sprintf(`["%v"]`, dest)
}
// Convert continues a conversion.
func (s *scope) Convert(src, dest interface{}, flags FieldMatchingFlags) error {
return s.converter.Convert(src, dest, flags, s.meta)
}
// DefaultConvert continues a conversion, performing a default conversion (no conversion func)
// for the current stack frame.
func (s *scope) DefaultConvert(src, dest interface{}, flags FieldMatchingFlags) error {
return s.converter.DefaultConvert(src, dest, flags, s.meta)
}
// SrcTag returns the tag of the struct containing the current source item, if any.
func (s *scope) SrcTag() reflect.StructTag {
return s.srcStack.top().tag
}
// DestTag returns the tag of the struct containing the current dest item, if any.
func (s *scope) DestTag() reflect.StructTag {
return s.destStack.top().tag
}
// Flags returns the flags with which the current conversion was started.
func (s *scope) Flags() FieldMatchingFlags {
return s.flags
}
// Meta returns the meta object that was originally passed to Convert.
func (s *scope) Meta() *Meta {
return s.meta
}
// describe prints the path to get to the current (source, dest) values.
func (s *scope) describe() (src, dest string) {
return s.srcStack.describe(), s.destStack.describe()
}
// error makes an error that includes information about where we were in the objects
// we were asked to convert.
func (s *scope) errorf(message string, args ...interface{}) error {
srcPath, destPath := s.describe()
where := fmt.Sprintf("converting %v to %v: ", srcPath, destPath)
return fmt.Errorf(where+message, args...)
}
// Verifies whether a conversion function has a correct signature.
func verifyConversionFunctionSignature(ft reflect.Type) error {
if ft.Kind() != reflect.Func {
return fmt.Errorf("expected func, got: %v", ft)
}
if ft.NumIn() != 3 {
return fmt.Errorf("expected three 'in' params, got: %v", ft)
}
if ft.NumOut() != 1 {
return fmt.Errorf("expected one 'out' param, got: %v", ft)
}
if ft.In(0).Kind() != reflect.Ptr {
return fmt.Errorf("expected pointer arg for 'in' param 0, got: %v", ft)
}
if ft.In(1).Kind() != reflect.Ptr {
return fmt.Errorf("expected pointer arg for 'in' param 1, got: %v", ft)
}
scopeType := Scope(nil)
if e, a := reflect.TypeOf(&scopeType).Elem(), ft.In(2); e != a {
return fmt.Errorf("expected '%v' arg for 'in' param 2, got '%v' (%v)", e, a, ft)
}
var forErrorType error
// This convolution is necessary, otherwise TypeOf picks up on the fact
// that forErrorType is nil.
errorType := reflect.TypeOf(&forErrorType).Elem()
if ft.Out(0) != errorType {
return fmt.Errorf("expected error return, got: %v", ft)
}
return nil
}
// RegisterConversionFunc registers a conversion func with the
// Converter. conversionFunc must take three parameters: a pointer to the input
// type, a pointer to the output type, and a conversion.Scope (which should be
// used if recursive conversion calls are desired). It must return an error.
//
// Example:
// c.RegisterConversionFunc(
// func(in *Pod, out *v1.Pod, s Scope) error {
// // conversion logic...
// return nil
// })
func (c *Converter) RegisterConversionFunc(conversionFunc interface{}) error {
return c.conversionFuncs.Add(conversionFunc)
}
// Similar to RegisterConversionFunc, but registers conversion function that were
// automatically generated.
func (c *Converter) RegisterGeneratedConversionFunc(conversionFunc interface{}) error {
return c.generatedConversionFuncs.Add(conversionFunc)
}
// RegisterIgnoredConversion registers a "no-op" for conversion, where any requested
// conversion between from and to is ignored.
func (c *Converter) RegisterIgnoredConversion(from, to interface{}) error {
typeFrom := reflect.TypeOf(from)
typeTo := reflect.TypeOf(to)
if reflect.TypeOf(from).Kind() != reflect.Ptr {
return fmt.Errorf("expected pointer arg for 'from' param 0, got: %v", typeFrom)
}
if typeTo.Kind() != reflect.Ptr {
return fmt.Errorf("expected pointer arg for 'to' param 1, got: %v", typeTo)
}
c.ignoredConversions[typePair{typeFrom.Elem(), typeTo.Elem()}] = struct{}{}
return nil
}
// IsConversionIgnored returns true if the specified objects should be dropped during
// conversion.
func (c *Converter) IsConversionIgnored(inType, outType reflect.Type) bool {
_, found := c.ignoredConversions[typePair{inType, outType}]
return found
}
func (c *Converter) HasConversionFunc(inType, outType reflect.Type) bool {
_, found := c.conversionFuncs.fns[typePair{inType, outType}]
return found
}
func (c *Converter) ConversionFuncValue(inType, outType reflect.Type) (reflect.Value, bool) {
value, found := c.conversionFuncs.fns[typePair{inType, outType}]
return value, found
}
// SetStructFieldCopy registers a correspondence. Whenever a struct field is encountered
// which has a type and name matching srcFieldType and srcFieldName, it wil be copied
// into the field in the destination struct matching destFieldType & Name, if such a
// field exists.
// May be called multiple times, even for the same source field & type--all applicable
// copies will be performed.
func (c *Converter) SetStructFieldCopy(srcFieldType interface{}, srcFieldName string, destFieldType interface{}, destFieldName string) error {
st := reflect.TypeOf(srcFieldType)
dt := reflect.TypeOf(destFieldType)
srcKey := typeNamePair{st, srcFieldName}
destKey := typeNamePair{dt, destFieldName}
c.structFieldDests[srcKey] = append(c.structFieldDests[srcKey], destKey)
c.structFieldSources[destKey] = append(c.structFieldSources[destKey], srcKey)
return nil
}
// RegisterDefaultingFunc registers a value-defaulting func with the Converter.
// defaultingFunc must take one parameter: a pointer to the input type.
//
// Example:
// c.RegisterDefaultingFunc(
// func(in *v1.Pod) {
// // defaulting logic...
// })
func (c *Converter) RegisterDefaultingFunc(defaultingFunc interface{}) error {
fv := reflect.ValueOf(defaultingFunc)
ft := fv.Type()
if ft.Kind() != reflect.Func {
return fmt.Errorf("expected func, got: %v", ft)
}
if ft.NumIn() != 1 {
return fmt.Errorf("expected one 'in' param, got: %v", ft)
}
if ft.NumOut() != 0 {
return fmt.Errorf("expected zero 'out' params, got: %v", ft)
}
if ft.In(0).Kind() != reflect.Ptr {
return fmt.Errorf("expected pointer arg for 'in' param 0, got: %v", ft)
}
inType := ft.In(0).Elem()
c.defaultingFuncs[inType] = fv
c.defaultingInterfaces[inType] = defaultingFunc
return nil
}
// RegisterInputDefaults registers a field name mapping function, used when converting
// from maps to structs. Inputs to the conversion methods are checked for this type and a mapping
// applied automatically if the input matches in. A set of default flags for the input conversion
// may also be provided, which will be used when no explicit flags are requested.
func (c *Converter) RegisterInputDefaults(in interface{}, fn FieldMappingFunc, defaultFlags FieldMatchingFlags) error {
fv := reflect.ValueOf(in)
ft := fv.Type()
if ft.Kind() != reflect.Ptr {
return fmt.Errorf("expected pointer 'in' argument, got: %v", ft)
}
c.inputFieldMappingFuncs[ft] = fn
c.inputDefaultFlags[ft] = defaultFlags
return nil
}
// FieldMatchingFlags contains a list of ways in which struct fields could be
// copied. These constants may be | combined.
type FieldMatchingFlags int
const (
// Loop through destination fields, search for matching source
// field to copy it from. Source fields with no corresponding
// destination field will be ignored. If SourceToDest is
// specified, this flag is ignored. If neither is specified,
// or no flags are passed, this flag is the default.
DestFromSource FieldMatchingFlags = 0
// Loop through source fields, search for matching dest field
// to copy it into. Destination fields with no corresponding
// source field will be ignored.
SourceToDest FieldMatchingFlags = 1 << iota
// Don't treat it as an error if the corresponding source or
// dest field can't be found.
IgnoreMissingFields
// Don't require type names to match.
AllowDifferentFieldTypeNames
)
// IsSet returns true if the given flag or combination of flags is set.
func (f FieldMatchingFlags) IsSet(flag FieldMatchingFlags) bool {
if flag == DestFromSource {
// The bit logic doesn't work on the default value.
return f&SourceToDest != SourceToDest
}
return f&flag == flag
}
// Convert will translate src to dest if it knows how. Both must be pointers.
// If no conversion func is registered and the default copying mechanism
// doesn't work on this type pair, an error will be returned.
// Read the comments on the various FieldMatchingFlags constants to understand
// what the 'flags' parameter does.
// 'meta' is given to allow you to pass information to conversion functions,
// it is not used by Convert() other than storing it in the scope.
// Not safe for objects with cyclic references!
func (c *Converter) Convert(src, dest interface{}, flags FieldMatchingFlags, meta *Meta) error {
if len(c.genericConversions) > 0 {
// TODO: avoid scope allocation
s := &scope{converter: c, flags: flags, meta: meta}
for _, fn := range c.genericConversions {
if ok, err := fn(src, dest, s); ok {
return err
}
}
}
return c.doConversion(src, dest, flags, meta, c.convert)
}
// DefaultConvert will translate src to dest if it knows how. Both must be pointers.
// No conversion func is used. If the default copying mechanism
// doesn't work on this type pair, an error will be returned.
// Read the comments on the various FieldMatchingFlags constants to understand
// what the 'flags' parameter does.
// 'meta' is given to allow you to pass information to conversion functions,
// it is not used by DefaultConvert() other than storing it in the scope.
// Not safe for objects with cyclic references!
func (c *Converter) DefaultConvert(src, dest interface{}, flags FieldMatchingFlags, meta *Meta) error {
return c.doConversion(src, dest, flags, meta, c.defaultConvert)
}
type conversionFunc func(sv, dv reflect.Value, scope *scope) error
func (c *Converter) doConversion(src, dest interface{}, flags FieldMatchingFlags, meta *Meta, f conversionFunc) error {
dv, err := EnforcePtr(dest)
if err != nil {
return err
}
if !dv.CanAddr() && !dv.CanSet() {
return fmt.Errorf("can't write to dest")
}
sv, err := EnforcePtr(src)
if err != nil {
return err
}
s := &scope{
converter: c,
flags: flags,
meta: meta,
}
// Leave something on the stack, so that calls to struct tag getters never fail.
s.srcStack.push(scopeStackElem{})
s.destStack.push(scopeStackElem{})
return f(sv, dv, s)
}
// callCustom calls 'custom' with sv & dv. custom must be a conversion function.
func (c *Converter) callCustom(sv, dv, custom reflect.Value, scope *scope) error {
if !sv.CanAddr() {
sv2 := reflect.New(sv.Type())
sv2.Elem().Set(sv)
sv = sv2
} else {
sv = sv.Addr()
}
if !dv.CanAddr() {
if !dv.CanSet() {
return scope.errorf("can't addr or set dest.")
}
dvOrig := dv
dv := reflect.New(dvOrig.Type())
defer func() { dvOrig.Set(dv) }()
} else {
dv = dv.Addr()
}
args := []reflect.Value{sv, dv, reflect.ValueOf(scope)}
ret := custom.Call(args)[0].Interface()
// This convolution is necessary because nil interfaces won't convert
// to errors.
if ret == nil {
return nil
}
return ret.(error)
}
// convert recursively copies sv into dv, calling an appropriate conversion function if
// one is registered.
func (c *Converter) convert(sv, dv reflect.Value, scope *scope) error {
dt, st := dv.Type(), sv.Type()
// Apply default values.
if fv, ok := c.defaultingFuncs[st]; ok {
if c.Debug != nil {
c.Debug.Logf("Applying defaults for '%v'", st)
}
args := []reflect.Value{sv.Addr()}
fv.Call(args)
}
pair := typePair{st, dt}
// ignore conversions of this type
if _, ok := c.ignoredConversions[pair]; ok {
if c.Debug != nil {
c.Debug.Logf("Ignoring conversion of '%v' to '%v'", st, dt)
}
return nil
}
// Convert sv to dv.
if fv, ok := c.conversionFuncs.fns[pair]; ok {
if c.Debug != nil {
c.Debug.Logf("Calling custom conversion of '%v' to '%v'", st, dt)
}
return c.callCustom(sv, dv, fv, scope)
}
if fv, ok := c.generatedConversionFuncs.fns[pair]; ok {
if c.Debug != nil {
c.Debug.Logf("Calling generated conversion of '%v' to '%v'", st, dt)
}
return c.callCustom(sv, dv, fv, scope)
}
return c.defaultConvert(sv, dv, scope)
}
// defaultConvert recursively copies sv into dv. no conversion function is called
// for the current stack frame (but conversion functions may be called for nested objects)
func (c *Converter) defaultConvert(sv, dv reflect.Value, scope *scope) error {
dt, st := dv.Type(), sv.Type()
if !dv.CanSet() {
return scope.errorf("Cannot set dest. (Tried to deep copy something with unexported fields?)")
}
if !scope.flags.IsSet(AllowDifferentFieldTypeNames) && c.nameFunc(dt) != c.nameFunc(st) {
return scope.errorf(
"type names don't match (%v, %v), and no conversion 'func (%v, %v) error' registered.",
c.nameFunc(st), c.nameFunc(dt), st, dt)
}
switch st.Kind() {
case reflect.Map, reflect.Ptr, reflect.Slice, reflect.Interface, reflect.Struct:
// Don't copy these via assignment/conversion!
default:
// This should handle all simple types.
if st.AssignableTo(dt) {
dv.Set(sv)
return nil
}
if st.ConvertibleTo(dt) {
dv.Set(sv.Convert(dt))
return nil
}
}
if c.Debug != nil {
c.Debug.Logf("Trying to convert '%v' to '%v'", st, dt)
}
scope.srcStack.push(scopeStackElem{value: sv})
scope.destStack.push(scopeStackElem{value: dv})
defer scope.srcStack.pop()
defer scope.destStack.pop()
switch dv.Kind() {
case reflect.Struct:
return c.convertKV(toKVValue(sv), toKVValue(dv), scope)
case reflect.Slice:
if sv.IsNil() {
// Don't make a zero-length slice.
dv.Set(reflect.Zero(dt))
return nil
}
dv.Set(reflect.MakeSlice(dt, sv.Len(), sv.Cap()))
for i := 0; i < sv.Len(); i++ {
scope.setIndices(i, i)
if err := c.convert(sv.Index(i), dv.Index(i), scope); err != nil {
return err
}
}
case reflect.Ptr:
if sv.IsNil() {
// Don't copy a nil ptr!
dv.Set(reflect.Zero(dt))
return nil
}
dv.Set(reflect.New(dt.Elem()))
switch st.Kind() {
case reflect.Ptr, reflect.Interface:
return c.convert(sv.Elem(), dv.Elem(), scope)
default:
return c.convert(sv, dv.Elem(), scope)
}
case reflect.Map:
if sv.IsNil() {
// Don't copy a nil ptr!
dv.Set(reflect.Zero(dt))
return nil
}
dv.Set(reflect.MakeMap(dt))
for _, sk := range sv.MapKeys() {
dk := reflect.New(dt.Key()).Elem()
if err := c.convert(sk, dk, scope); err != nil {
return err
}
dkv := reflect.New(dt.Elem()).Elem()
scope.setKeys(sk.Interface(), dk.Interface())
// TODO: sv.MapIndex(sk) may return a value with CanAddr() == false,
// because a map[string]struct{} does not allow a pointer reference.
// Calling a custom conversion function defined for the map value
// will panic. Example is PodInfo map[string]ContainerStatus.
if err := c.convert(sv.MapIndex(sk), dkv, scope); err != nil {
return err
}
dv.SetMapIndex(dk, dkv)
}
case reflect.Interface:
if sv.IsNil() {
// Don't copy a nil interface!
dv.Set(reflect.Zero(dt))
return nil
}
tmpdv := reflect.New(sv.Elem().Type()).Elem()
if err := c.convert(sv.Elem(), tmpdv, scope); err != nil {
return err
}
dv.Set(reflect.ValueOf(tmpdv.Interface()))
return nil
default:
return scope.errorf("couldn't copy '%v' into '%v'; didn't understand types", st, dt)
}
return nil
}
var stringType = reflect.TypeOf("")
func toKVValue(v reflect.Value) kvValue {
switch v.Kind() {
case reflect.Struct:
return structAdaptor(v)
case reflect.Map:
if v.Type().Key().AssignableTo(stringType) {
return stringMapAdaptor(v)
}
}
return nil
}
// kvValue lets us write the same conversion logic to work with both maps
// and structs. Only maps with string keys make sense for this.
type kvValue interface {
// returns all keys, as a []string.
keys() []string
// Will just return "" for maps.
tagOf(key string) reflect.StructTag
// Will return the zero Value if the key doesn't exist.
value(key string) reflect.Value
// Maps require explicit setting-- will do nothing for structs.
// Returns false on failure.
confirmSet(key string, v reflect.Value) bool
}
type stringMapAdaptor reflect.Value
func (a stringMapAdaptor) len() int {
return reflect.Value(a).Len()
}
func (a stringMapAdaptor) keys() []string {
v := reflect.Value(a)
keys := make([]string, v.Len())
for i, v := range v.MapKeys() {
if v.IsNil() {
continue
}
switch t := v.Interface().(type) {
case string:
keys[i] = t
}
}
return keys
}
func (a stringMapAdaptor) tagOf(key string) reflect.StructTag {
return ""
}
func (a stringMapAdaptor) value(key string) reflect.Value {
return reflect.Value(a).MapIndex(reflect.ValueOf(key))
}
func (a stringMapAdaptor) confirmSet(key string, v reflect.Value) bool {
return true
}
type structAdaptor reflect.Value
func (a structAdaptor) len() int {
v := reflect.Value(a)
return v.Type().NumField()
}
func (a structAdaptor) keys() []string {
v := reflect.Value(a)
t := v.Type()
keys := make([]string, t.NumField())
for i := range keys {
keys[i] = t.Field(i).Name
}
return keys
}
func (a structAdaptor) tagOf(key string) reflect.StructTag {
v := reflect.Value(a)
field, ok := v.Type().FieldByName(key)
if ok {
return field.Tag
}
return ""
}
func (a structAdaptor) value(key string) reflect.Value {
v := reflect.Value(a)
return v.FieldByName(key)
}
func (a structAdaptor) confirmSet(key string, v reflect.Value) bool {
return true
}
// convertKV can convert things that consist of key/value pairs, like structs
// and some maps.
func (c *Converter) convertKV(skv, dkv kvValue, scope *scope) error {
if skv == nil || dkv == nil {
// TODO: add keys to stack to support really understandable error messages.
return fmt.Errorf("Unable to convert %#v to %#v", skv, dkv)
}
lister := dkv
if scope.flags.IsSet(SourceToDest) {
lister = skv
}
var mapping FieldMappingFunc
if scope.meta != nil && scope.meta.KeyNameMapping != nil {
mapping = scope.meta.KeyNameMapping
}
for _, key := range lister.keys() {
if found, err := c.checkField(key, skv, dkv, scope); found {
if err != nil {
return err
}
continue
}
stag := skv.tagOf(key)
dtag := dkv.tagOf(key)
skey := key
dkey := key
if mapping != nil {
skey, dkey = scope.meta.KeyNameMapping(key, stag, dtag)
}
df := dkv.value(dkey)
sf := skv.value(skey)
if !df.IsValid() || !sf.IsValid() {
switch {
case scope.flags.IsSet(IgnoreMissingFields):
// No error.
case scope.flags.IsSet(SourceToDest):
return scope.errorf("%v not present in dest", dkey)
default:
return scope.errorf("%v not present in src", skey)
}
continue
}
scope.srcStack.top().key = skey
scope.srcStack.top().tag = stag
scope.destStack.top().key = dkey
scope.destStack.top().tag = dtag
if err := c.convert(sf, df, scope); err != nil {
return err
}
}
return nil
}
// checkField returns true if the field name matches any of the struct
// field copying rules. The error should be ignored if it returns false.
func (c *Converter) checkField(fieldName string, skv, dkv kvValue, scope *scope) (bool, error) {
replacementMade := false
if scope.flags.IsSet(DestFromSource) {
df := dkv.value(fieldName)
if !df.IsValid() {
return false, nil
}
destKey := typeNamePair{df.Type(), fieldName}
// Check each of the potential source (type, name) pairs to see if they're
// present in sv.
for _, potentialSourceKey := range c.structFieldSources[destKey] {
sf := skv.value(potentialSourceKey.fieldName)
if !sf.IsValid() {
continue
}
if sf.Type() == potentialSourceKey.fieldType {
// Both the source's name and type matched, so copy.
scope.srcStack.top().key = potentialSourceKey.fieldName
scope.destStack.top().key = fieldName
if err := c.convert(sf, df, scope); err != nil {
return true, err
}
dkv.confirmSet(fieldName, df)
replacementMade = true
}
}
return replacementMade, nil
}
sf := skv.value(fieldName)
if !sf.IsValid() {
return false, nil
}
srcKey := typeNamePair{sf.Type(), fieldName}
// Check each of the potential dest (type, name) pairs to see if they're
// present in dv.
for _, potentialDestKey := range c.structFieldDests[srcKey] {
df := dkv.value(potentialDestKey.fieldName)
if !df.IsValid() {
continue
}
if df.Type() == potentialDestKey.fieldType {
// Both the dest's name and type matched, so copy.
scope.srcStack.top().key = fieldName
scope.destStack.top().key = potentialDestKey.fieldName
if err := c.convert(sf, df, scope); err != nil {
return true, err
}
dkv.confirmSet(potentialDestKey.fieldName, df)
replacementMade = true
}
}
return replacementMade, nil
}

View file

@ -0,0 +1,826 @@
/*
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 conversion
import (
"fmt"
"reflect"
"strconv"
"strings"
"testing"
"github.com/google/gofuzz"
flag "github.com/spf13/pflag"
"k8s.io/apimachinery/pkg/util/diff"
)
var fuzzIters = flag.Int("fuzz-iters", 50, "How many fuzzing iterations to do.")
// Test a weird version/kind embedding format.
type MyWeirdCustomEmbeddedVersionKindField struct {
ID string `json:"ID,omitempty"`
APIVersion string `json:"myVersionKey,omitempty"`
ObjectKind string `json:"myKindKey,omitempty"`
Z string `json:"Z,omitempty"`
Y uint64 `json:"Y,omitempty"`
}
type TestType1 struct {
MyWeirdCustomEmbeddedVersionKindField `json:",inline"`
A string `json:"A,omitempty"`
B int `json:"B,omitempty"`
C int8 `json:"C,omitempty"`
D int16 `json:"D,omitempty"`
E int32 `json:"E,omitempty"`
F int64 `json:"F,omitempty"`
G uint `json:"G,omitempty"`
H uint8 `json:"H,omitempty"`
I uint16 `json:"I,omitempty"`
J uint32 `json:"J,omitempty"`
K uint64 `json:"K,omitempty"`
L bool `json:"L,omitempty"`
M map[string]int `json:"M,omitempty"`
N map[string]TestType2 `json:"N,omitempty"`
O *TestType2 `json:"O,omitempty"`
P []TestType2 `json:"Q,omitempty"`
}
type TestType2 struct {
A string `json:"A,omitempty"`
B int `json:"B,omitempty"`
}
type ExternalTestType2 struct {
A string `json:"A,omitempty"`
B int `json:"B,omitempty"`
}
type ExternalTestType1 struct {
MyWeirdCustomEmbeddedVersionKindField `json:",inline"`
A string `json:"A,omitempty"`
B int `json:"B,omitempty"`
C int8 `json:"C,omitempty"`
D int16 `json:"D,omitempty"`
E int32 `json:"E,omitempty"`
F int64 `json:"F,omitempty"`
G uint `json:"G,omitempty"`
H uint8 `json:"H,omitempty"`
I uint16 `json:"I,omitempty"`
J uint32 `json:"J,omitempty"`
K uint64 `json:"K,omitempty"`
L bool `json:"L,omitempty"`
M map[string]int `json:"M,omitempty"`
N map[string]ExternalTestType2 `json:"N,omitempty"`
O *ExternalTestType2 `json:"O,omitempty"`
P []ExternalTestType2 `json:"Q,omitempty"`
}
func testLogger(t *testing.T) DebugLogger {
// We don't set logger to eliminate rubbish logs in tests.
// If you want to switch it, simply switch it to: "return t"
return nil
}
func TestConverter_byteSlice(t *testing.T) {
c := NewConverter(DefaultNameFunc)
src := []byte{1, 2, 3}
dest := []byte{}
err := c.Convert(&src, &dest, 0, nil)
if err != nil {
t.Fatalf("expected no error")
}
if e, a := src, dest; !reflect.DeepEqual(e, a) {
t.Errorf("expected %#v, got %#v", e, a)
}
}
func TestConverter_MismatchedTypes(t *testing.T) {
c := NewConverter(DefaultNameFunc)
err := c.RegisterConversionFunc(
func(in *[]string, out *int, s Scope) error {
if str, err := strconv.Atoi((*in)[0]); err != nil {
return err
} else {
*out = str
return nil
}
},
)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
src := []string{"5"}
var dest *int
err = c.Convert(&src, &dest, 0, nil)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if e, a := 5, *dest; e != a {
t.Errorf("expected %#v, got %#v", e, a)
}
}
func TestConverter_DefaultConvert(t *testing.T) {
type A struct {
Foo string
Baz int
}
type B struct {
Bar string
Baz int
}
c := NewConverter(DefaultNameFunc)
c.Debug = testLogger(t)
c.nameFunc = func(t reflect.Type) string { return "MyType" }
// Ensure conversion funcs can call DefaultConvert to get default behavior,
// then fixup remaining fields manually
err := c.RegisterConversionFunc(func(in *A, out *B, s Scope) error {
if err := s.DefaultConvert(in, out, IgnoreMissingFields); err != nil {
return err
}
out.Bar = in.Foo
return nil
})
if err != nil {
t.Fatalf("unexpected error %v", err)
}
x := A{"hello, intrepid test reader!", 3}
y := B{}
err = c.Convert(&x, &y, 0, nil)
if err != nil {
t.Fatalf("unexpected error %v", err)
}
if e, a := x.Foo, y.Bar; e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := x.Baz, y.Baz; e != a {
t.Errorf("expected %v, got %v", e, a)
}
}
func TestConverter_DeepCopy(t *testing.T) {
type A struct {
Foo *string
Bar []string
Baz interface{}
Qux map[string]string
}
c := NewConverter(DefaultNameFunc)
c.Debug = testLogger(t)
foo, baz := "foo", "baz"
x := A{
Foo: &foo,
Bar: []string{"bar"},
Baz: &baz,
Qux: map[string]string{"qux": "qux"},
}
y := A{}
if err := c.Convert(&x, &y, 0, nil); err != nil {
t.Fatalf("unexpected error %v", err)
}
*x.Foo = "foo2"
x.Bar[0] = "bar2"
*x.Baz.(*string) = "baz2"
x.Qux["qux"] = "qux2"
if e, a := *x.Foo, *y.Foo; e == a {
t.Errorf("expected difference between %v and %v", e, a)
}
if e, a := x.Bar, y.Bar; reflect.DeepEqual(e, a) {
t.Errorf("expected difference between %v and %v", e, a)
}
if e, a := *x.Baz.(*string), *y.Baz.(*string); e == a {
t.Errorf("expected difference between %v and %v", e, a)
}
if e, a := x.Qux, y.Qux; reflect.DeepEqual(e, a) {
t.Errorf("expected difference between %v and %v", e, a)
}
}
func TestConverter_CallsRegisteredFunctions(t *testing.T) {
type A struct {
Foo string
Baz int
}
type B struct {
Bar string
Baz int
}
type C struct{}
c := NewConverter(DefaultNameFunc)
c.Debug = testLogger(t)
err := c.RegisterConversionFunc(func(in *A, out *B, s Scope) error {
out.Bar = in.Foo
return s.Convert(&in.Baz, &out.Baz, 0)
})
if err != nil {
t.Fatalf("unexpected error %v", err)
}
err = c.RegisterConversionFunc(func(in *B, out *A, s Scope) error {
out.Foo = in.Bar
return s.Convert(&in.Baz, &out.Baz, 0)
})
if err != nil {
t.Fatalf("unexpected error %v", err)
}
x := A{"hello, intrepid test reader!", 3}
y := B{}
err = c.Convert(&x, &y, 0, nil)
if err != nil {
t.Fatalf("unexpected error %v", err)
}
if e, a := x.Foo, y.Bar; e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := x.Baz, y.Baz; e != a {
t.Errorf("expected %v, got %v", e, a)
}
z := B{"all your test are belong to us", 42}
w := A{}
err = c.Convert(&z, &w, 0, nil)
if err != nil {
t.Fatalf("unexpected error %v", err)
}
if e, a := z.Bar, w.Foo; e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := z.Baz, w.Baz; e != a {
t.Errorf("expected %v, got %v", e, a)
}
err = c.RegisterConversionFunc(func(in *A, out *C, s Scope) error {
return fmt.Errorf("C can't store an A, silly")
})
if err != nil {
t.Fatalf("unexpected error %v", err)
}
err = c.Convert(&A{}, &C{}, 0, nil)
if err == nil {
t.Errorf("unexpected non-error")
}
}
func TestConverter_IgnoredConversion(t *testing.T) {
type A struct{}
type B struct{}
count := 0
c := NewConverter(DefaultNameFunc)
if err := c.RegisterConversionFunc(func(in *A, out *B, s Scope) error {
count++
return nil
}); err != nil {
t.Fatalf("unexpected error %v", err)
}
if err := c.RegisterIgnoredConversion(&A{}, &B{}); err != nil {
t.Fatal(err)
}
a := A{}
b := B{}
if err := c.Convert(&a, &b, 0, nil); err != nil {
t.Errorf("%v", err)
}
if count != 0 {
t.Errorf("unexpected number of conversion invocations")
}
}
func TestConverter_IgnoredConversionNested(t *testing.T) {
type C string
type A struct {
C C
}
type B struct {
C C
}
c := NewConverter(DefaultNameFunc)
typed := C("")
if err := c.RegisterIgnoredConversion(&typed, &typed); err != nil {
t.Fatal(err)
}
a := A{C: C("test")}
b := B{C: C("other")}
if err := c.Convert(&a, &b, AllowDifferentFieldTypeNames, nil); err != nil {
t.Errorf("%v", err)
}
if b.C != C("other") {
t.Errorf("expected no conversion of field C: %#v", b)
}
}
func TestConverter_GeneratedConversionOverriden(t *testing.T) {
type A struct{}
type B struct{}
c := NewConverter(DefaultNameFunc)
if err := c.RegisterConversionFunc(func(in *A, out *B, s Scope) error {
return nil
}); err != nil {
t.Fatalf("unexpected error %v", err)
}
if err := c.RegisterGeneratedConversionFunc(func(in *A, out *B, s Scope) error {
return fmt.Errorf("generated function should be overriden")
}); err != nil {
t.Fatalf("unexpected error %v", err)
}
a := A{}
b := B{}
if err := c.Convert(&a, &b, 0, nil); err != nil {
t.Errorf("%v", err)
}
}
func TestConverter_WithConversionOverriden(t *testing.T) {
type A struct{}
type B struct{}
c := NewConverter(DefaultNameFunc)
if err := c.RegisterConversionFunc(func(in *A, out *B, s Scope) error {
return fmt.Errorf("conversion function should be overriden")
}); err != nil {
t.Fatalf("unexpected error %v", err)
}
if err := c.RegisterGeneratedConversionFunc(func(in *A, out *B, s Scope) error {
return fmt.Errorf("generated function should be overriden")
}); err != nil {
t.Fatalf("unexpected error %v", err)
}
ext := NewConversionFuncs()
ext.Add(func(in *A, out *B, s Scope) error {
return nil
})
newc := c.WithConversions(ext)
a := A{}
b := B{}
if err := c.Convert(&a, &b, 0, nil); err == nil || err.Error() != "conversion function should be overriden" {
t.Errorf("unexpected error: %v", err)
}
if err := newc.Convert(&a, &b, 0, nil); err != nil {
t.Errorf("%v", err)
}
}
func TestConverter_MapsStringArrays(t *testing.T) {
type A struct {
Foo string
Baz int
Other string
}
c := NewConverter(DefaultNameFunc)
c.Debug = testLogger(t)
if err := c.RegisterConversionFunc(func(input *[]string, out *string, s Scope) error {
if len(*input) == 0 {
*out = ""
}
*out = (*input)[0]
return nil
}); err != nil {
t.Fatalf("unexpected error %v", err)
}
x := map[string][]string{
"Foo": {"bar"},
"Baz": {"1"},
"Other": {"", "test"},
"other": {"wrong"},
}
y := A{"test", 2, "something"}
if err := c.Convert(&x, &y, AllowDifferentFieldTypeNames, nil); err == nil {
t.Error("unexpected non-error")
}
if err := c.RegisterConversionFunc(func(input *[]string, out *int, s Scope) error {
if len(*input) == 0 {
*out = 0
}
str := (*input)[0]
i, err := strconv.Atoi(str)
if err != nil {
return err
}
*out = i
return nil
}); err != nil {
t.Fatalf("unexpected error %v", err)
}
if err := c.Convert(&x, &y, AllowDifferentFieldTypeNames, nil); err != nil {
t.Fatalf("unexpected error %v", err)
}
if !reflect.DeepEqual(y, A{"bar", 1, ""}) {
t.Errorf("unexpected result: %#v", y)
}
}
func TestConverter_MapsStringArraysWithMappingKey(t *testing.T) {
type A struct {
Foo string `json:"test"`
Baz int
Other string
}
c := NewConverter(DefaultNameFunc)
c.Debug = testLogger(t)
if err := c.RegisterConversionFunc(func(input *[]string, out *string, s Scope) error {
if len(*input) == 0 {
*out = ""
}
*out = (*input)[0]
return nil
}); err != nil {
t.Fatalf("unexpected error %v", err)
}
x := map[string][]string{
"Foo": {"bar"},
"test": {"baz"},
}
y := A{"", 0, ""}
if err := c.Convert(&x, &y, AllowDifferentFieldTypeNames|IgnoreMissingFields, &Meta{}); err != nil {
t.Fatalf("unexpected error %v", err)
}
if !reflect.DeepEqual(y, A{"bar", 0, ""}) {
t.Errorf("unexpected result: %#v", y)
}
mapping := func(key string, sourceTag, destTag reflect.StructTag) (source string, dest string) {
if s := destTag.Get("json"); len(s) > 0 {
return strings.SplitN(s, ",", 2)[0], key
}
return key, key
}
if err := c.Convert(&x, &y, AllowDifferentFieldTypeNames|IgnoreMissingFields, &Meta{KeyNameMapping: mapping}); err != nil {
t.Fatalf("unexpected error %v", err)
}
if !reflect.DeepEqual(y, A{"baz", 0, ""}) {
t.Errorf("unexpected result: %#v", y)
}
}
func TestConverter_fuzz(t *testing.T) {
// Use the same types from the scheme test.
table := []struct {
from, to, check interface{}
}{
{&TestType1{}, &ExternalTestType1{}, &TestType1{}},
{&ExternalTestType1{}, &TestType1{}, &ExternalTestType1{}},
}
f := fuzz.New().NilChance(.5).NumElements(0, 100)
c := NewConverter(DefaultNameFunc)
c.nameFunc = func(t reflect.Type) string {
// Hide the fact that we don't have separate packages for these things.
return map[reflect.Type]string{
reflect.TypeOf(TestType1{}): "TestType1",
reflect.TypeOf(ExternalTestType1{}): "TestType1",
reflect.TypeOf(TestType2{}): "TestType2",
reflect.TypeOf(ExternalTestType2{}): "TestType2",
}[t]
}
c.Debug = testLogger(t)
for i, item := range table {
for j := 0; j < *fuzzIters; j++ {
f.Fuzz(item.from)
err := c.Convert(item.from, item.to, 0, nil)
if err != nil {
t.Errorf("(%v, %v): unexpected error: %v", i, j, err)
continue
}
err = c.Convert(item.to, item.check, 0, nil)
if err != nil {
t.Errorf("(%v, %v): unexpected error: %v", i, j, err)
continue
}
if e, a := item.from, item.check; !reflect.DeepEqual(e, a) {
t.Errorf("(%v, %v): unexpected diff: %v", i, j, diff.ObjectDiff(e, a))
}
}
}
}
func TestConverter_MapElemAddr(t *testing.T) {
type Foo struct {
A map[int]int
}
type Bar struct {
A map[string]string
}
c := NewConverter(DefaultNameFunc)
c.Debug = testLogger(t)
err := c.RegisterConversionFunc(
func(in *int, out *string, s Scope) error {
*out = fmt.Sprintf("%v", *in)
return nil
},
)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
err = c.RegisterConversionFunc(
func(in *string, out *int, s Scope) error {
if str, err := strconv.Atoi(*in); err != nil {
return err
} else {
*out = str
return nil
}
},
)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
f := fuzz.New().NilChance(0).NumElements(3, 3)
first := Foo{}
second := Bar{}
f.Fuzz(&first)
err = c.Convert(&first, &second, AllowDifferentFieldTypeNames, nil)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
third := Foo{}
err = c.Convert(&second, &third, AllowDifferentFieldTypeNames, nil)
if e, a := first, third; !reflect.DeepEqual(e, a) {
t.Errorf("Unexpected diff: %v", diff.ObjectDiff(e, a))
}
}
func TestConverter_tags(t *testing.T) {
type Foo struct {
A string `test:"foo"`
}
type Bar struct {
A string `test:"bar"`
}
c := NewConverter(DefaultNameFunc)
c.Debug = testLogger(t)
err := c.RegisterConversionFunc(
func(in *string, out *string, s Scope) error {
if e, a := "foo", s.SrcTag().Get("test"); e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := "bar", s.DestTag().Get("test"); e != a {
t.Errorf("expected %v, got %v", e, a)
}
return nil
},
)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
err = c.Convert(&Foo{}, &Bar{}, AllowDifferentFieldTypeNames, nil)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
}
func TestConverter_meta(t *testing.T) {
type Foo struct{ A string }
type Bar struct{ A string }
c := NewConverter(DefaultNameFunc)
c.Debug = testLogger(t)
checks := 0
err := c.RegisterConversionFunc(
func(in *Foo, out *Bar, s Scope) error {
if s.Meta() == nil {
t.Errorf("Meta did not get passed!")
}
checks++
s.Convert(&in.A, &out.A, 0)
return nil
},
)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
err = c.RegisterConversionFunc(
func(in *string, out *string, s Scope) error {
if s.Meta() == nil {
t.Errorf("Meta did not get passed a second time!")
}
checks++
return nil
},
)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
err = c.Convert(&Foo{}, &Bar{}, 0, &Meta{})
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
if checks != 2 {
t.Errorf("Registered functions did not get called.")
}
}
func TestConverter_flags(t *testing.T) {
type Foo struct{ A string }
type Bar struct{ A string }
table := []struct {
from, to interface{}
flags FieldMatchingFlags
shouldSucceed bool
}{
// Check that DestFromSource allows extra fields only in source.
{
from: &struct{ A string }{},
to: &struct{ A, B string }{},
flags: DestFromSource,
shouldSucceed: false,
}, {
from: &struct{ A, B string }{},
to: &struct{ A string }{},
flags: DestFromSource,
shouldSucceed: true,
},
// Check that SourceToDest allows for extra fields only in dest.
{
from: &struct{ A string }{},
to: &struct{ A, B string }{},
flags: SourceToDest,
shouldSucceed: true,
}, {
from: &struct{ A, B string }{},
to: &struct{ A string }{},
flags: SourceToDest,
shouldSucceed: false,
},
// Check that IgnoreMissingFields makes the above failure cases pass.
{
from: &struct{ A string }{},
to: &struct{ A, B string }{},
flags: DestFromSource | IgnoreMissingFields,
shouldSucceed: true,
}, {
from: &struct{ A, B string }{},
to: &struct{ A string }{},
flags: SourceToDest | IgnoreMissingFields,
shouldSucceed: true,
},
// Check that the field type name must match unless
// AllowDifferentFieldTypeNames is specified.
{
from: &struct{ A, B Foo }{},
to: &struct{ A Bar }{},
flags: DestFromSource,
shouldSucceed: false,
}, {
from: &struct{ A Foo }{},
to: &struct{ A, B Bar }{},
flags: SourceToDest,
shouldSucceed: false,
}, {
from: &struct{ A, B Foo }{},
to: &struct{ A Bar }{},
flags: DestFromSource | AllowDifferentFieldTypeNames,
shouldSucceed: true,
}, {
from: &struct{ A Foo }{},
to: &struct{ A, B Bar }{},
flags: SourceToDest | AllowDifferentFieldTypeNames,
shouldSucceed: true,
},
}
f := fuzz.New().NilChance(.5).NumElements(0, 100)
c := NewConverter(DefaultNameFunc)
c.Debug = testLogger(t)
for i, item := range table {
for j := 0; j < *fuzzIters; j++ {
f.Fuzz(item.from)
err := c.Convert(item.from, item.to, item.flags, nil)
if item.shouldSucceed && err != nil {
t.Errorf("(%v, %v): unexpected error: %v", i, j, err)
continue
}
if !item.shouldSucceed && err == nil {
t.Errorf("(%v, %v): unexpected non-error", i, j)
continue
}
}
}
}
func TestConverter_FieldRename(t *testing.T) {
type WeirdMeta struct {
Name string
Type string
}
type NameMeta struct {
Name string
}
type TypeMeta struct {
Type string
}
type A struct {
WeirdMeta
}
type B struct {
TypeMeta
NameMeta
}
c := NewConverter(DefaultNameFunc)
err := c.SetStructFieldCopy(WeirdMeta{}, "WeirdMeta", TypeMeta{}, "TypeMeta")
if err != nil {
t.Fatalf("unexpected error %v", err)
}
err = c.SetStructFieldCopy(WeirdMeta{}, "WeirdMeta", NameMeta{}, "NameMeta")
if err != nil {
t.Fatalf("unexpected error %v", err)
}
err = c.SetStructFieldCopy(TypeMeta{}, "TypeMeta", WeirdMeta{}, "WeirdMeta")
if err != nil {
t.Fatalf("unexpected error %v", err)
}
err = c.SetStructFieldCopy(NameMeta{}, "NameMeta", WeirdMeta{}, "WeirdMeta")
if err != nil {
t.Fatalf("unexpected error %v", err)
}
c.Debug = testLogger(t)
aVal := &A{
WeirdMeta: WeirdMeta{
Name: "Foo",
Type: "Bar",
},
}
bVal := &B{
TypeMeta: TypeMeta{"Bar"},
NameMeta: NameMeta{"Foo"},
}
table := map[string]struct {
from, to, expect interface{}
flags FieldMatchingFlags
}{
"to": {
aVal,
&B{},
bVal,
AllowDifferentFieldTypeNames | SourceToDest | IgnoreMissingFields,
},
"from": {
bVal,
&A{},
aVal,
AllowDifferentFieldTypeNames | SourceToDest,
},
"toDestFirst": {
aVal,
&B{},
bVal,
AllowDifferentFieldTypeNames,
},
"fromDestFirst": {
bVal,
&A{},
aVal,
AllowDifferentFieldTypeNames | IgnoreMissingFields,
},
}
for name, item := range table {
err := c.Convert(item.from, item.to, item.flags, nil)
if err != nil {
t.Errorf("%v: unexpected error: %v", name, err)
continue
}
if e, a := item.expect, item.to; !reflect.DeepEqual(e, a) {
t.Errorf("%v: unexpected diff: %v", name, diff.ObjectDiff(e, a))
}
}
}

View file

@ -0,0 +1,161 @@
/*
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 conversion
import (
"math/rand"
"reflect"
"testing"
"github.com/google/gofuzz"
)
func TestDeepCopy(t *testing.T) {
semantic := EqualitiesOrDie()
f := fuzz.New().NilChance(.5).NumElements(0, 100)
table := []interface{}{
map[string]string{},
int(5),
"hello world",
struct {
A, B, C struct {
D map[string]int
}
X []int
Y []byte
}{},
}
for _, obj := range table {
obj2, err := NewCloner().DeepCopy(obj)
if err != nil {
t.Errorf("Error: couldn't copy %#v", obj)
continue
}
if e, a := obj, obj2; !semantic.DeepEqual(e, a) {
t.Errorf("expected %#v\ngot %#v", e, a)
}
obj3 := reflect.New(reflect.TypeOf(obj)).Interface()
f.Fuzz(obj3)
obj4, err := NewCloner().DeepCopy(obj3)
if err != nil {
t.Errorf("Error: couldn't copy %#v", obj)
continue
}
if e, a := obj3, obj4; !semantic.DeepEqual(e, a) {
t.Errorf("expected %#v\ngot %#v", e, a)
}
f.Fuzz(obj3)
}
}
func copyOrDie(t *testing.T, in interface{}) interface{} {
out, err := NewCloner().DeepCopy(in)
if err != nil {
t.Fatalf("DeepCopy failed: %#q: %v", in, err)
}
return out
}
func TestDeepCopySliceSeparate(t *testing.T) {
x := []int{5}
y := copyOrDie(t, x).([]int)
x[0] = 3
if y[0] == 3 {
t.Errorf("deep copy wasn't deep: %#q %#q", x, y)
}
}
func TestDeepCopyArraySeparate(t *testing.T) {
x := [1]int{5}
y := copyOrDie(t, x).([1]int)
x[0] = 3
if y[0] == 3 {
t.Errorf("deep copy wasn't deep: %#q %#q", x, y)
}
}
func TestDeepCopyMapSeparate(t *testing.T) {
x := map[string]int{"foo": 5}
y := copyOrDie(t, x).(map[string]int)
x["foo"] = 3
if y["foo"] == 3 {
t.Errorf("deep copy wasn't deep: %#q %#q", x, y)
}
}
func TestDeepCopyPointerSeparate(t *testing.T) {
z := 5
x := &z
y := copyOrDie(t, x).(*int)
*x = 3
if *y == 3 {
t.Errorf("deep copy wasn't deep: %#q %#q", x, y)
}
}
func TestDeepCopyStruct(t *testing.T) {
type Foo struct {
A int
}
type Bar struct {
Foo
F *Foo
}
a := &Bar{Foo{1}, &Foo{2}}
b := copyOrDie(t, a).(*Bar)
a.A = 3
a.F.A = 4
if b.A != 1 || b.F.A != 2 {
t.Errorf("deep copy wasn't deep: %#v, %#v", a, b)
}
}
var result interface{}
func BenchmarkDeepCopy(b *testing.B) {
table := []interface{}{
map[string]string{},
int(5),
"hello world",
struct {
A, B, C struct {
D map[string]int
}
X []int
Y []byte
}{},
}
f := fuzz.New().RandSource(rand.NewSource(1)).NilChance(.5).NumElements(0, 100)
for i := range table {
out := table[i]
obj := reflect.New(reflect.TypeOf(out)).Interface()
f.Fuzz(obj)
table[i] = obj
}
b.ResetTimer()
var r interface{}
for i := 0; i < b.N; i++ {
for j := range table {
r, _ = NewCloner().DeepCopy(table[j])
}
}
result = r
}

View file

@ -0,0 +1,36 @@
/*
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 conversion
import (
"k8s.io/apimachinery/third_party/forked/golang/reflect"
)
// The code for this type must be located in third_party, since it forks from
// go std lib. But for convenience, we expose the type here, too.
type Equalities struct {
reflect.Equalities
}
// For convenience, panics on errors
func EqualitiesOrDie(funcs ...interface{}) Equalities {
e := Equalities{reflect.Equalities{}}
if err := e.AddFuncs(funcs...); err != nil {
panic(err)
}
return e
}

24
vendor/k8s.io/apimachinery/pkg/conversion/doc.go generated vendored Normal file
View file

@ -0,0 +1,24 @@
/*
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 conversion provides go object versioning.
//
// Specifically, conversion provides a way for you to define multiple versions
// of the same object. You may write functions which implement conversion logic,
// but for the fields which did not change, copying is automated. This makes it
// easy to modify the structures you use in memory without affecting the format
// you store on disk or respond to in your external API calls.
package conversion // import "k8s.io/apimachinery/pkg/conversion"

39
vendor/k8s.io/apimachinery/pkg/conversion/helper.go generated vendored Normal file
View file

@ -0,0 +1,39 @@
/*
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 conversion
import (
"fmt"
"reflect"
)
// EnforcePtr ensures that obj is a pointer of some sort. Returns a reflect.Value
// of the dereferenced pointer, ensuring that it is settable/addressable.
// Returns an error if this is not possible.
func EnforcePtr(obj interface{}) (reflect.Value, error) {
v := reflect.ValueOf(obj)
if v.Kind() != reflect.Ptr {
if v.Kind() == reflect.Invalid {
return reflect.Value{}, fmt.Errorf("expected pointer, but got invalid kind")
}
return reflect.Value{}, fmt.Errorf("expected pointer, but got %v type", v.Type())
}
if v.IsNil() {
return reflect.Value{}, fmt.Errorf("expected pointer, but got nil")
}
return v.Elem(), nil
}

View file

@ -0,0 +1,38 @@
/*
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 conversion
import "testing"
func TestInvalidPtrValueKind(t *testing.T) {
var simple interface{}
switch obj := simple.(type) {
default:
_, err := EnforcePtr(obj)
if err == nil {
t.Errorf("Expected error on invalid kind")
}
}
}
func TestEnforceNilPtr(t *testing.T) {
var nilPtr *struct{}
_, err := EnforcePtr(nilPtr)
if err == nil {
t.Errorf("Expected error on nil pointer")
}
}

View file

@ -0,0 +1,188 @@
/*
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 queryparams
import (
"fmt"
"net/url"
"reflect"
"strings"
)
// Marshaler converts an object to a query parameter string representation
type Marshaler interface {
MarshalQueryParameter() (string, error)
}
// Unmarshaler converts a string representation to an object
type Unmarshaler interface {
UnmarshalQueryParameter(string) error
}
func jsonTag(field reflect.StructField) (string, bool) {
structTag := field.Tag.Get("json")
if len(structTag) == 0 {
return "", false
}
parts := strings.Split(structTag, ",")
tag := parts[0]
if tag == "-" {
tag = ""
}
omitempty := false
parts = parts[1:]
for _, part := range parts {
if part == "omitempty" {
omitempty = true
break
}
}
return tag, omitempty
}
func formatValue(value interface{}) string {
return fmt.Sprintf("%v", value)
}
func isPointerKind(kind reflect.Kind) bool {
return kind == reflect.Ptr
}
func isStructKind(kind reflect.Kind) bool {
return kind == reflect.Struct
}
func isValueKind(kind reflect.Kind) bool {
switch kind {
case reflect.String, reflect.Bool, reflect.Int, reflect.Int8, reflect.Int16,
reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8,
reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Float32,
reflect.Float64, reflect.Complex64, reflect.Complex128:
return true
default:
return false
}
}
func zeroValue(value reflect.Value) bool {
return reflect.DeepEqual(reflect.Zero(value.Type()).Interface(), value.Interface())
}
func customMarshalValue(value reflect.Value) (reflect.Value, bool) {
// Return unless we implement a custom query marshaler
if !value.CanInterface() {
return reflect.Value{}, false
}
marshaler, ok := value.Interface().(Marshaler)
if !ok {
return reflect.Value{}, false
}
// Don't invoke functions on nil pointers
// If the type implements MarshalQueryParameter, AND the tag is not omitempty, AND the value is a nil pointer, "" seems like a reasonable response
if isPointerKind(value.Kind()) && zeroValue(value) {
return reflect.ValueOf(""), true
}
// Get the custom marshalled value
v, err := marshaler.MarshalQueryParameter()
if err != nil {
return reflect.Value{}, false
}
return reflect.ValueOf(v), true
}
func addParam(values url.Values, tag string, omitempty bool, value reflect.Value) {
if omitempty && zeroValue(value) {
return
}
val := ""
iValue := fmt.Sprintf("%v", value.Interface())
if iValue != "<nil>" {
val = iValue
}
values.Add(tag, val)
}
func addListOfParams(values url.Values, tag string, omitempty bool, list reflect.Value) {
for i := 0; i < list.Len(); i++ {
addParam(values, tag, omitempty, list.Index(i))
}
}
// Convert takes an object and converts it to a url.Values object using JSON tags as
// parameter names. Only top-level simple values, arrays, and slices are serialized.
// Embedded structs, maps, etc. will not be serialized.
func Convert(obj interface{}) (url.Values, error) {
result := url.Values{}
if obj == nil {
return result, nil
}
var sv reflect.Value
switch reflect.TypeOf(obj).Kind() {
case reflect.Ptr, reflect.Interface:
sv = reflect.ValueOf(obj).Elem()
default:
return nil, fmt.Errorf("expecting a pointer or interface")
}
st := sv.Type()
if !isStructKind(st.Kind()) {
return nil, fmt.Errorf("expecting a pointer to a struct")
}
// Check all object fields
convertStruct(result, st, sv)
return result, nil
}
func convertStruct(result url.Values, st reflect.Type, sv reflect.Value) {
for i := 0; i < st.NumField(); i++ {
field := sv.Field(i)
tag, omitempty := jsonTag(st.Field(i))
if len(tag) == 0 {
continue
}
ft := field.Type()
kind := ft.Kind()
if isPointerKind(kind) {
ft = ft.Elem()
kind = ft.Kind()
if !field.IsNil() {
field = reflect.Indirect(field)
}
}
switch {
case isValueKind(kind):
addParam(result, tag, omitempty, field)
case kind == reflect.Array || kind == reflect.Slice:
if isValueKind(ft.Elem().Kind()) {
addListOfParams(result, tag, omitempty, field)
}
case isStructKind(kind) && !(zeroValue(field) && omitempty):
if marshalValue, ok := customMarshalValue(field); ok {
addParam(result, tag, omitempty, marshalValue)
} else {
convertStruct(result, ft, field)
}
}
}
}

View file

@ -0,0 +1,212 @@
/*
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 queryparams_test
import (
"net/url"
"reflect"
"testing"
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/conversion/queryparams"
"k8s.io/apimachinery/pkg/runtime/schema"
)
type namedString string
type namedBool bool
type bar struct {
Float1 float32 `json:"float1"`
Float2 float64 `json:"float2"`
Int1 int64 `json:"int1,omitempty"`
Int2 int32 `json:"int2,omitempty"`
Int3 int16 `json:"int3,omitempty"`
Str1 string `json:"str1,omitempty"`
Ignored int
Ignored2 string
}
func (obj *bar) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind }
type foo struct {
Str string `json:"str"`
Integer int `json:"integer,omitempty"`
Slice []string `json:"slice,omitempty"`
Boolean bool `json:"boolean,omitempty"`
NamedStr namedString `json:"namedStr,omitempty"`
NamedBool namedBool `json:"namedBool,omitempty"`
Foobar bar `json:"foobar,omitempty"`
Testmap map[string]string `json:"testmap,omitempty"`
}
func (obj *foo) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind }
type baz struct {
Ptr *int `json:"ptr"`
Bptr *bool `json:"bptr,omitempty"`
}
func (obj *baz) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind }
// childStructs tests some of the types we serialize to query params for log API calls
// notably, the nested time struct
type childStructs struct {
Container string `json:"container,omitempty"`
Follow bool `json:"follow,omitempty"`
Previous bool `json:"previous,omitempty"`
SinceSeconds *int64 `json:"sinceSeconds,omitempty"`
SinceTime *metav1.Time `json:"sinceTime,omitempty"`
EmptyTime *metav1.Time `json:"emptyTime"`
}
func (obj *childStructs) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind }
func validateResult(t *testing.T, input interface{}, actual, expected url.Values) {
local := url.Values{}
for k, v := range expected {
local[k] = v
}
for k, v := range actual {
if ev, ok := local[k]; !ok || !reflect.DeepEqual(ev, v) {
if !ok {
t.Errorf("%#v: actual value key %s not found in expected map", input, k)
} else {
t.Errorf("%#v: values don't match: actual: %#v, expected: %#v", input, v, ev)
}
}
delete(local, k)
}
if len(local) > 0 {
t.Errorf("%#v: expected map has keys that were not found in actual map: %#v", input, local)
}
}
func TestConvert(t *testing.T) {
sinceSeconds := int64(123)
sinceTime := metav1.Date(2000, 1, 1, 12, 34, 56, 0, time.UTC)
tests := []struct {
input interface{}
expected url.Values
}{
{
input: &foo{
Str: "hello",
},
expected: url.Values{"str": {"hello"}},
},
{
input: &foo{
Str: "test string",
Slice: []string{"one", "two", "three"},
Integer: 234,
Boolean: true,
},
expected: url.Values{"str": {"test string"}, "slice": {"one", "two", "three"}, "integer": {"234"}, "boolean": {"true"}},
},
{
input: &foo{
Str: "named types",
NamedStr: "value1",
NamedBool: true,
},
expected: url.Values{"str": {"named types"}, "namedStr": {"value1"}, "namedBool": {"true"}},
},
{
input: &foo{
Str: "don't ignore embedded struct",
Foobar: bar{
Float1: 5.0,
},
},
expected: url.Values{"str": {"don't ignore embedded struct"}, "float1": {"5"}, "float2": {"0"}},
},
{
// Ignore untagged fields
input: &bar{
Float1: 23.5,
Float2: 100.7,
Int1: 1,
Int2: 2,
Int3: 3,
Ignored: 1,
Ignored2: "ignored",
},
expected: url.Values{"float1": {"23.5"}, "float2": {"100.7"}, "int1": {"1"}, "int2": {"2"}, "int3": {"3"}},
},
{
// include fields that are not tagged omitempty
input: &foo{
NamedStr: "named str",
},
expected: url.Values{"str": {""}, "namedStr": {"named str"}},
},
{
input: &baz{
Ptr: intp(5),
Bptr: boolp(true),
},
expected: url.Values{"ptr": {"5"}, "bptr": {"true"}},
},
{
input: &baz{
Bptr: boolp(true),
},
expected: url.Values{"ptr": {""}, "bptr": {"true"}},
},
{
input: &baz{
Ptr: intp(5),
},
expected: url.Values{"ptr": {"5"}},
},
{
input: &childStructs{
Container: "mycontainer",
Follow: true,
Previous: true,
SinceSeconds: &sinceSeconds,
SinceTime: &sinceTime, // test a custom marshaller
EmptyTime: nil, // test a nil custom marshaller without omitempty
},
expected: url.Values{"container": {"mycontainer"}, "follow": {"true"}, "previous": {"true"}, "sinceSeconds": {"123"}, "sinceTime": {"2000-01-01T12:34:56Z"}, "emptyTime": {""}},
},
{
input: &childStructs{
Container: "mycontainer",
Follow: true,
Previous: true,
SinceSeconds: &sinceSeconds,
SinceTime: nil, // test a nil custom marshaller with omitempty
},
expected: url.Values{"container": {"mycontainer"}, "follow": {"true"}, "previous": {"true"}, "sinceSeconds": {"123"}, "emptyTime": {""}},
},
}
for _, test := range tests {
result, err := queryparams.Convert(test.input)
if err != nil {
t.Errorf("Unexpected error while converting %#v: %v", test.input, err)
}
validateResult(t, test.input, result, test.expected)
}
}
func intp(n int) *int { return &n }
func boolp(b bool) *bool { return &b }

View file

@ -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 queryparams provides conversion from versioned
// runtime objects to URL query values
package queryparams // import "k8s.io/apimachinery/pkg/conversion/queryparams"

19
vendor/k8s.io/apimachinery/pkg/fields/doc.go generated vendored Normal file
View file

@ -0,0 +1,19 @@
/*
Copyright 2015 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package fields implements a simple field system, parsing and matching
// selectors with sets of fields.
package fields // import "k8s.io/apimachinery/pkg/fields"

62
vendor/k8s.io/apimachinery/pkg/fields/fields.go generated vendored Normal file
View file

@ -0,0 +1,62 @@
/*
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 fields
import (
"sort"
"strings"
)
// Fields allows you to present fields independently from their storage.
type Fields interface {
// Has returns whether the provided field exists.
Has(field string) (exists bool)
// Get returns the value for the provided field.
Get(field string) (value string)
}
// Set is a map of field:value. It implements Fields.
type Set map[string]string
// String returns all fields listed as a human readable string.
// Conveniently, exactly the format that ParseSelector takes.
func (ls Set) String() string {
selector := make([]string, 0, len(ls))
for key, value := range ls {
selector = append(selector, key+"="+value)
}
// Sort for determinism.
sort.StringSlice(selector).Sort()
return strings.Join(selector, ",")
}
// Has returns whether the provided field exists in the map.
func (ls Set) Has(field string) bool {
_, exists := ls[field]
return exists
}
// Get returns the value in the map for the provided field.
func (ls Set) Get(field string) string {
return ls[field]
}
// AsSelector converts fields into a selectors.
func (ls Set) AsSelector() Selector {
return SelectorFromSet(ls)
}

57
vendor/k8s.io/apimachinery/pkg/fields/fields_test.go generated vendored Normal file
View file

@ -0,0 +1,57 @@
/*
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 fields
import (
"testing"
)
func matches(t *testing.T, ls Set, want string) {
if ls.String() != want {
t.Errorf("Expected '%s', but got '%s'", want, ls.String())
}
}
func TestSetString(t *testing.T) {
matches(t, Set{"x": "y"}, "x=y")
matches(t, Set{"foo": "bar"}, "foo=bar")
matches(t, Set{"foo": "bar", "baz": "qup"}, "baz=qup,foo=bar")
}
func TestFieldHas(t *testing.T) {
fieldHasTests := []struct {
Ls Fields
Key string
Has bool
}{
{Set{"x": "y"}, "x", true},
{Set{"x": ""}, "x", true},
{Set{"x": "y"}, "foo", false},
}
for _, lh := range fieldHasTests {
if has := lh.Ls.Has(lh.Key); has != lh.Has {
t.Errorf("%#v.Has(%#v) => %v, expected %v", lh.Ls, lh.Key, has, lh.Has)
}
}
}
func TestFieldGet(t *testing.T) {
ls := Set{"x": "y"}
if ls.Get("x") != "y" {
t.Errorf("Set.Get is broken")
}
}

30
vendor/k8s.io/apimachinery/pkg/fields/requirements.go generated vendored Normal file
View file

@ -0,0 +1,30 @@
/*
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 fields
import "k8s.io/apimachinery/pkg/selection"
// Requirements is AND of all requirements.
type Requirements []Requirement
// Requirement contains a field, a value, and an operator that relates the field and value.
// This is currently for reading internal selection information of field selector.
type Requirement struct {
Operator selection.Operator
Field string
Value string
}

Some files were not shown because too many files have changed in this diff Show more