1
0
Fork 0
forked from barak/tarpoon

Add glide.yaml and vendor deps

This commit is contained in:
Dalton Hubble 2016-12-03 22:43:32 -08:00
parent db918f12ad
commit 5b3d5e81bd
18880 changed files with 5166045 additions and 1 deletions

123
vendor/k8s.io/kubernetes/pkg/api/BUILD generated vendored Normal file
View file

@ -0,0 +1,123 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
"go_test",
"cgo_library",
)
go_library(
name = "go_default_library",
srcs = [
"context.go",
"conversion.go",
"defaults.go",
"doc.go",
"field_constants.go",
"generate.go",
"helpers.go",
"mapper.go",
"meta.go",
"ref.go",
"register.go",
"requestcontext.go",
"resource_helpers.go",
"types.generated.go",
"types.go",
"zz_generated.deepcopy.go",
],
tags = ["automanaged"],
deps = [
"//pkg/api/meta:go_default_library",
"//pkg/api/meta/metatypes:go_default_library",
"//pkg/api/resource:go_default_library",
"//pkg/apis/meta/v1:go_default_library",
"//pkg/auth/user:go_default_library",
"//pkg/conversion:go_default_library",
"//pkg/fields:go_default_library",
"//pkg/labels:go_default_library",
"//pkg/runtime:go_default_library",
"//pkg/runtime/schema:go_default_library",
"//pkg/runtime/serializer:go_default_library",
"//pkg/selection:go_default_library",
"//pkg/types:go_default_library",
"//pkg/util/intstr:go_default_library",
"//pkg/util/labels:go_default_library",
"//pkg/util/rand:go_default_library",
"//pkg/util/sets:go_default_library",
"//pkg/util/uuid:go_default_library",
"//pkg/util/validation/field:go_default_library",
"//vendor:github.com/davecgh/go-spew/spew",
"//vendor:github.com/golang/glog",
"//vendor:github.com/ugorji/go/codec",
"//vendor:golang.org/x/net/context",
],
)
go_test(
name = "go_default_test",
srcs = [
"generate_test.go",
"helpers_test.go",
"ref_test.go",
"resource_helpers_test.go",
],
library = "go_default_library",
tags = ["automanaged"],
deps = [
"//pkg/api/resource:go_default_library",
"//pkg/apis/meta/v1:go_default_library",
"//pkg/labels:go_default_library",
"//pkg/runtime:go_default_library",
"//pkg/runtime/schema:go_default_library",
],
)
go_test(
name = "go_default_xtest",
srcs = [
"context_test.go",
"conversion_test.go",
"copy_test.go",
"deep_copy_test.go",
"defaulting_test.go",
"meta_test.go",
"serialization_proto_test.go",
"serialization_test.go",
],
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/api/meta:go_default_library",
"//pkg/api/meta/metatypes:go_default_library",
"//pkg/api/testapi:go_default_library",
"//pkg/api/testing:go_default_library",
"//pkg/api/v1:go_default_library",
"//pkg/apimachinery/registered:go_default_library",
"//pkg/apis/batch/v2alpha1:go_default_library",
"//pkg/apis/extensions:go_default_library",
"//pkg/apis/extensions/v1beta1:go_default_library",
"//pkg/apis/meta/v1:go_default_library",
"//pkg/conversion:go_default_library",
"//pkg/runtime:go_default_library",
"//pkg/runtime/schema:go_default_library",
"//pkg/runtime/serializer/protobuf:go_default_library",
"//pkg/runtime/serializer/streaming:go_default_library",
"//pkg/types:go_default_library",
"//pkg/util/diff:go_default_library",
"//pkg/util/sets:go_default_library",
"//pkg/util/uuid:go_default_library",
"//pkg/watch:go_default_library",
"//pkg/watch/versioned:go_default_library",
"//vendor:github.com/davecgh/go-spew/spew",
"//vendor:github.com/gogo/protobuf/proto",
"//vendor:github.com/golang/protobuf/proto",
"//vendor:github.com/google/gofuzz",
"//vendor:github.com/spf13/pflag",
"//vendor:github.com/ugorji/go/codec",
],
)

46
vendor/k8s.io/kubernetes/pkg/api/OWNERS generated vendored Normal file
View file

@ -0,0 +1,46 @@
approvers:
- bgrant0607
- erictune
- lavalamp
- smarterclayton
- thockin
reviewers:
- thockin
- lavalamp
- smarterclayton
- wojtek-t
- bgrant0607
- deads2k
- yujuhong
- brendandburns
- derekwaynecarr
- caesarxuchao
- vishh
- mikedanese
- liggitt
- nikhiljindal
- bprashanth
- gmarek
- erictune
- davidopp
- pmorie
- sttts
- kargakis
- dchen1107
- saad-ali
- zmerlynn
- luxas
- janetkuo
- justinsb
- pwittrock
- roberthbailey
- ncdc
- timstclair
- yifan-gu
- eparis
- mwielgus
- timothysc
- soltysh
- piosz
- jsafrane
- jbeda

20
vendor/k8s.io/kubernetes/pkg/api/annotations/BUILD generated vendored Normal file
View file

@ -0,0 +1,20 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
"go_test",
"cgo_library",
)
go_library(
name = "go_default_library",
srcs = [
"annotations.go",
"doc.go",
],
tags = ["automanaged"],
)

4
vendor/k8s.io/kubernetes/pkg/api/annotations/OWNERS generated vendored Executable file
View file

@ -0,0 +1,4 @@
reviewers:
- dims
- david-mcmahon
- jianhuiz

View file

@ -0,0 +1,23 @@
/*
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 annotations
const kubectlPrefix = "kubectl.kubernetes.io/"
// LastAppliedConfigAnnotation is the annotation used to store the previous
// configuration of a resource for use in a three way diff by UpdateApplyAnnotation.
const LastAppliedConfigAnnotation = kubectlPrefix + "last-applied-configuration"

18
vendor/k8s.io/kubernetes/pkg/api/annotations/doc.go generated vendored Normal file
View file

@ -0,0 +1,18 @@
/*
Copyright 2016 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
// Package annotations defines annotation keys that shared between server and client
package annotations // import "k8s.io/kubernetes/pkg/api/annotations"

152
vendor/k8s.io/kubernetes/pkg/api/context.go generated vendored Normal file
View file

@ -0,0 +1,152 @@
/*
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 api
import (
stderrs "errors"
"time"
"golang.org/x/net/context"
"k8s.io/kubernetes/pkg/auth/user"
"k8s.io/kubernetes/pkg/types"
)
// Context carries values across API boundaries.
// This context matches the context.Context interface
// (https://blog.golang.org/context), for the purposes
// of passing the api.Context through to the storage tier.
// TODO: Determine the extent that this abstraction+interface
// is used by the api, and whether we can remove.
type Context interface {
// Value returns the value associated with key or nil if none.
Value(key interface{}) interface{}
// Deadline returns the time when this Context will be canceled, if any.
Deadline() (deadline time.Time, ok bool)
// Done returns a channel that is closed when this Context is canceled
// or times out.
Done() <-chan struct{}
// Err indicates why this context was canceled, after the Done channel
// is closed.
Err() error
}
// The key type is unexported to prevent collisions
type key int
const (
// namespaceKey is the context key for the request namespace.
namespaceKey key = iota
// userKey is the context key for the request user.
userKey
// uidKey is the context key for the uid to assign to an object on create.
uidKey
// userAgentKey is the context key for the request user agent.
userAgentKey
)
// NewContext instantiates a base context object for request flows.
func NewContext() Context {
return context.TODO()
}
// NewDefaultContext instantiates a base context object for request flows in the default namespace
func NewDefaultContext() Context {
return WithNamespace(NewContext(), NamespaceDefault)
}
// WithValue returns a copy of parent in which the value associated with key is val.
func WithValue(parent Context, key interface{}, val interface{}) Context {
internalCtx, ok := parent.(context.Context)
if !ok {
panic(stderrs.New("Invalid context type"))
}
return context.WithValue(internalCtx, key, val)
}
// WithNamespace returns a copy of parent in which the namespace value is set
func WithNamespace(parent Context, namespace string) Context {
return WithValue(parent, namespaceKey, namespace)
}
// NamespaceFrom returns the value of the namespace key on the ctx
func NamespaceFrom(ctx Context) (string, bool) {
namespace, ok := ctx.Value(namespaceKey).(string)
return namespace, ok
}
// NamespaceValue returns the value of the namespace key on the ctx, or the empty string if none
func NamespaceValue(ctx Context) string {
namespace, _ := NamespaceFrom(ctx)
return namespace
}
// ValidNamespace returns false if the namespace on the context differs from the resource. If the resource has no namespace, it is set to the value in the context.
func ValidNamespace(ctx Context, resource *ObjectMeta) bool {
ns, ok := NamespaceFrom(ctx)
if len(resource.Namespace) == 0 {
resource.Namespace = ns
}
return ns == resource.Namespace && ok
}
// WithNamespaceDefaultIfNone returns a context whose namespace is the default if and only if the parent context has no namespace value
func WithNamespaceDefaultIfNone(parent Context) Context {
namespace, ok := NamespaceFrom(parent)
if !ok || len(namespace) == 0 {
return WithNamespace(parent, NamespaceDefault)
}
return parent
}
// WithUser returns a copy of parent in which the user value is set
func WithUser(parent Context, user user.Info) Context {
return WithValue(parent, userKey, user)
}
// UserFrom returns the value of the user key on the ctx
func UserFrom(ctx Context) (user.Info, bool) {
user, ok := ctx.Value(userKey).(user.Info)
return user, ok
}
// WithUID returns a copy of parent in which the uid value is set
func WithUID(parent Context, uid types.UID) Context {
return WithValue(parent, uidKey, uid)
}
// UIDFrom returns the value of the uid key on the ctx
func UIDFrom(ctx Context) (types.UID, bool) {
uid, ok := ctx.Value(uidKey).(types.UID)
return uid, ok
}
// WithUserAgent returns a copy of parent in which the user value is set
func WithUserAgent(parent Context, userAgent string) Context {
return WithValue(parent, userAgentKey, userAgent)
}
// UserAgentFrom returns the value of the userAgent key on the ctx
func UserAgentFrom(ctx Context) (string, bool) {
userAgent, ok := ctx.Value(userAgentKey).(string)
return userAgent, ok
}

68
vendor/k8s.io/kubernetes/pkg/api/context_test.go generated vendored Normal file
View file

@ -0,0 +1,68 @@
/*
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 api_test
import (
"testing"
"k8s.io/kubernetes/pkg/api"
)
// TestNamespaceContext validates that a namespace can be get/set on a context object
func TestNamespaceContext(t *testing.T) {
ctx := api.NewDefaultContext()
result, ok := api.NamespaceFrom(ctx)
if !ok {
t.Errorf("Error getting namespace")
}
if api.NamespaceDefault != result {
t.Errorf("Expected: %v, Actual: %v", api.NamespaceDefault, result)
}
ctx = api.NewContext()
result, ok = api.NamespaceFrom(ctx)
if ok {
t.Errorf("Should not be ok because there is no namespace on the context")
}
}
// TestValidNamespace validates that namespace rules are enforced on a resource prior to create or update
func TestValidNamespace(t *testing.T) {
ctx := api.NewDefaultContext()
namespace, _ := api.NamespaceFrom(ctx)
resource := api.ReplicationController{}
if !api.ValidNamespace(ctx, &resource.ObjectMeta) {
t.Errorf("expected success")
}
if namespace != resource.Namespace {
t.Errorf("expected resource to have the default namespace assigned during validation")
}
resource = api.ReplicationController{ObjectMeta: api.ObjectMeta{Namespace: "other"}}
if api.ValidNamespace(ctx, &resource.ObjectMeta) {
t.Errorf("Expected error that resource and context errors do not match because resource has different namespace")
}
ctx = api.NewContext()
if api.ValidNamespace(ctx, &resource.ObjectMeta) {
t.Errorf("Expected error that resource and context errors do not match since context has no namespace")
}
ctx = api.NewContext()
ns := api.NamespaceValue(ctx)
if ns != "" {
t.Errorf("Expected the empty string")
}
}

250
vendor/k8s.io/kubernetes/pkg/api/conversion.go generated vendored Normal file
View file

@ -0,0 +1,250 @@
/*
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 api
import (
"fmt"
"k8s.io/kubernetes/pkg/api/resource"
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/conversion"
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util/intstr"
utillabels "k8s.io/kubernetes/pkg/util/labels"
"k8s.io/kubernetes/pkg/util/validation/field"
)
func addConversionFuncs(scheme *runtime.Scheme) error {
return scheme.AddConversionFuncs(
Convert_v1_TypeMeta_To_v1_TypeMeta,
Convert_unversioned_ListMeta_To_unversioned_ListMeta,
Convert_intstr_IntOrString_To_intstr_IntOrString,
Convert_unversioned_Time_To_unversioned_Time,
Convert_Slice_string_To_unversioned_Time,
Convert_resource_Quantity_To_resource_Quantity,
Convert_string_To_labels_Selector,
Convert_labels_Selector_To_string,
Convert_string_To_fields_Selector,
Convert_fields_Selector_To_string,
Convert_Pointer_bool_To_bool,
Convert_bool_To_Pointer_bool,
Convert_Pointer_string_To_string,
Convert_string_To_Pointer_string,
Convert_Pointer_int64_To_int,
Convert_int_To_Pointer_int64,
Convert_Pointer_int32_To_int32,
Convert_int32_To_Pointer_int32,
Convert_Pointer_float64_To_float64,
Convert_float64_To_Pointer_float64,
Convert_map_to_unversioned_LabelSelector,
Convert_unversioned_LabelSelector_to_map,
)
}
func Convert_Pointer_float64_To_float64(in **float64, out *float64, s conversion.Scope) error {
if *in == nil {
*out = 0
return nil
}
*out = float64(**in)
return nil
}
func Convert_float64_To_Pointer_float64(in *float64, out **float64, s conversion.Scope) error {
temp := float64(*in)
*out = &temp
return nil
}
func Convert_Pointer_int32_To_int32(in **int32, out *int32, s conversion.Scope) error {
if *in == nil {
*out = 0
return nil
}
*out = int32(**in)
return nil
}
func Convert_int32_To_Pointer_int32(in *int32, out **int32, s conversion.Scope) error {
temp := int32(*in)
*out = &temp
return nil
}
func Convert_Pointer_int64_To_int(in **int64, out *int, s conversion.Scope) error {
if *in == nil {
*out = 0
return nil
}
*out = int(**in)
return nil
}
func Convert_int_To_Pointer_int64(in *int, out **int64, s conversion.Scope) error {
temp := int64(*in)
*out = &temp
return nil
}
func Convert_Pointer_string_To_string(in **string, out *string, s conversion.Scope) error {
if *in == nil {
*out = ""
return nil
}
*out = **in
return nil
}
func Convert_string_To_Pointer_string(in *string, out **string, s conversion.Scope) error {
if in == nil {
stringVar := ""
*out = &stringVar
return nil
}
*out = in
return nil
}
func Convert_Pointer_bool_To_bool(in **bool, out *bool, s conversion.Scope) error {
if *in == nil {
*out = false
return nil
}
*out = **in
return nil
}
func Convert_bool_To_Pointer_bool(in *bool, out **bool, s conversion.Scope) error {
if in == nil {
boolVar := false
*out = &boolVar
return nil
}
*out = in
return nil
}
// +k8s:conversion-fn=drop
func Convert_v1_TypeMeta_To_v1_TypeMeta(in, out *metav1.TypeMeta, s conversion.Scope) error {
// These values are explicitly not copied
//out.APIVersion = in.APIVersion
//out.Kind = in.Kind
return nil
}
// +k8s:conversion-fn=copy-only
func Convert_unversioned_ListMeta_To_unversioned_ListMeta(in, out *metav1.ListMeta, s conversion.Scope) error {
*out = *in
return nil
}
// +k8s:conversion-fn=copy-only
func Convert_intstr_IntOrString_To_intstr_IntOrString(in, out *intstr.IntOrString, s conversion.Scope) error {
*out = *in
return nil
}
// +k8s:conversion-fn=copy-only
func Convert_unversioned_Time_To_unversioned_Time(in *metav1.Time, out *metav1.Time, s conversion.Scope) error {
// Cannot deep copy these, because time.Time has unexported fields.
*out = *in
return nil
}
// Convert_Slice_string_To_unversioned_Time allows converting a URL query parameter value
func Convert_Slice_string_To_unversioned_Time(input *[]string, out *metav1.Time, s conversion.Scope) error {
str := ""
if len(*input) > 0 {
str = (*input)[0]
}
return out.UnmarshalQueryParameter(str)
}
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
}
// +k8s:conversion-fn=copy-only
func Convert_resource_Quantity_To_resource_Quantity(in *resource.Quantity, out *resource.Quantity, s conversion.Scope) error {
*out = *in
return nil
}
func Convert_map_to_unversioned_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 {
utillabels.AddLabelToSelector(out, labelKey, labelValue)
}
return nil
}
func Convert_unversioned_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
}

116
vendor/k8s.io/kubernetes/pkg/api/conversion_test.go generated vendored Normal file
View file

@ -0,0 +1,116 @@
/*
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 api_test
import (
"io/ioutil"
"math/rand"
"testing"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/testapi"
apitesting "k8s.io/kubernetes/pkg/api/testing"
"k8s.io/kubernetes/pkg/apimachinery/registered"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util/diff"
)
func BenchmarkPodConversion(b *testing.B) {
apiObjectFuzzer := apitesting.FuzzerFor(nil, api.SchemeGroupVersion, rand.NewSource(benchmarkSeed))
items := make([]api.Pod, 4)
for i := range items {
apiObjectFuzzer.Fuzz(&items[i])
items[i].Spec.InitContainers = nil
items[i].Status.InitContainerStatuses = nil
}
// add a fixed item
items = append(items, benchmarkPod)
width := len(items)
scheme := api.Scheme
for i := 0; i < b.N; i++ {
pod := &items[i%width]
versionedObj, err := scheme.UnsafeConvertToVersion(pod, registered.GroupOrDie(api.GroupName).GroupVersion)
if err != nil {
b.Fatalf("Conversion error: %v", err)
}
if _, err = scheme.UnsafeConvertToVersion(versionedObj, testapi.Default.InternalGroupVersion()); err != nil {
b.Fatalf("Conversion error: %v", err)
}
}
}
func BenchmarkNodeConversion(b *testing.B) {
data, err := ioutil.ReadFile("node_example.json")
if err != nil {
b.Fatalf("Unexpected error while reading file: %v", err)
}
var node api.Node
if err := runtime.DecodeInto(testapi.Default.Codec(), data, &node); err != nil {
b.Fatalf("Unexpected error decoding node: %v", err)
}
scheme := api.Scheme
var result *api.Node
b.ResetTimer()
for i := 0; i < b.N; i++ {
versionedObj, err := scheme.UnsafeConvertToVersion(&node, registered.GroupOrDie(api.GroupName).GroupVersion)
if err != nil {
b.Fatalf("Conversion error: %v", err)
}
obj, err := scheme.UnsafeConvertToVersion(versionedObj, testapi.Default.InternalGroupVersion())
if err != nil {
b.Fatalf("Conversion error: %v", err)
}
result = obj.(*api.Node)
}
b.StopTimer()
if !api.Semantic.DeepDerivative(node, *result) {
b.Fatalf("Incorrect conversion: %s", diff.ObjectDiff(node, *result))
}
}
func BenchmarkReplicationControllerConversion(b *testing.B) {
data, err := ioutil.ReadFile("replication_controller_example.json")
if err != nil {
b.Fatalf("Unexpected error while reading file: %v", err)
}
var replicationController api.ReplicationController
if err := runtime.DecodeInto(testapi.Default.Codec(), data, &replicationController); err != nil {
b.Fatalf("Unexpected error decoding node: %v", err)
}
scheme := api.Scheme
var result *api.ReplicationController
b.ResetTimer()
for i := 0; i < b.N; i++ {
versionedObj, err := scheme.UnsafeConvertToVersion(&replicationController, registered.GroupOrDie(api.GroupName).GroupVersion)
if err != nil {
b.Fatalf("Conversion error: %v", err)
}
obj, err := scheme.UnsafeConvertToVersion(versionedObj, testapi.Default.InternalGroupVersion())
if err != nil {
b.Fatalf("Conversion error: %v", err)
}
result = obj.(*api.ReplicationController)
}
b.StopTimer()
if !api.Semantic.DeepDerivative(replicationController, *result) {
b.Fatalf("Incorrect conversion: expected %v, got %v", replicationController, *result)
}
}

91
vendor/k8s.io/kubernetes/pkg/api/copy_test.go generated vendored Normal file
View file

@ -0,0 +1,91 @@
/*
Copyright 2015 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package api_test
import (
"bytes"
"math/rand"
"reflect"
"testing"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/testapi"
apitesting "k8s.io/kubernetes/pkg/api/testing"
"k8s.io/kubernetes/pkg/apimachinery/registered"
"k8s.io/kubernetes/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/util/diff"
"github.com/google/gofuzz"
)
func TestDeepCopyApiObjects(t *testing.T) {
for i := 0; i < *fuzzIters; i++ {
for _, version := range []schema.GroupVersion{testapi.Default.InternalGroupVersion(), registered.GroupOrDie(api.GroupName).GroupVersion} {
f := apitesting.FuzzerFor(t, version, rand.NewSource(rand.Int63()))
for kind := range api.Scheme.KnownTypes(version) {
doDeepCopyTest(t, version.WithKind(kind), f)
}
}
}
}
func doDeepCopyTest(t *testing.T, kind schema.GroupVersionKind, f *fuzz.Fuzzer) {
item, err := api.Scheme.New(kind)
if err != nil {
t.Fatalf("Could not create a %v: %s", kind, err)
}
f.Fuzz(item)
itemCopy, err := api.Scheme.DeepCopy(item)
if err != nil {
t.Errorf("Could not deep copy a %v: %s", kind, err)
return
}
if !reflect.DeepEqual(item, itemCopy) {
t.Errorf("\nexpected: %#v\n\ngot: %#v\n\ndiff: %v", item, itemCopy, diff.ObjectReflectDiff(item, itemCopy))
}
prefuzzData := &bytes.Buffer{}
if err := api.Codecs.LegacyCodec(kind.GroupVersion()).Encode(item, prefuzzData); err != nil {
t.Errorf("Could not encode a %v: %s", kind, err)
return
}
// Refuzz the copy, which should have no effect on the original
f.Fuzz(itemCopy)
postfuzzData := &bytes.Buffer{}
if err := api.Codecs.LegacyCodec(kind.GroupVersion()).Encode(item, postfuzzData); err != nil {
t.Errorf("Could not encode a %v: %s", kind, err)
return
}
if bytes.Compare(prefuzzData.Bytes(), postfuzzData.Bytes()) != 0 {
t.Log(diff.StringDiff(prefuzzData.String(), postfuzzData.String()))
t.Errorf("Fuzzing copy modified original of %#v", kind)
return
}
}
func TestDeepCopySingleType(t *testing.T) {
for i := 0; i < *fuzzIters; i++ {
for _, version := range []schema.GroupVersion{testapi.Default.InternalGroupVersion(), registered.GroupOrDie(api.GroupName).GroupVersion} {
f := apitesting.FuzzerFor(t, version, rand.NewSource(rand.Int63()))
doDeepCopyTest(t, version.WithKind("Pod"), f)
}
}
}

192
vendor/k8s.io/kubernetes/pkg/api/deep_copy_test.go generated vendored Normal file
View file

@ -0,0 +1,192 @@
/*
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 api_test
import (
"io/ioutil"
"testing"
"time"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/testapi"
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/types"
)
func parseTimeOrDie(ts string) metav1.Time {
t, err := time.Parse(time.RFC3339, ts)
if err != nil {
panic(err)
}
return metav1.Time{Time: t}
}
var benchmarkPod api.Pod = api.Pod{
TypeMeta: metav1.TypeMeta{
Kind: "Pod",
APIVersion: "v1",
},
ObjectMeta: api.ObjectMeta{
Name: "etcd-server-e2e-test-wojtekt-master",
Namespace: "default",
SelfLink: "/api/v1/namespaces/default/pods/etcd-server-e2e-test-wojtekt-master",
UID: types.UID("a671734a-e8e5-11e4-8fde-42010af09327"),
ResourceVersion: "22",
CreationTimestamp: parseTimeOrDie("2015-04-22T11:49:36Z"),
Annotations: map[string]string{
"kubernetes.io/config.mirror": "mirror",
"kubernetes.io/config.source": "file",
},
},
Spec: api.PodSpec{
Volumes: []api.Volume{
{
Name: "varetcd",
VolumeSource: api.VolumeSource{
HostPath: &api.HostPathVolumeSource{
Path: "/mnt/master-pd/var/etcd",
},
},
},
},
Containers: []api.Container{
{
Name: "etcd-container",
Image: "gcr.io/google_containers/etcd:2.0.9",
Command: []string{
"/usr/local/bin/etcd",
"--addr",
"127.0.0.1:2379",
"--bind-addr",
"127.0.0.1:2379",
"--data-dir",
"/var/etcd/data",
},
Ports: []api.ContainerPort{
{
Name: "serverport",
HostPort: 2380,
ContainerPort: 2380,
Protocol: "TCP",
},
{
Name: "clientport",
HostPort: 2379,
ContainerPort: 2379,
Protocol: "TCP",
},
},
VolumeMounts: []api.VolumeMount{
{
Name: "varetcd",
MountPath: "/var/etcd",
},
},
TerminationMessagePath: "/dev/termination-log",
ImagePullPolicy: api.PullIfNotPresent,
},
},
RestartPolicy: api.RestartPolicyAlways,
DNSPolicy: api.DNSClusterFirst,
NodeName: "e2e-test-wojtekt-master",
},
Status: api.PodStatus{
Phase: api.PodRunning,
Conditions: []api.PodCondition{
{
Type: api.PodReady,
Status: api.ConditionTrue,
},
},
ContainerStatuses: []api.ContainerStatus{
{
Name: "etcd-container",
State: api.ContainerState{
Running: &api.ContainerStateRunning{
StartedAt: parseTimeOrDie("2015-04-22T11:49:32Z"),
},
},
Ready: true,
RestartCount: 0,
Image: "gcr.io/google_containers/etcd:2.0.9",
ImageID: "docker://b6b9a86dc06aa1361357ca1b105feba961f6a4145adca6c54e142c0be0fe87b0",
ContainerID: "docker://3cbbf818f1addfc252957b4504f56ef2907a313fe6afc47fc75373674255d46d",
},
},
},
}
func BenchmarkPodCopy(b *testing.B) {
var result *api.Pod
for i := 0; i < b.N; i++ {
obj, err := api.Scheme.DeepCopy(&benchmarkPod)
if err != nil {
b.Fatalf("Unexpected error copying pod: %v", err)
}
result = obj.(*api.Pod)
}
if !api.Semantic.DeepEqual(benchmarkPod, *result) {
b.Fatalf("Incorrect copy: expected %v, got %v", benchmarkPod, *result)
}
}
func BenchmarkNodeCopy(b *testing.B) {
data, err := ioutil.ReadFile("node_example.json")
if err != nil {
b.Fatalf("Unexpected error while reading file: %v", err)
}
var node api.Node
if err := runtime.DecodeInto(testapi.Default.Codec(), data, &node); err != nil {
b.Fatalf("Unexpected error decoding node: %v", err)
}
var result *api.Node
for i := 0; i < b.N; i++ {
obj, err := api.Scheme.DeepCopy(&node)
if err != nil {
b.Fatalf("Unexpected error copying node: %v", err)
}
result = obj.(*api.Node)
}
if !api.Semantic.DeepEqual(node, *result) {
b.Fatalf("Incorrect copy: expected %v, got %v", node, *result)
}
}
func BenchmarkReplicationControllerCopy(b *testing.B) {
data, err := ioutil.ReadFile("replication_controller_example.json")
if err != nil {
b.Fatalf("Unexpected error while reading file: %v", err)
}
var replicationController api.ReplicationController
if err := runtime.DecodeInto(testapi.Default.Codec(), data, &replicationController); err != nil {
b.Fatalf("Unexpected error decoding node: %v", err)
}
var result *api.ReplicationController
for i := 0; i < b.N; i++ {
obj, err := api.Scheme.DeepCopy(&replicationController)
if err != nil {
b.Fatalf("Unexpected error copying replication controller: %v", err)
}
result = obj.(*api.ReplicationController)
}
if !api.Semantic.DeepEqual(replicationController, *result) {
b.Fatalf("Incorrect copy: expected %v, got %v", replicationController, *result)
}
}

205
vendor/k8s.io/kubernetes/pkg/api/defaulting_test.go generated vendored Normal file
View file

@ -0,0 +1,205 @@
/*
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 api_test
import (
"math/rand"
"reflect"
"sort"
"testing"
"github.com/google/gofuzz"
"k8s.io/kubernetes/pkg/api"
apiv1 "k8s.io/kubernetes/pkg/api/v1"
batchv2alpha1 "k8s.io/kubernetes/pkg/apis/batch/v2alpha1"
extensionsv1beta1 "k8s.io/kubernetes/pkg/apis/extensions/v1beta1"
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/util/diff"
)
type orderedGroupVersionKinds []schema.GroupVersionKind
func (o orderedGroupVersionKinds) Len() int { return len(o) }
func (o orderedGroupVersionKinds) Swap(i, j int) { o[i], o[j] = o[j], o[i] }
func (o orderedGroupVersionKinds) Less(i, j int) bool {
return o[i].String() < o[j].String()
}
func TestVerifyDefaulting(t *testing.T) {
job := &batchv2alpha1.JobTemplate{}
batchv2alpha1.SetObjectDefaults_JobTemplate(job)
if job.Template.Spec.Template.Spec.DNSPolicy != apiv1.DNSClusterFirst {
t.Errorf("unexpected defaulting: %#v", job)
}
}
// TODO: add a reflexive test that verifies that all SetDefaults functions are registered
func TestDefaulting(t *testing.T) {
// these are the known types with defaulters - you must add to this list if you add a top level defaulter
typesWithDefaulting := map[schema.GroupVersionKind]struct{}{
{Group: "", Version: "v1", Kind: "ConfigMap"}: {},
{Group: "", Version: "v1", Kind: "ConfigMapList"}: {},
{Group: "", Version: "v1", Kind: "Endpoints"}: {},
{Group: "", Version: "v1", Kind: "EndpointsList"}: {},
{Group: "", Version: "v1", Kind: "Namespace"}: {},
{Group: "", Version: "v1", Kind: "NamespaceList"}: {},
{Group: "", Version: "v1", Kind: "Node"}: {},
{Group: "", Version: "v1", Kind: "NodeList"}: {},
{Group: "", Version: "v1", Kind: "PersistentVolume"}: {},
{Group: "", Version: "v1", Kind: "PersistentVolumeList"}: {},
{Group: "", Version: "v1", Kind: "PersistentVolumeClaim"}: {},
{Group: "", Version: "v1", Kind: "PersistentVolumeClaimList"}: {},
{Group: "", Version: "v1", Kind: "PodAttachOptions"}: {},
{Group: "", Version: "v1", Kind: "PodExecOptions"}: {},
{Group: "", Version: "v1", Kind: "Pod"}: {},
{Group: "", Version: "v1", Kind: "PodList"}: {},
{Group: "", Version: "v1", Kind: "PodTemplate"}: {},
{Group: "", Version: "v1", Kind: "PodTemplateList"}: {},
{Group: "", Version: "v1", Kind: "ReplicationController"}: {},
{Group: "", Version: "v1", Kind: "ReplicationControllerList"}: {},
{Group: "", Version: "v1", Kind: "Secret"}: {},
{Group: "", Version: "v1", Kind: "SecretList"}: {},
{Group: "", Version: "v1", Kind: "Service"}: {},
{Group: "", Version: "v1", Kind: "ServiceList"}: {},
{Group: "apps", Version: "v1beta1", Kind: "StatefulSet"}: {},
{Group: "apps", Version: "v1beta1", Kind: "StatefulSetList"}: {},
{Group: "autoscaling", Version: "v1", Kind: "HorizontalPodAutoscaler"}: {},
{Group: "autoscaling", Version: "v1", Kind: "HorizontalPodAutoscalerList"}: {},
{Group: "batch", Version: "v1", Kind: "Job"}: {},
{Group: "batch", Version: "v1", Kind: "JobList"}: {},
{Group: "batch", Version: "v2alpha1", Kind: "CronJob"}: {},
{Group: "batch", Version: "v2alpha1", Kind: "CronJobList"}: {},
{Group: "batch", Version: "v2alpha1", Kind: "Job"}: {},
{Group: "batch", Version: "v2alpha1", Kind: "JobList"}: {},
{Group: "batch", Version: "v2alpha1", Kind: "JobTemplate"}: {},
{Group: "batch", Version: "v2alpha1", Kind: "ScheduledJob"}: {},
{Group: "batch", Version: "v2alpha1", Kind: "ScheduledJobList"}: {},
{Group: "componentconfig", Version: "v1alpha1", Kind: "KubeProxyConfiguration"}: {},
{Group: "componentconfig", Version: "v1alpha1", Kind: "KubeSchedulerConfiguration"}: {},
{Group: "componentconfig", Version: "v1alpha1", Kind: "KubeletConfiguration"}: {},
{Group: "kubeadm.k8s.io", Version: "v1alpha1", Kind: "MasterConfiguration"}: {},
// This object contains only int fields which currently breaks the defaulting test because
// it's pretty stupid. Once we add non integer fields, we should uncomment this.
// {Group: "kubeadm.k8s.io", Version: "v1alpha1", Kind: "NodeConfiguration"}: {},
{Group: "extensions", Version: "v1beta1", Kind: "DaemonSet"}: {},
{Group: "extensions", Version: "v1beta1", Kind: "DaemonSetList"}: {},
{Group: "extensions", Version: "v1beta1", Kind: "Deployment"}: {},
{Group: "extensions", Version: "v1beta1", Kind: "DeploymentList"}: {},
{Group: "extensions", Version: "v1beta1", Kind: "HorizontalPodAutoscaler"}: {},
{Group: "extensions", Version: "v1beta1", Kind: "HorizontalPodAutoscalerList"}: {},
{Group: "extensions", Version: "v1beta1", Kind: "Job"}: {},
{Group: "extensions", Version: "v1beta1", Kind: "JobList"}: {},
{Group: "extensions", Version: "v1beta1", Kind: "ReplicaSet"}: {},
{Group: "extensions", Version: "v1beta1", Kind: "ReplicaSetList"}: {},
{Group: "rbac.authorization.k8s.io", Version: "v1alpha1", Kind: "ClusterRoleBinding"}: {},
{Group: "rbac.authorization.k8s.io", Version: "v1alpha1", Kind: "ClusterRoleBindingList"}: {},
{Group: "rbac.authorization.k8s.io", Version: "v1alpha1", Kind: "RoleBinding"}: {},
{Group: "rbac.authorization.k8s.io", Version: "v1alpha1", Kind: "RoleBindingList"}: {},
}
f := fuzz.New().NilChance(.5).NumElements(1, 1).RandSource(rand.NewSource(1))
f.Funcs(
func(s *runtime.RawExtension, c fuzz.Continue) {},
func(s *metav1.LabelSelector, c fuzz.Continue) {
c.FuzzNoCustom(s)
s.MatchExpressions = nil // need to fuzz this specially
},
func(s *apiv1.ListOptions, c fuzz.Continue) {
c.FuzzNoCustom(s)
s.LabelSelector = "" // need to fuzz requirement strings specially
s.FieldSelector = "" // need to fuzz requirement strings specially
},
func(s *extensionsv1beta1.ScaleStatus, c fuzz.Continue) {
c.FuzzNoCustom(s)
s.TargetSelector = "" // need to fuzz requirement strings specially
},
)
scheme := api.Scheme
var testTypes orderedGroupVersionKinds
for gvk := range scheme.AllKnownTypes() {
if gvk.Version == runtime.APIVersionInternal {
continue
}
testTypes = append(testTypes, gvk)
}
sort.Sort(testTypes)
for _, gvk := range testTypes {
_, expectedChanged := typesWithDefaulting[gvk]
iter := 0
changedOnce := false
for {
if iter > *fuzzIters {
if !expectedChanged || changedOnce {
break
}
if iter > 200 {
t.Errorf("expected %s to trigger defaulting due to fuzzing", gvk)
break
}
// if we expected defaulting, continue looping until the fuzzer gives us one
// at worst, we will timeout
}
iter++
src, err := scheme.New(gvk)
if err != nil {
t.Fatal(err)
}
f.Fuzz(src)
src.GetObjectKind().SetGroupVersionKind(schema.GroupVersionKind{})
original, err := scheme.DeepCopy(src)
if err != nil {
t.Fatal(err)
}
// get internal
withDefaults, _ := scheme.DeepCopy(src)
scheme.Default(withDefaults.(runtime.Object))
if !reflect.DeepEqual(original, withDefaults) {
changedOnce = true
if !expectedChanged {
t.Errorf("{Group: \"%s\", Version: \"%s\", Kind: \"%s\"} did not expect defaults to be set - update expected or check defaulter registering: %s", gvk.Group, gvk.Version, gvk.Kind, diff.ObjectReflectDiff(original, withDefaults))
}
}
}
}
}
func BenchmarkPodDefaulting(b *testing.B) {
f := fuzz.New().NilChance(.5).NumElements(1, 1).RandSource(rand.NewSource(1))
items := make([]apiv1.Pod, 100)
for i := range items {
f.Fuzz(&items[i])
}
scheme := api.Scheme
b.ResetTimer()
for i := 0; i < b.N; i++ {
pod := &items[i%len(items)]
scheme.Default(pod)
}
b.StopTimer()
}

36
vendor/k8s.io/kubernetes/pkg/api/defaults.go generated vendored Normal file
View file

@ -0,0 +1,36 @@
/*
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 api
import (
"k8s.io/kubernetes/pkg/fields"
"k8s.io/kubernetes/pkg/labels"
"k8s.io/kubernetes/pkg/runtime"
)
func addDefaultingFuncs(scheme *runtime.Scheme) error {
return scheme.AddDefaultingFuncs(
func(obj *ListOptions) {
if obj.LabelSelector == nil {
obj.LabelSelector = labels.Everything()
}
if obj.FieldSelector == nil {
obj.FieldSelector = fields.Everything()
}
},
)
}

24
vendor/k8s.io/kubernetes/pkg/api/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.
*/
// +k8s:deepcopy-gen=package,register
// Package api contains the latest (or "internal") version of the
// Kubernetes API objects. This is the API objects as represented in memory.
// The contract presented to clients is located in the versioned packages,
// which are sub-directories. The first one is "v1". Those packages
// describe how a particular version is serialized to storage/network.
package api // import "k8s.io/kubernetes/pkg/api"

34
vendor/k8s.io/kubernetes/pkg/api/endpoints/BUILD generated vendored Normal file
View file

@ -0,0 +1,34 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
"go_test",
"cgo_library",
)
go_library(
name = "go_default_library",
srcs = ["util.go"],
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/types:go_default_library",
"//pkg/util/hash:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = ["util_test.go"],
library = "go_default_library",
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/types:go_default_library",
"//vendor:github.com/davecgh/go-spew/spew",
],
)

8
vendor/k8s.io/kubernetes/pkg/api/endpoints/OWNERS generated vendored Executable file
View file

@ -0,0 +1,8 @@
reviewers:
- thockin
- smarterclayton
- mikedanese
- sttts
- eparis
- resouer
- david-mcmahon

238
vendor/k8s.io/kubernetes/pkg/api/endpoints/util.go generated vendored Normal file
View file

@ -0,0 +1,238 @@
/*
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 endpoints
import (
"bytes"
"crypto/md5"
"encoding/hex"
"hash"
"sort"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/types"
hashutil "k8s.io/kubernetes/pkg/util/hash"
)
const (
// TODO: to be deleted after v1.3 is released
// Its value is the json representation of map[string(IP)][HostRecord]
// example: '{"10.245.1.6":{"HostName":"my-webserver"}}'
PodHostnamesAnnotation = "endpoints.beta.kubernetes.io/hostnames-map"
)
// TODO: to be deleted after v1.3 is released
type HostRecord struct {
HostName string
}
// RepackSubsets takes a slice of EndpointSubset objects, expands it to the full
// representation, and then repacks that into the canonical layout. This
// ensures that code which operates on these objects can rely on the common
// form for things like comparison. The result is a newly allocated slice.
func RepackSubsets(subsets []api.EndpointSubset) []api.EndpointSubset {
// First map each unique port definition to the sets of hosts that
// offer it.
allAddrs := map[addressKey]*api.EndpointAddress{}
portToAddrReadyMap := map[api.EndpointPort]addressSet{}
for i := range subsets {
for _, port := range subsets[i].Ports {
for k := range subsets[i].Addresses {
mapAddressByPort(&subsets[i].Addresses[k], port, true, allAddrs, portToAddrReadyMap)
}
for k := range subsets[i].NotReadyAddresses {
mapAddressByPort(&subsets[i].NotReadyAddresses[k], port, false, allAddrs, portToAddrReadyMap)
}
}
}
// Next, map the sets of hosts to the sets of ports they offer.
// Go does not allow maps or slices as keys to maps, so we have
// to synthesize an artificial key and do a sort of 2-part
// associative entity.
type keyString string
keyToAddrReadyMap := map[keyString]addressSet{}
addrReadyMapKeyToPorts := map[keyString][]api.EndpointPort{}
for port, addrs := range portToAddrReadyMap {
key := keyString(hashAddresses(addrs))
keyToAddrReadyMap[key] = addrs
addrReadyMapKeyToPorts[key] = append(addrReadyMapKeyToPorts[key], port)
}
// Next, build the N-to-M association the API wants.
final := []api.EndpointSubset{}
for key, ports := range addrReadyMapKeyToPorts {
var readyAddrs, notReadyAddrs []api.EndpointAddress
for addr, ready := range keyToAddrReadyMap[key] {
if ready {
readyAddrs = append(readyAddrs, *addr)
} else {
notReadyAddrs = append(notReadyAddrs, *addr)
}
}
final = append(final, api.EndpointSubset{Addresses: readyAddrs, NotReadyAddresses: notReadyAddrs, Ports: ports})
}
// Finally, sort it.
return SortSubsets(final)
}
// The sets of hosts must be de-duped, using IP+UID as the key.
type addressKey struct {
ip string
uid types.UID
}
// mapAddressByPort adds an address into a map by its ports, registering the address with a unique pointer, and preserving
// any existing ready state.
func mapAddressByPort(addr *api.EndpointAddress, port api.EndpointPort, ready bool, allAddrs map[addressKey]*api.EndpointAddress, portToAddrReadyMap map[api.EndpointPort]addressSet) *api.EndpointAddress {
// use addressKey to distinguish between two endpoints that are identical addresses
// but may have come from different hosts, for attribution. For instance, Mesos
// assigns pods the node IP, but the pods are distinct.
key := addressKey{ip: addr.IP}
if addr.TargetRef != nil {
key.uid = addr.TargetRef.UID
}
// Accumulate the address. The full EndpointAddress structure is preserved for use when
// we rebuild the subsets so that the final TargetRef has all of the necessary data.
existingAddress := allAddrs[key]
if existingAddress == nil {
// Make a copy so we don't write to the
// input args of this function.
existingAddress = &api.EndpointAddress{}
*existingAddress = *addr
allAddrs[key] = existingAddress
}
// Remember that this port maps to this address.
if _, found := portToAddrReadyMap[port]; !found {
portToAddrReadyMap[port] = addressSet{}
}
// if we have not yet recorded this port for this address, or if the previous
// state was ready, write the current ready state. not ready always trumps
// ready.
if wasReady, found := portToAddrReadyMap[port][existingAddress]; !found || wasReady {
portToAddrReadyMap[port][existingAddress] = ready
}
return existingAddress
}
type addressSet map[*api.EndpointAddress]bool
type addrReady struct {
addr *api.EndpointAddress
ready bool
}
func hashAddresses(addrs addressSet) string {
// Flatten the list of addresses into a string so it can be used as a
// map key. Unfortunately, DeepHashObject is implemented in terms of
// spew, and spew does not handle non-primitive map keys well. So
// first we collapse it into a slice, sort the slice, then hash that.
slice := make([]addrReady, 0, len(addrs))
for k, ready := range addrs {
slice = append(slice, addrReady{k, ready})
}
sort.Sort(addrsReady(slice))
hasher := md5.New()
hashutil.DeepHashObject(hasher, slice)
return hex.EncodeToString(hasher.Sum(nil)[0:])
}
func lessAddrReady(a, b addrReady) bool {
// ready is not significant to hashing since we can't have duplicate addresses
return LessEndpointAddress(a.addr, b.addr)
}
type addrsReady []addrReady
func (sl addrsReady) Len() int { return len(sl) }
func (sl addrsReady) Swap(i, j int) { sl[i], sl[j] = sl[j], sl[i] }
func (sl addrsReady) Less(i, j int) bool {
return lessAddrReady(sl[i], sl[j])
}
func LessEndpointAddress(a, b *api.EndpointAddress) bool {
ipComparison := bytes.Compare([]byte(a.IP), []byte(b.IP))
if ipComparison != 0 {
return ipComparison < 0
}
if b.TargetRef == nil {
return false
}
if a.TargetRef == nil {
return true
}
return a.TargetRef.UID < b.TargetRef.UID
}
type addrPtrsByIpAndUID []*api.EndpointAddress
func (sl addrPtrsByIpAndUID) Len() int { return len(sl) }
func (sl addrPtrsByIpAndUID) Swap(i, j int) { sl[i], sl[j] = sl[j], sl[i] }
func (sl addrPtrsByIpAndUID) Less(i, j int) bool {
return LessEndpointAddress(sl[i], sl[j])
}
// SortSubsets sorts an array of EndpointSubset objects in place. For ease of
// use it returns the input slice.
func SortSubsets(subsets []api.EndpointSubset) []api.EndpointSubset {
for i := range subsets {
ss := &subsets[i]
sort.Sort(addrsByIpAndUID(ss.Addresses))
sort.Sort(addrsByIpAndUID(ss.NotReadyAddresses))
sort.Sort(portsByHash(ss.Ports))
}
sort.Sort(subsetsByHash(subsets))
return subsets
}
func hashObject(hasher hash.Hash, obj interface{}) []byte {
hashutil.DeepHashObject(hasher, obj)
return hasher.Sum(nil)
}
type subsetsByHash []api.EndpointSubset
func (sl subsetsByHash) Len() int { return len(sl) }
func (sl subsetsByHash) Swap(i, j int) { sl[i], sl[j] = sl[j], sl[i] }
func (sl subsetsByHash) Less(i, j int) bool {
hasher := md5.New()
h1 := hashObject(hasher, sl[i])
h2 := hashObject(hasher, sl[j])
return bytes.Compare(h1, h2) < 0
}
type addrsByIpAndUID []api.EndpointAddress
func (sl addrsByIpAndUID) Len() int { return len(sl) }
func (sl addrsByIpAndUID) Swap(i, j int) { sl[i], sl[j] = sl[j], sl[i] }
func (sl addrsByIpAndUID) Less(i, j int) bool {
return LessEndpointAddress(&sl[i], &sl[j])
}
type portsByHash []api.EndpointPort
func (sl portsByHash) Len() int { return len(sl) }
func (sl portsByHash) Swap(i, j int) { sl[i], sl[j] = sl[j], sl[i] }
func (sl portsByHash) Less(i, j int) bool {
hasher := md5.New()
h1 := hashObject(hasher, sl[i])
h2 := hashObject(hasher, sl[j])
return bytes.Compare(h1, h2) < 0
}

464
vendor/k8s.io/kubernetes/pkg/api/endpoints/util_test.go generated vendored Normal file
View file

@ -0,0 +1,464 @@
/*
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 endpoints
import (
"reflect"
"testing"
"github.com/davecgh/go-spew/spew"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/types"
)
func podRef(uid string) *api.ObjectReference {
ref := api.ObjectReference{UID: types.UID(uid)}
return &ref
}
func TestPackSubsets(t *testing.T) {
// The downside of table-driven tests is that some things have to live outside the table.
fooObjRef := api.ObjectReference{Name: "foo"}
barObjRef := api.ObjectReference{Name: "bar"}
testCases := []struct {
name string
given []api.EndpointSubset
expect []api.EndpointSubset
}{
{
name: "empty everything",
given: []api.EndpointSubset{{Addresses: []api.EndpointAddress{}, Ports: []api.EndpointPort{}}},
expect: []api.EndpointSubset{},
}, {
name: "empty addresses",
given: []api.EndpointSubset{{Addresses: []api.EndpointAddress{}, Ports: []api.EndpointPort{{Port: 111}}}},
expect: []api.EndpointSubset{},
}, {
name: "empty ports",
given: []api.EndpointSubset{{Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}}, Ports: []api.EndpointPort{}}},
expect: []api.EndpointSubset{},
}, {
name: "empty ports",
given: []api.EndpointSubset{{NotReadyAddresses: []api.EndpointAddress{{IP: "1.2.3.4"}}, Ports: []api.EndpointPort{}}},
expect: []api.EndpointSubset{},
}, {
name: "one set, one ip, one port",
given: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
Ports: []api.EndpointPort{{Port: 111}},
}},
expect: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
Ports: []api.EndpointPort{{Port: 111}},
}},
}, {
name: "one set, one ip, one port (IPv6)",
given: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "beef::1:2:3:4"}},
Ports: []api.EndpointPort{{Port: 111}},
}},
expect: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "beef::1:2:3:4"}},
Ports: []api.EndpointPort{{Port: 111}},
}},
}, {
name: "one set, one notReady ip, one port",
given: []api.EndpointSubset{{
NotReadyAddresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
Ports: []api.EndpointPort{{Port: 111}},
}},
expect: []api.EndpointSubset{{
NotReadyAddresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
Ports: []api.EndpointPort{{Port: 111}},
}},
}, {
name: "one set, one ip, one UID, one port",
given: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "1.2.3.4", TargetRef: podRef("uid-1")}},
Ports: []api.EndpointPort{{Port: 111}},
}},
expect: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "1.2.3.4", TargetRef: podRef("uid-1")}},
Ports: []api.EndpointPort{{Port: 111}},
}},
}, {
name: "one set, one notReady ip, one UID, one port",
given: []api.EndpointSubset{{
NotReadyAddresses: []api.EndpointAddress{{IP: "1.2.3.4", TargetRef: podRef("uid-1")}},
Ports: []api.EndpointPort{{Port: 111}},
}},
expect: []api.EndpointSubset{{
NotReadyAddresses: []api.EndpointAddress{{IP: "1.2.3.4", TargetRef: podRef("uid-1")}},
Ports: []api.EndpointPort{{Port: 111}},
}},
}, {
name: "one set, one ip, empty UID, one port",
given: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "1.2.3.4", TargetRef: podRef("")}},
Ports: []api.EndpointPort{{Port: 111}},
}},
expect: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "1.2.3.4", TargetRef: podRef("")}},
Ports: []api.EndpointPort{{Port: 111}},
}},
}, {
name: "one set, one notReady ip, empty UID, one port",
given: []api.EndpointSubset{{
NotReadyAddresses: []api.EndpointAddress{{IP: "1.2.3.4", TargetRef: podRef("")}},
Ports: []api.EndpointPort{{Port: 111}},
}},
expect: []api.EndpointSubset{{
NotReadyAddresses: []api.EndpointAddress{{IP: "1.2.3.4", TargetRef: podRef("")}},
Ports: []api.EndpointPort{{Port: 111}},
}},
}, {
name: "one set, two ips, one port",
given: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}, {IP: "5.6.7.8"}},
Ports: []api.EndpointPort{{Port: 111}},
}},
expect: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}, {IP: "5.6.7.8"}},
Ports: []api.EndpointPort{{Port: 111}},
}},
}, {
name: "one set, two mixed ips, one port",
given: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
NotReadyAddresses: []api.EndpointAddress{{IP: "5.6.7.8"}},
Ports: []api.EndpointPort{{Port: 111}},
}},
expect: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
NotReadyAddresses: []api.EndpointAddress{{IP: "5.6.7.8"}},
Ports: []api.EndpointPort{{Port: 111}},
}},
}, {
name: "one set, two duplicate ips, one port, notReady is covered by ready",
given: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
NotReadyAddresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
Ports: []api.EndpointPort{{Port: 111}},
}},
expect: []api.EndpointSubset{{
NotReadyAddresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
Ports: []api.EndpointPort{{Port: 111}},
}},
}, {
name: "one set, one ip, two ports",
given: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
Ports: []api.EndpointPort{{Port: 111}, {Port: 222}},
}},
expect: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
Ports: []api.EndpointPort{{Port: 111}, {Port: 222}},
}},
}, {
name: "one set, dup ips, one port",
given: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}, {IP: "1.2.3.4"}},
Ports: []api.EndpointPort{{Port: 111}},
}},
expect: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
Ports: []api.EndpointPort{{Port: 111}},
}},
}, {
name: "one set, dup ips, one port (IPv6)",
given: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "beef::1"}, {IP: "beef::1"}},
Ports: []api.EndpointPort{{Port: 111}},
}},
expect: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "beef::1"}},
Ports: []api.EndpointPort{{Port: 111}},
}},
}, {
name: "one set, dup ips with target-refs, one port",
given: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{
{IP: "1.2.3.4", TargetRef: &fooObjRef},
{IP: "1.2.3.4", TargetRef: &barObjRef},
},
Ports: []api.EndpointPort{{Port: 111}},
}},
expect: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "1.2.3.4", TargetRef: &fooObjRef}},
Ports: []api.EndpointPort{{Port: 111}},
}},
}, {
name: "one set, dup mixed ips with target-refs, one port",
given: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{
{IP: "1.2.3.4", TargetRef: &fooObjRef},
},
NotReadyAddresses: []api.EndpointAddress{
{IP: "1.2.3.4", TargetRef: &barObjRef},
},
Ports: []api.EndpointPort{{Port: 111}},
}},
expect: []api.EndpointSubset{{
// finding the same address twice is considered an error on input, only the first address+port
// reference is preserved
NotReadyAddresses: []api.EndpointAddress{{IP: "1.2.3.4", TargetRef: &fooObjRef}},
Ports: []api.EndpointPort{{Port: 111}},
}},
}, {
name: "one set, one ip, dup ports",
given: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
Ports: []api.EndpointPort{{Port: 111}, {Port: 111}},
}},
expect: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
Ports: []api.EndpointPort{{Port: 111}},
}},
}, {
name: "two sets, dup ip, dup port",
given: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
Ports: []api.EndpointPort{{Port: 111}},
}, {
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
Ports: []api.EndpointPort{{Port: 111}},
}},
expect: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
Ports: []api.EndpointPort{{Port: 111}},
}},
}, {
name: "two sets, dup mixed ip, dup port",
given: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
Ports: []api.EndpointPort{{Port: 111}},
}, {
NotReadyAddresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
Ports: []api.EndpointPort{{Port: 111}},
}},
expect: []api.EndpointSubset{{
NotReadyAddresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
Ports: []api.EndpointPort{{Port: 111}},
}},
}, {
name: "two sets, dup ip, two ports",
given: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
Ports: []api.EndpointPort{{Port: 111}},
}, {
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
Ports: []api.EndpointPort{{Port: 222}},
}},
expect: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
Ports: []api.EndpointPort{{Port: 111}, {Port: 222}},
}},
}, {
name: "two sets, dup ip, dup uids, two ports",
given: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "1.2.3.4", TargetRef: podRef("uid-1")}},
Ports: []api.EndpointPort{{Port: 111}},
}, {
Addresses: []api.EndpointAddress{{IP: "1.2.3.4", TargetRef: podRef("uid-1")}},
Ports: []api.EndpointPort{{Port: 222}},
}},
expect: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "1.2.3.4", TargetRef: podRef("uid-1")}},
Ports: []api.EndpointPort{{Port: 111}, {Port: 222}},
}},
}, {
name: "two sets, dup mixed ip, dup uids, two ports",
given: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "1.2.3.4", TargetRef: podRef("uid-1")}},
Ports: []api.EndpointPort{{Port: 111}},
}, {
NotReadyAddresses: []api.EndpointAddress{{IP: "1.2.3.4", TargetRef: podRef("uid-1")}},
Ports: []api.EndpointPort{{Port: 222}},
}},
expect: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "1.2.3.4", TargetRef: podRef("uid-1")}},
Ports: []api.EndpointPort{{Port: 111}},
}, {
NotReadyAddresses: []api.EndpointAddress{{IP: "1.2.3.4", TargetRef: podRef("uid-1")}},
Ports: []api.EndpointPort{{Port: 222}},
}},
}, {
name: "two sets, two ips, dup port",
given: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
Ports: []api.EndpointPort{{Port: 111}},
}, {
Addresses: []api.EndpointAddress{{IP: "5.6.7.8"}},
Ports: []api.EndpointPort{{Port: 111}},
}},
expect: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}, {IP: "5.6.7.8"}},
Ports: []api.EndpointPort{{Port: 111}},
}},
}, {
name: "two set, dup ip, two uids, dup ports",
given: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "1.2.3.4", TargetRef: podRef("uid-1")}},
Ports: []api.EndpointPort{{Port: 111}},
}, {
Addresses: []api.EndpointAddress{{IP: "1.2.3.4", TargetRef: podRef("uid-2")}},
Ports: []api.EndpointPort{{Port: 111}},
}},
expect: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{
{IP: "1.2.3.4", TargetRef: podRef("uid-1")},
{IP: "1.2.3.4", TargetRef: podRef("uid-2")},
},
Ports: []api.EndpointPort{{Port: 111}},
}},
}, {
name: "two set, dup ip, with and without uid, dup ports",
given: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
Ports: []api.EndpointPort{{Port: 111}},
}, {
Addresses: []api.EndpointAddress{{IP: "1.2.3.4", TargetRef: podRef("uid-2")}},
Ports: []api.EndpointPort{{Port: 111}},
}},
expect: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{
{IP: "1.2.3.4"},
{IP: "1.2.3.4", TargetRef: podRef("uid-2")},
},
Ports: []api.EndpointPort{{Port: 111}},
}},
}, {
name: "two sets, two ips, two dup ip with uid, dup port, wrong order",
given: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "5.6.7.8"}},
Ports: []api.EndpointPort{{Port: 111}},
}, {
Addresses: []api.EndpointAddress{{IP: "5.6.7.8", TargetRef: podRef("uid-1")}},
Ports: []api.EndpointPort{{Port: 111}},
}, {
Addresses: []api.EndpointAddress{{IP: "1.2.3.4", TargetRef: podRef("uid-1")}},
Ports: []api.EndpointPort{{Port: 111}},
}, {
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
Ports: []api.EndpointPort{{Port: 111}},
}},
expect: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{
{IP: "1.2.3.4"},
{IP: "1.2.3.4", TargetRef: podRef("uid-1")},
{IP: "5.6.7.8"},
{IP: "5.6.7.8", TargetRef: podRef("uid-1")},
},
Ports: []api.EndpointPort{{Port: 111}},
}},
}, {
name: "two sets, two mixed ips, two dup ip with uid, dup port, wrong order, ends up with split addresses",
given: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "5.6.7.8"}},
Ports: []api.EndpointPort{{Port: 111}},
}, {
NotReadyAddresses: []api.EndpointAddress{{IP: "5.6.7.8", TargetRef: podRef("uid-1")}},
Ports: []api.EndpointPort{{Port: 111}},
}, {
NotReadyAddresses: []api.EndpointAddress{{IP: "1.2.3.4", TargetRef: podRef("uid-1")}},
Ports: []api.EndpointPort{{Port: 111}},
}, {
NotReadyAddresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
Ports: []api.EndpointPort{{Port: 111}},
}},
expect: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{
{IP: "5.6.7.8"},
},
NotReadyAddresses: []api.EndpointAddress{
{IP: "1.2.3.4"},
{IP: "1.2.3.4", TargetRef: podRef("uid-1")},
{IP: "5.6.7.8", TargetRef: podRef("uid-1")},
},
Ports: []api.EndpointPort{{Port: 111}},
}},
}, {
name: "two sets, two ips, two ports",
given: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
Ports: []api.EndpointPort{{Port: 111}},
}, {
Addresses: []api.EndpointAddress{{IP: "5.6.7.8"}},
Ports: []api.EndpointPort{{Port: 222}},
}},
expect: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
Ports: []api.EndpointPort{{Port: 111}},
}, {
Addresses: []api.EndpointAddress{{IP: "5.6.7.8"}},
Ports: []api.EndpointPort{{Port: 222}},
}},
}, {
name: "four sets, three ips, three ports, jumbled",
given: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
Ports: []api.EndpointPort{{Port: 111}},
}, {
Addresses: []api.EndpointAddress{{IP: "1.2.3.5"}},
Ports: []api.EndpointPort{{Port: 222}},
}, {
Addresses: []api.EndpointAddress{{IP: "1.2.3.6"}},
Ports: []api.EndpointPort{{Port: 111}},
}, {
Addresses: []api.EndpointAddress{{IP: "1.2.3.5"}},
Ports: []api.EndpointPort{{Port: 333}},
}},
expect: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}, {IP: "1.2.3.6"}},
Ports: []api.EndpointPort{{Port: 111}},
}, {
Addresses: []api.EndpointAddress{{IP: "1.2.3.5"}},
Ports: []api.EndpointPort{{Port: 222}, {Port: 333}},
}},
}, {
name: "four sets, three mixed ips, three ports, jumbled",
given: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}},
Ports: []api.EndpointPort{{Port: 111}},
}, {
NotReadyAddresses: []api.EndpointAddress{{IP: "1.2.3.5"}},
Ports: []api.EndpointPort{{Port: 222}},
}, {
Addresses: []api.EndpointAddress{{IP: "1.2.3.6"}},
Ports: []api.EndpointPort{{Port: 111}},
}, {
NotReadyAddresses: []api.EndpointAddress{{IP: "1.2.3.5"}},
Ports: []api.EndpointPort{{Port: 333}},
}},
expect: []api.EndpointSubset{{
Addresses: []api.EndpointAddress{{IP: "1.2.3.4"}, {IP: "1.2.3.6"}},
Ports: []api.EndpointPort{{Port: 111}},
}, {
NotReadyAddresses: []api.EndpointAddress{{IP: "1.2.3.5"}},
Ports: []api.EndpointPort{{Port: 222}, {Port: 333}},
}},
},
}
for _, tc := range testCases {
result := RepackSubsets(tc.given)
if !reflect.DeepEqual(result, SortSubsets(tc.expect)) {
t.Errorf("case %q: expected %s, got %s", tc.name, spew.Sprintf("%#v", SortSubsets(tc.expect)), spew.Sprintf("%#v", result))
}
}
}

40
vendor/k8s.io/kubernetes/pkg/api/errors/BUILD generated vendored Normal file
View file

@ -0,0 +1,40 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
"go_test",
"cgo_library",
)
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"errors.go",
],
tags = ["automanaged"],
deps = [
"//pkg/apis/meta/v1:go_default_library",
"//pkg/runtime:go_default_library",
"//pkg/runtime/schema:go_default_library",
"//pkg/util/validation/field:go_default_library",
],
)
go_test(
name = "go_default_test",
srcs = ["errors_test.go"],
library = "go_default_library",
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/apis/meta/v1:go_default_library",
"//pkg/runtime:go_default_library",
"//pkg/runtime/schema:go_default_library",
"//pkg/util/validation/field:go_default_library",
],
)

28
vendor/k8s.io/kubernetes/pkg/api/errors/OWNERS generated vendored Executable file
View file

@ -0,0 +1,28 @@
reviewers:
- thockin
- lavalamp
- smarterclayton
- wojtek-t
- bgrant0607
- 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/kubernetes/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/kubernetes/pkg/api/errors"

471
vendor/k8s.io/kubernetes/pkg/api/errors/errors.go generated vendored Normal file
View file

@ -0,0 +1,471 @@
/*
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/kubernetes/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/runtime/schema"
"k8s.io/kubernetes/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. Used by pkg/apiserver.
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
message = "the server does not allow access to the requested resource"
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
}
// 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
}

190
vendor/k8s.io/kubernetes/pkg/api/errors/errors_test.go generated vendored Normal file
View file

@ -0,0 +1,190 @@
/*
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"
"k8s.io/kubernetes/pkg/api"
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/util/validation/field"
)
func TestErrorNew(t *testing.T) {
err := NewAlreadyExists(api.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(api.Resource("tests"), "2", errors.New("message"))) {
t.Errorf("expected to be conflict")
}
if !IsNotFound(NewNotFound(api.Resource("tests"), "3")) {
t.Errorf("expected to be %s", metav1.StatusReasonNotFound)
}
if !IsInvalid(NewInvalid(api.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(api.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(api.Resource("tests"), "reason", 0)) {
t.Errorf("expected to be %s", metav1.StatusReasonServerTimeout)
}
if time, ok := SuggestsClientDelay(NewServerTimeout(api.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(api.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(api.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)
}
}
}

25
vendor/k8s.io/kubernetes/pkg/api/errors/storage/BUILD generated vendored Normal file
View file

@ -0,0 +1,25 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
"go_test",
"cgo_library",
)
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"storage.go",
],
tags = ["automanaged"],
deps = [
"//pkg/api/errors:go_default_library",
"//pkg/runtime/schema:go_default_library",
"//pkg/storage:go_default_library",
],
)

18
vendor/k8s.io/kubernetes/pkg/api/errors/storage/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 etcd provides conversion of etcd errors to API errors.
package storage // import "k8s.io/kubernetes/pkg/api/errors/storage"

View file

@ -0,0 +1,108 @@
/*
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 storage
import (
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/storage"
)
// InterpretListError converts a generic error on a retrieval
// operation into the appropriate API error.
func InterpretListError(err error, qualifiedResource schema.GroupResource) error {
switch {
case storage.IsNotFound(err):
return errors.NewNotFound(qualifiedResource, "")
case storage.IsUnreachable(err):
return errors.NewServerTimeout(qualifiedResource, "list", 2) // TODO: make configurable or handled at a higher level
default:
return err
}
}
// InterpretGetError converts a generic error on a retrieval
// operation into the appropriate API error.
func InterpretGetError(err error, qualifiedResource schema.GroupResource, name string) error {
switch {
case storage.IsNotFound(err):
return errors.NewNotFound(qualifiedResource, name)
case storage.IsUnreachable(err):
return errors.NewServerTimeout(qualifiedResource, "get", 2) // TODO: make configurable or handled at a higher level
default:
return err
}
}
// InterpretCreateError converts a generic error on a create
// operation into the appropriate API error.
func InterpretCreateError(err error, qualifiedResource schema.GroupResource, name string) error {
switch {
case storage.IsNodeExist(err):
return errors.NewAlreadyExists(qualifiedResource, name)
case storage.IsUnreachable(err):
return errors.NewServerTimeout(qualifiedResource, "create", 2) // TODO: make configurable or handled at a higher level
default:
return err
}
}
// InterpretUpdateError converts a generic error on an update
// operation into the appropriate API error.
func InterpretUpdateError(err error, qualifiedResource schema.GroupResource, name string) error {
switch {
case storage.IsTestFailed(err), storage.IsNodeExist(err), storage.IsInvalidObj(err):
return errors.NewConflict(qualifiedResource, name, err)
case storage.IsUnreachable(err):
return errors.NewServerTimeout(qualifiedResource, "update", 2) // TODO: make configurable or handled at a higher level
case storage.IsNotFound(err):
return errors.NewNotFound(qualifiedResource, name)
case storage.IsInternalError(err):
return errors.NewInternalError(err)
default:
return err
}
}
// InterpretDeleteError converts a generic error on a delete
// operation into the appropriate API error.
func InterpretDeleteError(err error, qualifiedResource schema.GroupResource, name string) error {
switch {
case storage.IsNotFound(err):
return errors.NewNotFound(qualifiedResource, name)
case storage.IsUnreachable(err):
return errors.NewServerTimeout(qualifiedResource, "delete", 2) // TODO: make configurable or handled at a higher level
case storage.IsTestFailed(err), storage.IsNodeExist(err), storage.IsInvalidObj(err):
return errors.NewConflict(qualifiedResource, name, err)
case storage.IsInternalError(err):
return errors.NewInternalError(err)
default:
return err
}
}
// InterpretWatchError converts a generic error on a watch
// operation into the appropriate API error.
func InterpretWatchError(err error, resource schema.GroupResource, name string) error {
switch {
case storage.IsInvalidError(err):
invalidError, _ := err.(storage.InvalidError)
return errors.NewInvalid(schema.GroupKind{Group: resource.Group, Kind: resource.Resource}, name, invalidError.Errs)
default:
return err
}
}

29
vendor/k8s.io/kubernetes/pkg/api/events/BUILD generated vendored Normal file
View file

@ -0,0 +1,29 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
"go_test",
"cgo_library",
)
go_library(
name = "go_default_library",
srcs = ["sorted_event_list.go"],
tags = ["automanaged"],
deps = ["//pkg/api:go_default_library"],
)
go_test(
name = "go_default_test",
srcs = ["sorted_event_list_test.go"],
library = "go_default_library",
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/apis/meta/v1:go_default_library",
],
)

2
vendor/k8s.io/kubernetes/pkg/api/events/OWNERS generated vendored Executable file
View file

@ -0,0 +1,2 @@
reviewers:
- kargakis

View file

@ -0,0 +1,36 @@
/*
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 events
import (
"k8s.io/kubernetes/pkg/api"
)
// SortableEvents implements sort.Interface for []api.Event based on the Timestamp field
type SortableEvents []api.Event
func (list SortableEvents) Len() int {
return len(list)
}
func (list SortableEvents) Swap(i, j int) {
list[i], list[j] = list[j], list[i]
}
func (list SortableEvents) Less(i, j int) bool {
return list[i].LastTimestamp.Time.Before(list[j].LastTimestamp.Time)
}

View file

@ -0,0 +1,66 @@
/*
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 events
import (
"sort"
"testing"
"time"
"k8s.io/kubernetes/pkg/api"
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
)
func TestSortableEvents(t *testing.T) {
// Arrange
list := SortableEvents([]api.Event{
{
Source: api.EventSource{Component: "kubelet"},
Message: "Item 1",
FirstTimestamp: metav1.NewTime(time.Date(2014, time.January, 15, 0, 0, 0, 0, time.UTC)),
LastTimestamp: metav1.NewTime(time.Date(2014, time.January, 15, 0, 0, 0, 0, time.UTC)),
Count: 1,
Type: api.EventTypeNormal,
},
{
Source: api.EventSource{Component: "scheduler"},
Message: "Item 2",
FirstTimestamp: metav1.NewTime(time.Date(1987, time.June, 17, 0, 0, 0, 0, time.UTC)),
LastTimestamp: metav1.NewTime(time.Date(1987, time.June, 17, 0, 0, 0, 0, time.UTC)),
Count: 1,
Type: api.EventTypeNormal,
},
{
Source: api.EventSource{Component: "kubelet"},
Message: "Item 3",
FirstTimestamp: metav1.NewTime(time.Date(2002, time.December, 25, 0, 0, 0, 0, time.UTC)),
LastTimestamp: metav1.NewTime(time.Date(2002, time.December, 25, 0, 0, 0, 0, time.UTC)),
Count: 1,
Type: api.EventTypeNormal,
},
})
// Act
sort.Sort(list)
// Assert
if list[0].Message != "Item 2" ||
list[1].Message != "Item 3" ||
list[2].Message != "Item 1" {
t.Fatal("List is not sorted by time. List: ", list)
}
}

38
vendor/k8s.io/kubernetes/pkg/api/field_constants.go generated vendored Normal file
View file

@ -0,0 +1,38 @@
/*
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 api
// Field path constants that are specific to the internal API
// representation.
const (
NodeUnschedulableField = "spec.unschedulable"
ObjectNameField = "metadata.name"
PodHostField = "spec.nodeName"
PodStatusField = "status.phase"
SecretTypeField = "type"
EventReasonField = "reason"
EventSourceField = "source"
EventTypeField = "type"
EventInvolvedKindField = "involvedObject.kind"
EventInvolvedNamespaceField = "involvedObject.namespace"
EventInvolvedNameField = "involvedObject.name"
EventInvolvedUIDField = "involvedObject.uid"
EventInvolvedAPIVersionField = "involvedObject.apiVersion"
EventInvolvedResourceVersionField = "involvedObject.resourceVersion"
EventInvolvedFieldPathField = "involvedObject.fieldPath"
)

64
vendor/k8s.io/kubernetes/pkg/api/generate.go generated vendored Normal file
View file

@ -0,0 +1,64 @@
/*
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 api
import (
"fmt"
utilrand "k8s.io/kubernetes/pkg/util/rand"
)
// NameGenerator generates names for objects. Some backends may have more information
// available to guide selection of new names and this interface hides those details.
type NameGenerator interface {
// GenerateName generates a valid name from the base name, adding a random suffix to the
// the base. If base is valid, the returned name must also be valid. The generator is
// responsible for knowing the maximum valid name length.
GenerateName(base string) string
}
// GenerateName will resolve the object name of the provided ObjectMeta to a generated version if
// necessary. It expects that validation for ObjectMeta has already completed (that Base is a
// valid name) and that the NameGenerator generates a name that is also valid.
func GenerateName(u NameGenerator, meta *ObjectMeta) {
if len(meta.GenerateName) == 0 || len(meta.Name) != 0 {
return
}
meta.Name = u.GenerateName(meta.GenerateName)
}
// simpleNameGenerator generates random names.
type simpleNameGenerator struct{}
// SimpleNameGenerator is a generator that returns the name plus a random suffix of five alphanumerics
// when a name is requested. The string is guaranteed to not exceed the length of a standard Kubernetes
// name (63 characters)
var SimpleNameGenerator NameGenerator = simpleNameGenerator{}
const (
// TODO: make this flexible for non-core resources with alternate naming rules.
maxNameLength = 63
randomLength = 5
maxGeneratedNameLength = maxNameLength - randomLength
)
func (simpleNameGenerator) GenerateName(base string) string {
if len(base) > maxGeneratedNameLength {
base = base[:maxGeneratedNameLength]
}
return fmt.Sprintf("%s%s", base, utilrand.String(randomLength))
}

79
vendor/k8s.io/kubernetes/pkg/api/generate_test.go generated vendored Normal file
View file

@ -0,0 +1,79 @@
/*
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 api
import (
"strings"
"testing"
)
type nameGeneratorFunc func(base string) string
func (fn nameGeneratorFunc) GenerateName(base string) string {
return fn(base)
}
func TestGenerateName(t *testing.T) {
testCases := []struct {
meta ObjectMeta
base string
returned string
}{
{
returned: "",
},
{
meta: ObjectMeta{
GenerateName: "test",
},
base: "test",
returned: "test",
},
{
meta: ObjectMeta{
Name: "foo",
GenerateName: "test",
},
base: "test",
returned: "foo",
},
}
for i, testCase := range testCases {
GenerateName(nameGeneratorFunc(func(base string) string {
if base != testCase.base {
t.Errorf("%d: unexpected call with base", i)
}
return "test"
}), &testCase.meta)
expect := testCase.returned
if expect != testCase.meta.Name {
t.Errorf("%d: unexpected name: %#v", i, testCase.meta)
}
}
}
func TestSimpleNameGenerator(t *testing.T) {
meta := &ObjectMeta{
GenerateName: "foo",
}
GenerateName(SimpleNameGenerator, meta)
if !strings.HasPrefix(meta.Name, "foo") || meta.Name == "foo" {
t.Errorf("unexpected name: %#v", meta)
}
}

631
vendor/k8s.io/kubernetes/pkg/api/helpers.go generated vendored Normal file

File diff suppressed because it is too large Load diff

541
vendor/k8s.io/kubernetes/pkg/api/helpers_test.go generated vendored Normal file

File diff suppressed because it is too large Load diff

42
vendor/k8s.io/kubernetes/pkg/api/install/BUILD generated vendored Normal file
View file

@ -0,0 +1,42 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
"go_test",
"cgo_library",
)
go_library(
name = "go_default_library",
srcs = ["install.go"],
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/api/meta:go_default_library",
"//pkg/api/v1:go_default_library",
"//pkg/apimachinery:go_default_library",
"//pkg/apimachinery/registered:go_default_library",
"//pkg/runtime:go_default_library",
"//pkg/runtime/schema:go_default_library",
"//pkg/util/sets:go_default_library",
"//vendor:github.com/golang/glog",
],
)
go_test(
name = "go_default_test",
srcs = ["install_test.go"],
library = "go_default_library",
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/apimachinery/registered:go_default_library",
"//pkg/apis/meta/v1:go_default_library",
"//pkg/runtime:go_default_library",
"//pkg/runtime/schema:go_default_library",
],
)

11
vendor/k8s.io/kubernetes/pkg/api/install/OWNERS generated vendored Executable file
View file

@ -0,0 +1,11 @@
reviewers:
- lavalamp
- smarterclayton
- deads2k
- caesarxuchao
- liggitt
- nikhiljindal
- dims
- krousey
- david-mcmahon
- feihujiang

153
vendor/k8s.io/kubernetes/pkg/api/install/install.go generated vendored Normal file
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 install installs the v1 monolithic api, making it available as an
// option to all of the API encoding/decoding machinery.
package install
import (
"fmt"
"github.com/golang/glog"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/apimachinery"
"k8s.io/kubernetes/pkg/apimachinery/registered"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/util/sets"
)
const importPrefix = "k8s.io/kubernetes/pkg/api"
var accessor = meta.NewAccessor()
// availableVersions lists all known external versions for this group from most preferred to least preferred
var availableVersions = []schema.GroupVersion{v1.SchemeGroupVersion}
func init() {
registered.RegisterVersions(availableVersions)
externalVersions := []schema.GroupVersion{}
for _, v := range availableVersions {
if registered.IsAllowedVersion(v) {
externalVersions = append(externalVersions, v)
}
}
if len(externalVersions) == 0 {
glog.V(4).Infof("No version is registered for group %v", api.GroupName)
return
}
if err := registered.EnableVersions(externalVersions...); err != nil {
glog.V(4).Infof("%v", err)
return
}
if err := enableVersions(externalVersions); err != nil {
glog.V(4).Infof("%v", err)
return
}
}
// TODO: enableVersions should be centralized rather than spread in each API
// group.
// We can combine registered.RegisterVersions, registered.EnableVersions and
// registered.RegisterGroup once we have moved enableVersions there.
func enableVersions(externalVersions []schema.GroupVersion) error {
addVersionsToScheme(externalVersions...)
preferredExternalVersion := externalVersions[0]
groupMeta := apimachinery.GroupMeta{
GroupVersion: preferredExternalVersion,
GroupVersions: externalVersions,
RESTMapper: newRESTMapper(externalVersions),
SelfLinker: runtime.SelfLinker(accessor),
InterfacesFor: interfacesFor,
}
if err := registered.RegisterGroup(groupMeta); err != nil {
return err
}
return nil
}
func newRESTMapper(externalVersions []schema.GroupVersion) 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(
"Node",
"Namespace",
"PersistentVolume",
"ComponentStatus",
)
// these kinds should be excluded from the list of resources
ignoredKinds := sets.NewString(
"ListOptions",
"DeleteOptions",
"Status",
"PodLogOptions",
"PodExecOptions",
"PodAttachOptions",
"PodProxyOptions",
"NodeProxyOptions",
"ServiceProxyOptions",
"ThirdPartyResource",
"ThirdPartyResourceData",
"ThirdPartyResourceList")
mapper := api.NewDefaultRESTMapper(externalVersions, interfacesFor, importPrefix, ignoredKinds, rootScoped)
return mapper
}
// InterfacesFor returns the default Codec and ResourceVersioner for a given version
// string, or an error if the version is not known.
func interfacesFor(version schema.GroupVersion) (*meta.VersionInterfaces, error) {
switch version {
case v1.SchemeGroupVersion:
return &meta.VersionInterfaces{
ObjectConvertor: api.Scheme,
MetadataAccessor: accessor,
}, nil
default:
g, _ := registered.Group(api.GroupName)
return nil, fmt.Errorf("unsupported storage version: %s (valid: %v)", version, g.GroupVersions)
}
}
func addVersionsToScheme(externalVersions ...schema.GroupVersion) {
// add the internal version to Scheme
if err := api.AddToScheme(api.Scheme); err != nil {
// Programmer error, detect immediately
panic(err)
}
// add the enabled external versions to Scheme
for _, v := range externalVersions {
if !registered.IsEnabledVersion(v) {
glog.Errorf("Version %s is not enabled, so it will not be added to the Scheme.", v)
continue
}
switch v {
case v1.SchemeGroupVersion:
if err := v1.AddToScheme(api.Scheme); err != nil {
// Programmer error, detect immediately
panic(err)
}
}
}
}

View file

@ -0,0 +1,129 @@
/*
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 install
import (
"encoding/json"
"reflect"
"testing"
internal "k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/apimachinery/registered"
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/runtime/schema"
)
func TestResourceVersioner(t *testing.T) {
pod := internal.Pod{ObjectMeta: internal.ObjectMeta{ResourceVersion: "10"}}
version, err := accessor.ResourceVersion(&pod)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if version != "10" {
t.Errorf("unexpected version %v", version)
}
podList := internal.PodList{ListMeta: metav1.ListMeta{ResourceVersion: "10"}}
version, err = accessor.ResourceVersion(&podList)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if version != "10" {
t.Errorf("unexpected version %v", version)
}
}
func TestCodec(t *testing.T) {
pod := internal.Pod{}
// We do want to use package registered rather than testapi here, because we
// want to test if the package install and package registered work as expected.
data, err := runtime.Encode(internal.Codecs.LegacyCodec(registered.GroupOrDie(internal.GroupName).GroupVersion), &pod)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
other := internal.Pod{}
if err := json.Unmarshal(data, &other); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if other.APIVersion != registered.GroupOrDie(internal.GroupName).GroupVersion.Version || other.Kind != "Pod" {
t.Errorf("unexpected unmarshalled object %#v", other)
}
}
func TestInterfacesFor(t *testing.T) {
if _, err := registered.GroupOrDie(internal.GroupName).InterfacesFor(internal.SchemeGroupVersion); err == nil {
t.Fatalf("unexpected non-error: %v", err)
}
for i, version := range registered.GroupOrDie(internal.GroupName).GroupVersions {
if vi, err := registered.GroupOrDie(internal.GroupName).InterfacesFor(version); err != nil || vi == nil {
t.Fatalf("%d: unexpected result: %v", i, err)
}
}
}
func TestRESTMapper(t *testing.T) {
gv := schema.GroupVersion{Group: "", Version: "v1"}
rcGVK := gv.WithKind("ReplicationController")
podTemplateGVK := gv.WithKind("PodTemplate")
if gvk, err := registered.RESTMapper().KindFor(internal.SchemeGroupVersion.WithResource("replicationcontrollers")); err != nil || gvk != rcGVK {
t.Errorf("unexpected version mapping: %v %v", gvk, err)
}
if m, err := registered.GroupOrDie(internal.GroupName).RESTMapper.RESTMapping(podTemplateGVK.GroupKind(), ""); err != nil || m.GroupVersionKind != podTemplateGVK || m.Resource != "podtemplates" {
t.Errorf("unexpected version mapping: %#v %v", m, err)
}
for _, version := range registered.GroupOrDie(internal.GroupName).GroupVersions {
mapping, err := registered.GroupOrDie(internal.GroupName).RESTMapper.RESTMapping(rcGVK.GroupKind(), version.Version)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if mapping.Resource != "replicationControllers" && mapping.Resource != "replicationcontrollers" {
t.Errorf("incorrect resource name: %#v", mapping)
}
if mapping.GroupVersionKind.GroupVersion() != version {
t.Errorf("incorrect version: %v", mapping)
}
interfaces, _ := registered.GroupOrDie(internal.GroupName).InterfacesFor(version)
if mapping.ObjectConvertor != interfaces.ObjectConvertor {
t.Errorf("unexpected: %#v, expected: %#v", mapping, interfaces)
}
rc := &internal.ReplicationController{ObjectMeta: internal.ObjectMeta{Name: "foo"}}
name, err := mapping.MetadataAccessor.Name(rc)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if name != "foo" {
t.Errorf("unable to retrieve object meta with: %v", mapping.MetadataAccessor)
}
}
}
func TestUnversioned(t *testing.T) {
for _, obj := range []runtime.Object{
&metav1.Status{},
} {
if unversioned, ok := internal.Scheme.IsUnversioned(obj); !unversioned || !ok {
t.Errorf("%v is expected to be unversioned", reflect.TypeOf(obj))
}
}
}

58
vendor/k8s.io/kubernetes/pkg/api/mapper.go generated vendored Normal file
View file

@ -0,0 +1,58 @@
/*
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 api
import (
"strings"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/util/sets"
)
// Instantiates a DefaultRESTMapper based on types registered in api.Scheme
func NewDefaultRESTMapper(defaultGroupVersions []schema.GroupVersion, interfacesFunc meta.VersionInterfacesFunc,
importPathPrefix string, ignoredKinds, rootScoped sets.String) *meta.DefaultRESTMapper {
return NewDefaultRESTMapperFromScheme(defaultGroupVersions, interfacesFunc, importPathPrefix, ignoredKinds, rootScoped, Scheme)
}
// Instantiates a DefaultRESTMapper based on types registered in the given scheme.
func NewDefaultRESTMapperFromScheme(defaultGroupVersions []schema.GroupVersion, interfacesFunc meta.VersionInterfacesFunc,
importPathPrefix string, ignoredKinds, rootScoped sets.String, scheme *runtime.Scheme) *meta.DefaultRESTMapper {
mapper := meta.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 := meta.RESTScopeNamespace
if rootScoped.Has(kind) {
scope = meta.RESTScopeRoot
}
mapper.Add(gvk, scope)
}
}
return mapper
}

140
vendor/k8s.io/kubernetes/pkg/api/meta.go generated vendored Normal file
View file

@ -0,0 +1,140 @@
/*
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 api
import (
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/meta/metatypes"
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/conversion"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/types"
"k8s.io/kubernetes/pkg/util/uuid"
)
// FillObjectMetaSystemFields populates fields that are managed by the system on ObjectMeta.
func FillObjectMetaSystemFields(ctx Context, meta *ObjectMeta) {
meta.CreationTimestamp = metav1.Now()
// allows admission controllers to assign a UID earlier in the request processing
// to support tracking resources pending creation.
uid, found := UIDFrom(ctx)
if !found {
uid = uuid.NewUUID()
}
meta.UID = uid
meta.SelfLink = ""
}
// HasObjectMetaSystemFieldValues returns true if fields that are managed by the system on ObjectMeta have values.
func HasObjectMetaSystemFieldValues(meta *ObjectMeta) bool {
return !meta.CreationTimestamp.Time.IsZero() ||
len(meta.UID) != 0
}
// 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.Accessor() 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
func ListMetaFor(obj runtime.Object) (*metav1.ListMeta, error) {
v, err := conversion.EnforcePtr(obj)
if err != nil {
return nil, err
}
var meta *metav1.ListMeta
err = runtime.FieldPtr(v, "ListMeta", &meta)
return meta, err
}
func (obj *ObjectMeta) GetObjectMeta() meta.Object { return obj }
// Namespace implements meta.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() metav1.Time { return meta.CreationTimestamp }
func (meta *ObjectMeta) SetCreationTimestamp(creationTimestamp metav1.Time) {
meta.CreationTimestamp = creationTimestamp
}
func (meta *ObjectMeta) GetDeletionTimestamp() *metav1.Time { return meta.DeletionTimestamp }
func (meta *ObjectMeta) SetDeletionTimestamp(deletionTimestamp *metav1.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() []metatypes.OwnerReference {
ret := make([]metatypes.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 []metatypes.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
}

78
vendor/k8s.io/kubernetes/pkg/api/meta/BUILD generated vendored Normal file
View file

@ -0,0 +1,78 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
"go_test",
"cgo_library",
)
go_library(
name = "go_default_library",
srcs = [
"doc.go",
"errors.go",
"firsthit_restmapper.go",
"help.go",
"interfaces.go",
"meta.go",
"multirestmapper.go",
"priority.go",
"restmapper.go",
"unstructured.go",
],
tags = ["automanaged"],
deps = [
"//pkg/api/meta/metatypes:go_default_library",
"//pkg/apis/meta/v1:go_default_library",
"//pkg/conversion:go_default_library",
"//pkg/runtime:go_default_library",
"//pkg/runtime/schema:go_default_library",
"//pkg/types:go_default_library",
"//pkg/util/errors:go_default_library",
"//pkg/util/sets:go_default_library",
"//vendor:github.com/golang/glog",
],
)
go_test(
name = "go_default_test",
srcs = [
"multirestmapper_test.go",
"priority_test.go",
"restmapper_test.go",
],
library = "go_default_library",
tags = ["automanaged"],
deps = [
"//pkg/runtime:go_default_library",
"//pkg/runtime/schema:go_default_library",
],
)
go_test(
name = "go_default_xtest",
srcs = [
"help_test.go",
"meta_test.go",
"scheme_test.go",
],
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/api/meta:go_default_library",
"//pkg/api/meta/metatypes:go_default_library",
"//pkg/api/testapi:go_default_library",
"//pkg/api/v1:go_default_library",
"//pkg/apis/meta/v1:go_default_library",
"//pkg/runtime:go_default_library",
"//pkg/runtime/schema:go_default_library",
"//pkg/types:go_default_library",
"//pkg/util/diff:go_default_library",
"//pkg/util/sets:go_default_library",
"//vendor:github.com/google/gofuzz",
],
)

26
vendor/k8s.io/kubernetes/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

19
vendor/k8s.io/kubernetes/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/kubernetes/pkg/api/meta"

105
vendor/k8s.io/kubernetes/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/kubernetes/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/kubernetes/pkg/runtime/schema"
utilerrors "k8s.io/kubernetes/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)
}

201
vendor/k8s.io/kubernetes/pkg/api/meta/help.go generated vendored Normal file
View file

@ -0,0 +1,201 @@
/*
Copyright 2015 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package meta
import (
"fmt"
"reflect"
"k8s.io/kubernetes/pkg/conversion"
"k8s.io/kubernetes/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 to see if we have an `items` key
// This is a list type for recognition, but other Items type methods will fail on it
// and give you errors.
if unstructured, ok := obj.(*runtime.Unstructured); ok {
_, ok := unstructured.Object["items"]
return ok
}
_, 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
}

344
vendor/k8s.io/kubernetes/pkg/api/meta/help_test.go generated vendored Normal file
View file

@ -0,0 +1,344 @@
/*
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_test
import (
"reflect"
"testing"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/v1"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/util/diff"
"github.com/google/gofuzz"
)
func TestIsList(t *testing.T) {
tests := []struct {
obj runtime.Object
isList bool
}{
{&api.PodList{}, true},
{&api.Pod{}, false},
}
for _, item := range tests {
if e, a := item.isList, meta.IsListType(item.obj); e != a {
t.Errorf("%v: Expected %v, got %v", reflect.TypeOf(item.obj), e, a)
}
}
}
func TestExtractList(t *testing.T) {
list1 := []runtime.Object{
&api.Pod{ObjectMeta: api.ObjectMeta{Name: "1"}},
&api.Service{ObjectMeta: api.ObjectMeta{Name: "2"}},
}
list2 := &v1.List{
Items: []runtime.RawExtension{
{Raw: []byte("foo")},
{Raw: []byte("bar")},
{Object: &v1.Pod{ObjectMeta: v1.ObjectMeta{Name: "other"}}},
},
}
list3 := &fakePtrValueList{
Items: []*api.Pod{
{ObjectMeta: api.ObjectMeta{Name: "1"}},
{ObjectMeta: api.ObjectMeta{Name: "2"}},
},
}
list4 := &api.PodList{
Items: []api.Pod{
{ObjectMeta: api.ObjectMeta{Name: "1"}},
{ObjectMeta: api.ObjectMeta{Name: "2"}},
{ObjectMeta: api.ObjectMeta{Name: "3"}},
},
}
list5 := &v1.PodList{
Items: []v1.Pod{
{ObjectMeta: v1.ObjectMeta{Name: "1"}},
{ObjectMeta: v1.ObjectMeta{Name: "2"}},
{ObjectMeta: v1.ObjectMeta{Name: "3"}},
},
}
testCases := []struct {
in runtime.Object
out []interface{}
equal bool
}{
{
in: &api.List{},
out: []interface{}{},
},
{
in: &v1.List{},
out: []interface{}{},
},
{
in: &v1.PodList{},
out: []interface{}{},
},
{
in: &api.List{Items: list1},
out: []interface{}{list1[0], list1[1]},
},
{
in: list2,
out: []interface{}{&runtime.Unknown{Raw: list2.Items[0].Raw}, &runtime.Unknown{Raw: list2.Items[1].Raw}, list2.Items[2].Object},
equal: true,
},
{
in: list3,
out: []interface{}{list3.Items[0], list3.Items[1]},
},
{
in: list4,
out: []interface{}{&list4.Items[0], &list4.Items[1], &list4.Items[2]},
},
{
in: list5,
out: []interface{}{&list5.Items[0], &list5.Items[1], &list5.Items[2]},
},
}
for i, test := range testCases {
list, err := meta.ExtractList(test.in)
if err != nil {
t.Fatalf("%d: extract: Unexpected error %v", i, err)
}
if e, a := len(test.out), len(list); e != a {
t.Fatalf("%d: extract: Expected %v, got %v", i, e, a)
}
for j, e := range test.out {
if e != list[j] {
if !test.equal {
t.Fatalf("%d: extract: Expected list[%d] to be %#v, but found %#v", i, j, e, list[j])
}
if !reflect.DeepEqual(e, list[j]) {
t.Fatalf("%d: extract: Expected list[%d] to be %#v, but found %#v", i, j, e, list[j])
}
}
}
}
}
func TestEachListItem(t *testing.T) {
list1 := []runtime.Object{
&api.Pod{ObjectMeta: api.ObjectMeta{Name: "1"}},
&api.Service{ObjectMeta: api.ObjectMeta{Name: "2"}},
}
list2 := &v1.List{
Items: []runtime.RawExtension{
{Raw: []byte("foo")},
{Raw: []byte("bar")},
{Object: &v1.Pod{ObjectMeta: v1.ObjectMeta{Name: "other"}}},
},
}
list3 := &fakePtrValueList{
Items: []*api.Pod{
{ObjectMeta: api.ObjectMeta{Name: "1"}},
{ObjectMeta: api.ObjectMeta{Name: "2"}},
},
}
list4 := &api.PodList{
Items: []api.Pod{
{ObjectMeta: api.ObjectMeta{Name: "1"}},
{ObjectMeta: api.ObjectMeta{Name: "2"}},
{ObjectMeta: api.ObjectMeta{Name: "3"}},
},
}
list5 := &v1.PodList{
Items: []v1.Pod{
{ObjectMeta: v1.ObjectMeta{Name: "1"}},
{ObjectMeta: v1.ObjectMeta{Name: "2"}},
{ObjectMeta: v1.ObjectMeta{Name: "3"}},
},
}
testCases := []struct {
in runtime.Object
out []interface{}
}{
{
in: &api.List{},
out: []interface{}{},
},
{
in: &v1.List{},
out: []interface{}{},
},
{
in: &v1.PodList{},
out: []interface{}{},
},
{
in: &api.List{Items: list1},
out: []interface{}{list1[0], list1[1]},
},
{
in: list2,
out: []interface{}{nil, nil, list2.Items[2].Object},
},
{
in: list3,
out: []interface{}{list3.Items[0], list3.Items[1]},
},
{
in: list4,
out: []interface{}{&list4.Items[0], &list4.Items[1], &list4.Items[2]},
},
{
in: list5,
out: []interface{}{&list5.Items[0], &list5.Items[1], &list5.Items[2]},
},
}
for i, test := range testCases {
list := []runtime.Object{}
err := meta.EachListItem(test.in, func(obj runtime.Object) error {
list = append(list, obj)
return nil
})
if err != nil {
t.Fatalf("%d: each: Unexpected error %v", i, err)
}
if e, a := len(test.out), len(list); e != a {
t.Fatalf("%d: each: Expected %v, got %v", i, e, a)
}
for j, e := range test.out {
if e != list[j] {
t.Fatalf("%d: each: Expected list[%d] to be %#v, but found %#v", i, j, e, list[j])
}
}
}
}
type fakePtrInterfaceList struct {
Items *[]runtime.Object
}
func (obj fakePtrInterfaceList) GetObjectKind() schema.ObjectKind {
return schema.EmptyObjectKind
}
func TestExtractListOfInterfacePtrs(t *testing.T) {
pl := &fakePtrInterfaceList{
Items: &[]runtime.Object{},
}
list, err := meta.ExtractList(pl)
if err != nil {
t.Fatalf("Unexpected error %v", err)
}
if len(list) > 0 {
t.Fatalf("Expected empty list, got %#v", list)
}
}
type fakePtrValueList struct {
Items []*api.Pod
}
func (obj fakePtrValueList) GetObjectKind() schema.ObjectKind {
return schema.EmptyObjectKind
}
func TestSetList(t *testing.T) {
pl := &api.PodList{}
list := []runtime.Object{
&api.Pod{ObjectMeta: api.ObjectMeta{Name: "1"}},
&api.Pod{ObjectMeta: api.ObjectMeta{Name: "2"}},
&api.Pod{ObjectMeta: api.ObjectMeta{Name: "3"}},
}
err := meta.SetList(pl, list)
if err != nil {
t.Fatalf("Unexpected error %v", err)
}
if e, a := len(list), len(pl.Items); e != a {
t.Fatalf("Expected %v, got %v", e, a)
}
for i := range list {
if e, a := list[i].(*api.Pod).Name, pl.Items[i].Name; e != a {
t.Fatalf("Expected %v, got %v", e, a)
}
}
}
func TestSetListToRuntimeObjectArray(t *testing.T) {
pl := &api.List{}
list := []runtime.Object{
&api.Pod{ObjectMeta: api.ObjectMeta{Name: "1"}},
&api.Pod{ObjectMeta: api.ObjectMeta{Name: "2"}},
&api.Pod{ObjectMeta: api.ObjectMeta{Name: "3"}},
}
err := meta.SetList(pl, list)
if err != nil {
t.Fatalf("Unexpected error %v", err)
}
if e, a := len(list), len(pl.Items); e != a {
t.Fatalf("Expected %v, got %v", e, a)
}
for i := range list {
if e, a := list[i], pl.Items[i]; e != a {
t.Fatalf("%d: unmatched: %s", i, diff.ObjectDiff(e, a))
}
}
}
func TestSetListToMatchingType(t *testing.T) {
pl := &runtime.UnstructuredList{}
list := []runtime.Object{
&runtime.Unstructured{Object: map[string]interface{}{"foo": 1}},
&runtime.Unstructured{Object: map[string]interface{}{"foo": 2}},
&runtime.Unstructured{Object: map[string]interface{}{"foo": 3}},
}
err := meta.SetList(pl, list)
if err != nil {
t.Fatalf("Unexpected error %v", err)
}
if e, a := len(list), len(pl.Items); e != a {
t.Fatalf("Expected %v, got %v", e, a)
}
for i := range list {
if e, a := list[i], pl.Items[i]; e != a {
t.Fatalf("%d: unmatched: %s", i, diff.ObjectDiff(e, a))
}
}
}
func TestSetExtractListRoundTrip(t *testing.T) {
fuzzer := fuzz.New().NilChance(0).NumElements(1, 5)
for i := 0; i < 5; i++ {
start := &api.PodList{}
fuzzer.Fuzz(&start.Items)
list, err := meta.ExtractList(start)
if err != nil {
t.Errorf("Unexpected error %v", err)
continue
}
got := &api.PodList{}
err = meta.SetList(got, list)
if err != nil {
t.Errorf("Unexpected error %v", err)
continue
}
if e, a := start, got; !reflect.DeepEqual(e, a) {
t.Fatalf("Expected %#v, got %#v", e, a)
}
}
}

185
vendor/k8s.io/kubernetes/pkg/api/meta/interfaces.go generated vendored Normal file
View file

@ -0,0 +1,185 @@
/*
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 (
"k8s.io/kubernetes/pkg/api/meta/metatypes"
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/types"
)
// VersionInterfaces contains the interfaces one should use for dealing with types of a particular version.
type VersionInterfaces struct {
runtime.ObjectConvertor
MetadataAccessor
}
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() metav1.Time
SetCreationTimestamp(timestamp metav1.Time)
GetDeletionTimestamp() *metav1.Time
SetDeletionTimestamp(timestamp *metav1.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() []metatypes.OwnerReference
SetOwnerReferences([]metatypes.OwnerReference)
GetClusterName() string
SetClusterName(clusterName string)
}
var _ Object = &runtime.Unstructured{}
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.
RESTMappings(gk schema.GroupKind) ([]*RESTMapping, error)
AliasesForResource(resource string) ([]string, bool)
ResourceSingularizer(resource string) (singular string, err error)
}

568
vendor/k8s.io/kubernetes/pkg/api/meta/meta.go generated vendored Normal file

File diff suppressed because it is too large Load diff

421
vendor/k8s.io/kubernetes/pkg/api/meta/meta_test.go generated vendored Normal file
View file

@ -0,0 +1,421 @@
/*
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_test
import (
"reflect"
"testing"
"github.com/google/gofuzz"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/meta/metatypes"
"k8s.io/kubernetes/pkg/api/v1"
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/types"
)
func TestAPIObjectMeta(t *testing.T) {
j := &api.Pod{
TypeMeta: metav1.TypeMeta{APIVersion: "/a", Kind: "b"},
ObjectMeta: api.ObjectMeta{
Namespace: "bar",
Name: "foo",
GenerateName: "prefix",
UID: "uid",
ResourceVersion: "1",
SelfLink: "some/place/only/we/know",
Labels: map[string]string{"foo": "bar"},
Annotations: map[string]string{"x": "y"},
Finalizers: []string{
"finalizer.1",
"finalizer.2",
},
},
}
var _ meta.Object = &j.ObjectMeta
var _ meta.ObjectMetaAccessor = j
accessor, err := meta.Accessor(j)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if accessor != meta.Object(j) {
t.Fatalf("should have returned the same pointer: %#v\n\n%#v", accessor, j)
}
if e, a := "bar", accessor.GetNamespace(); e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := "foo", accessor.GetName(); e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := "prefix", accessor.GetGenerateName(); e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := "uid", string(accessor.GetUID()); e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := "1", accessor.GetResourceVersion(); e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := "some/place/only/we/know", accessor.GetSelfLink(); e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := []string{"finalizer.1", "finalizer.2"}, accessor.GetFinalizers(); !reflect.DeepEqual(e, a) {
t.Errorf("expected %v, got %v", e, a)
}
typeAccessor, err := meta.TypeAccessor(j)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if e, a := "a", typeAccessor.GetAPIVersion(); e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := "b", typeAccessor.GetKind(); e != a {
t.Errorf("expected %v, got %v", e, a)
}
accessor.SetNamespace("baz")
accessor.SetName("bar")
accessor.SetGenerateName("generate")
accessor.SetUID("other")
typeAccessor.SetAPIVersion("c")
typeAccessor.SetKind("d")
accessor.SetResourceVersion("2")
accessor.SetSelfLink("google.com")
accessor.SetFinalizers([]string{"finalizer.3"})
// Prove that accessor changes the original object.
if e, a := "baz", j.Namespace; e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := "bar", j.Name; e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := "generate", j.GenerateName; e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := types.UID("other"), j.UID; e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := "c", j.APIVersion; e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := "d", j.Kind; e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := "2", j.ResourceVersion; e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := "google.com", j.SelfLink; e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := []string{"finalizer.3"}, j.Finalizers; !reflect.DeepEqual(e, a) {
t.Errorf("expected %v, got %v", e, a)
}
typeAccessor.SetAPIVersion("d")
typeAccessor.SetKind("e")
if e, a := "d", j.APIVersion; e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := "e", j.Kind; e != a {
t.Errorf("expected %v, got %v", e, a)
}
}
func TestGenericTypeMeta(t *testing.T) {
type TypeMeta struct {
Kind string `json:"kind,omitempty"`
Namespace string `json:"namespace,omitempty"`
Name string `json:"name,omitempty"`
GenerateName string `json:"generateName,omitempty"`
UID string `json:"uid,omitempty"`
CreationTimestamp metav1.Time `json:"creationTimestamp,omitempty"`
SelfLink string `json:"selfLink,omitempty"`
ResourceVersion string `json:"resourceVersion,omitempty"`
APIVersion string `json:"apiVersion,omitempty"`
Labels map[string]string `json:"labels,omitempty"`
Annotations map[string]string `json:"annotations,omitempty"`
OwnerReferences []api.OwnerReference `json:"ownerReferences,omitempty"`
Finalizers []string `json:"finalizers,omitempty"`
}
j := struct{ TypeMeta }{TypeMeta{APIVersion: "a", Kind: "b"}}
typeAccessor, err := meta.TypeAccessor(&j)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if e, a := "a", typeAccessor.GetAPIVersion(); e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := "b", typeAccessor.GetKind(); e != a {
t.Errorf("expected %v, got %v", e, a)
}
typeAccessor.SetAPIVersion("c")
typeAccessor.SetKind("d")
if e, a := "c", j.APIVersion; e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := "d", j.Kind; e != a {
t.Errorf("expected %v, got %v", e, a)
}
typeAccessor.SetAPIVersion("d")
typeAccessor.SetKind("e")
if e, a := "d", j.APIVersion; e != a {
t.Errorf("expected %v, got %v", e, a)
}
if e, a := "e", j.Kind; e != a {
t.Errorf("expected %v, got %v", e, a)
}
}
type InternalTypeMeta struct {
Kind string `json:"kind,omitempty"`
Namespace string `json:"namespace,omitempty"`
Name string `json:"name,omitempty"`
GenerateName string `json:"generateName,omitempty"`
UID string `json:"uid,omitempty"`
CreationTimestamp metav1.Time `json:"creationTimestamp,omitempty"`
SelfLink string `json:"selfLink,omitempty"`
ResourceVersion string `json:"resourceVersion,omitempty"`
APIVersion string `json:"apiVersion,omitempty"`
Labels map[string]string `json:"labels,omitempty"`
Annotations map[string]string `json:"annotations,omitempty"`
Finalizers []string `json:"finalizers,omitempty"`
OwnerReferences []api.OwnerReference `json:"ownerReferences,omitempty"`
}
func (m *InternalTypeMeta) GetResourceVersion() string { return m.ResourceVersion }
func (m *InternalTypeMeta) SetResourceVersion(rv string) { m.ResourceVersion = rv }
func (m *InternalTypeMeta) GetSelfLink() string { return m.SelfLink }
func (m *InternalTypeMeta) SetSelfLink(link string) { m.SelfLink = link }
type MyAPIObject struct {
TypeMeta InternalTypeMeta `json:",inline"`
}
func (obj *MyAPIObject) GetListMeta() metav1.List { return &obj.TypeMeta }
func (obj *MyAPIObject) GetObjectKind() schema.ObjectKind { return obj }
func (obj *MyAPIObject) SetGroupVersionKind(gvk schema.GroupVersionKind) {
obj.TypeMeta.APIVersion, obj.TypeMeta.Kind = gvk.ToAPIVersionAndKind()
}
func (obj *MyAPIObject) GroupVersionKind() schema.GroupVersionKind {
return schema.FromAPIVersionAndKind(obj.TypeMeta.APIVersion, obj.TypeMeta.Kind)
}
type MyIncorrectlyMarkedAsAPIObject struct{}
func (obj *MyIncorrectlyMarkedAsAPIObject) GetObjectKind() schema.ObjectKind {
return schema.EmptyObjectKind
}
func TestResourceVersionerOfAPI(t *testing.T) {
type T struct {
runtime.Object
Expected string
}
testCases := map[string]T{
"empty api object": {&MyAPIObject{}, ""},
"api object with version": {&MyAPIObject{TypeMeta: InternalTypeMeta{ResourceVersion: "1"}}, "1"},
"pointer to api object with version": {&MyAPIObject{TypeMeta: InternalTypeMeta{ResourceVersion: "1"}}, "1"},
}
versioning := meta.NewAccessor()
for key, testCase := range testCases {
actual, err := versioning.ResourceVersion(testCase.Object)
if err != nil {
t.Errorf("%s: unexpected error %#v", key, err)
}
if actual != testCase.Expected {
t.Errorf("%s: expected %v, got %v", key, testCase.Expected, actual)
}
}
failingCases := map[string]struct {
runtime.Object
Expected string
}{
"not a valid object to try": {&MyIncorrectlyMarkedAsAPIObject{}, "1"},
}
for key, testCase := range failingCases {
_, err := versioning.ResourceVersion(testCase.Object)
if err == nil {
t.Errorf("%s: expected error, got nil", key)
}
}
setCases := map[string]struct {
runtime.Object
Expected string
}{
"pointer to api object with version": {&MyAPIObject{TypeMeta: InternalTypeMeta{ResourceVersion: "1"}}, "1"},
}
for key, testCase := range setCases {
if err := versioning.SetResourceVersion(testCase.Object, "5"); err != nil {
t.Errorf("%s: unexpected error %#v", key, err)
}
actual, err := versioning.ResourceVersion(testCase.Object)
if err != nil {
t.Errorf("%s: unexpected error %#v", key, err)
}
if actual != "5" {
t.Errorf("%s: expected %v, got %v", key, "5", actual)
}
}
}
func TestTypeMetaSelfLinker(t *testing.T) {
table := map[string]struct {
obj runtime.Object
expect string
try string
succeed bool
}{
"normal": {
obj: &MyAPIObject{TypeMeta: InternalTypeMeta{SelfLink: "foobar"}},
expect: "foobar",
try: "newbar",
succeed: true,
},
"fail": {
obj: &MyIncorrectlyMarkedAsAPIObject{},
succeed: false,
},
}
linker := runtime.SelfLinker(meta.NewAccessor())
for name, item := range table {
got, err := linker.SelfLink(item.obj)
if e, a := item.succeed, err == nil; e != a {
t.Errorf("%v: expected %v, got %v", name, e, a)
}
if e, a := item.expect, got; item.succeed && e != a {
t.Errorf("%v: expected %v, got %v", name, e, a)
}
err = linker.SetSelfLink(item.obj, item.try)
if e, a := item.succeed, err == nil; e != a {
t.Errorf("%v: expected %v, got %v", name, e, a)
}
if item.succeed {
got, err := linker.SelfLink(item.obj)
if err != nil {
t.Errorf("%v: expected no err, got %v", name, err)
}
if e, a := item.try, got; e != a {
t.Errorf("%v: expected %v, got %v", name, e, a)
}
}
}
}
type MyAPIObject2 struct {
metav1.TypeMeta
v1.ObjectMeta
}
func getObjectMetaAndOwnerRefereneces() (myAPIObject2 MyAPIObject2, metaOwnerReferences []metatypes.OwnerReference) {
fuzz.New().NilChance(.5).NumElements(1, 5).Fuzz(&myAPIObject2)
references := myAPIObject2.ObjectMeta.OwnerReferences
// This is necessary for the test to pass because the getter will return a
// non-nil slice.
metaOwnerReferences = make([]metatypes.OwnerReference, 0)
for i := 0; i < len(references); i++ {
metaOwnerReferences = append(metaOwnerReferences, metatypes.OwnerReference{
Kind: references[i].Kind,
Name: references[i].Name,
UID: references[i].UID,
APIVersion: references[i].APIVersion,
Controller: references[i].Controller,
})
}
if len(references) == 0 {
// This is necessary for the test to pass because the setter will make a
// non-nil slice.
myAPIObject2.ObjectMeta.OwnerReferences = make([]v1.OwnerReference, 0)
}
return myAPIObject2, metaOwnerReferences
}
func testGetOwnerReferences(t *testing.T) {
obj, expected := getObjectMetaAndOwnerRefereneces()
accessor, err := meta.Accessor(&obj)
if err != nil {
t.Error(err)
}
references := accessor.GetOwnerReferences()
if !reflect.DeepEqual(references, expected) {
t.Errorf("expect %#v\n got %#v", expected, references)
}
}
func testSetOwnerReferences(t *testing.T) {
expected, references := getObjectMetaAndOwnerRefereneces()
obj := MyAPIObject2{}
accessor, err := meta.Accessor(&obj)
if err != nil {
t.Error(err)
}
accessor.SetOwnerReferences(references)
if e, a := expected.ObjectMeta.OwnerReferences, obj.ObjectMeta.OwnerReferences; !reflect.DeepEqual(e, a) {
t.Errorf("expect %#v\n got %#v", e, a)
}
}
func TestAccessOwnerReferences(t *testing.T) {
fuzzIter := 5
for i := 0; i < fuzzIter; i++ {
testGetOwnerReferences(t)
testSetOwnerReferences(t)
}
}
// BenchmarkAccessorSetFastPath shows the interface fast path
func BenchmarkAccessorSetFastPath(b *testing.B) {
obj := &api.Pod{
TypeMeta: metav1.TypeMeta{APIVersion: "/a", Kind: "b"},
ObjectMeta: api.ObjectMeta{
Namespace: "bar",
Name: "foo",
GenerateName: "prefix",
UID: "uid",
ResourceVersion: "1",
SelfLink: "some/place/only/we/know",
Labels: map[string]string{"foo": "bar"},
Annotations: map[string]string{"x": "y"},
},
}
b.ResetTimer()
for i := 0; i < b.N; i++ {
acc, err := meta.Accessor(obj)
if err != nil {
b.Fatal(err)
}
acc.SetNamespace("something")
}
b.StopTimer()
}

18
vendor/k8s.io/kubernetes/pkg/api/meta/metatypes/BUILD generated vendored Normal file
View file

@ -0,0 +1,18 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
"go_test",
"cgo_library",
)
go_library(
name = "go_default_library",
srcs = ["types.go"],
tags = ["automanaged"],
deps = ["//pkg/types:go_default_library"],
)

View file

@ -0,0 +1,30 @@
/*
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.
*/
// The types defined in this package are used by the meta package to represent
// the in-memory version of objects. We cannot reuse the __internal version of
// API objects because it causes import cycle.
package metatypes
import "k8s.io/kubernetes/pkg/types"
type OwnerReference struct {
APIVersion string
Kind string
UID types.UID
Name string
Controller *bool
}

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/kubernetes/pkg/runtime/schema"
utilerrors "k8s.io/kubernetes/pkg/util/errors"
"k8s.io/kubernetes/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) ([]*RESTMapping, error) {
var allMappings []*RESTMapping
var errors []error
for _, t := range m {
currMappings, err := t.RESTMappings(gk)
// 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/kubernetes/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) (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/kubernetes/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/kubernetes/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, err := schema.ParseGroupVersion(version)
if err != nil {
return nil, err
}
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) ([]*RESTMapping, error) {
return m.Delegate.RESTMappings(gk)
}
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)
}

309
vendor/k8s.io/kubernetes/pkg/api/meta/priority_test.go generated vendored Normal file
View file

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

601
vendor/k8s.io/kubernetes/pkg/api/meta/restmapper.go generated vendored Normal file

File diff suppressed because it is too large Load diff

File diff suppressed because it is too large Load diff

121
vendor/k8s.io/kubernetes/pkg/api/meta/scheme_test.go generated vendored Normal file
View file

@ -0,0 +1,121 @@
/*
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_test
import (
"fmt"
"reflect"
"strings"
"testing"
"k8s.io/kubernetes/pkg/api/testapi"
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/util/sets"
)
// These types do not follow the list convention as documented in
// docs/devel/api-convention.md
var listTypeExceptions = sets.NewString("APIGroupList", "APIResourceList")
func validateListType(target reflect.Type) error {
// exceptions
if listTypeExceptions.Has(target.Name()) {
return nil
}
hasListSuffix := strings.HasSuffix(target.Name(), "List")
hasMetadata := false
hasItems := false
for i := 0; i < target.NumField(); i++ {
field := target.Field(i)
tag := field.Tag.Get("json")
switch {
case strings.HasPrefix(tag, "metadata"):
hasMetadata = true
case tag == "items":
hasItems = true
if field.Type.Kind() != reflect.Slice {
return fmt.Errorf("Expected items to be slice, got %s", field.Type.Kind())
}
}
}
if hasListSuffix && !hasMetadata {
return fmt.Errorf("Expected type %s to contain \"metadata\"", target.Name())
}
if hasListSuffix && !hasItems {
return fmt.Errorf("Expected type %s to contain \"items\"", target.Name())
}
// if a type contains field Items with JSON tag "items", its name should end with List.
if !hasListSuffix && hasItems {
return fmt.Errorf("Type %s has Items, its name is expected to end with \"List\"", target.Name())
}
return nil
}
// TestListTypes verifies that no external type violates the api convention of
// list types.
func TestListTypes(t *testing.T) {
for groupKey, group := range testapi.Groups {
for kind, target := range group.ExternalTypes() {
t.Logf("working on %v in %v", kind, groupKey)
err := validateListType(target)
if err != nil {
t.Error(err)
}
}
}
}
type WithoutMetaDataList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta
Items []interface{} `json:"items"`
}
type WithoutItemsList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
}
type WrongItemsJSONTagList struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []interface{} `json:"items,omitempty"`
}
// If a type has Items, its name should end with "List"
type ListWithWrongName struct {
metav1.TypeMeta `json:",inline"`
metav1.ListMeta `json:"metadata,omitempty"`
Items []interface{} `json:"items"`
}
// TestValidateListType verifies the validateListType function reports error on
// types that violate the api convention.
func TestValidateListType(t *testing.T) {
var testTypes = []interface{}{
WithoutMetaDataList{},
WithoutItemsList{},
WrongItemsJSONTagList{},
ListWithWrongName{},
}
for _, testType := range testTypes {
err := validateListType(reflect.TypeOf(testType))
if err == nil {
t.Errorf("Expected error")
}
}
}

31
vendor/k8s.io/kubernetes/pkg/api/meta/unstructured.go generated vendored Normal file
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/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/runtime/schema"
)
// InterfacesForUnstructured returns VersionInterfaces suitable for
// dealing with runtime.Unstructured objects.
func InterfacesForUnstructured(schema.GroupVersion) (*VersionInterfaces, error) {
return &VersionInterfaces{
ObjectConvertor: &runtime.UnstructuredObjectConverter{},
MetadataAccessor: NewAccessor(),
}, nil
}

185
vendor/k8s.io/kubernetes/pkg/api/meta_test.go generated vendored Normal file
View file

@ -0,0 +1,185 @@
/*
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 api_test
import (
"reflect"
"testing"
"github.com/google/gofuzz"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/meta/metatypes"
"k8s.io/kubernetes/pkg/apimachinery/registered"
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/util/uuid"
)
var _ meta.Object = &api.ObjectMeta{}
// TestFillObjectMetaSystemFields validates that system populated fields are set on an object
func TestFillObjectMetaSystemFields(t *testing.T) {
ctx := api.NewDefaultContext()
resource := api.ObjectMeta{}
api.FillObjectMetaSystemFields(ctx, &resource)
if resource.CreationTimestamp.Time.IsZero() {
t.Errorf("resource.CreationTimestamp is zero")
} else if len(resource.UID) == 0 {
t.Errorf("resource.UID missing")
}
// verify we can inject a UID
uid := uuid.NewUUID()
ctx = api.WithUID(ctx, uid)
resource = api.ObjectMeta{}
api.FillObjectMetaSystemFields(ctx, &resource)
if resource.UID != uid {
t.Errorf("resource.UID expected: %v, actual: %v", uid, resource.UID)
}
}
// TestHasObjectMetaSystemFieldValues validates that true is returned if and only if all fields are populated
func TestHasObjectMetaSystemFieldValues(t *testing.T) {
ctx := api.NewDefaultContext()
resource := api.ObjectMeta{}
if api.HasObjectMetaSystemFieldValues(&resource) {
t.Errorf("the resource does not have all fields yet populated, but incorrectly reports it does")
}
api.FillObjectMetaSystemFields(ctx, &resource)
if !api.HasObjectMetaSystemFieldValues(&resource) {
t.Errorf("the resource does have all fields populated, but incorrectly reports it does not")
}
}
func getObjectMetaAndOwnerReferences() (objectMeta api.ObjectMeta, metaOwnerReferences []metatypes.OwnerReference) {
fuzz.New().NilChance(.5).NumElements(1, 5).Fuzz(&objectMeta)
references := objectMeta.OwnerReferences
metaOwnerReferences = make([]metatypes.OwnerReference, 0)
for i := 0; i < len(references); i++ {
metaOwnerReferences = append(metaOwnerReferences, metatypes.OwnerReference{
Kind: references[i].Kind,
Name: references[i].Name,
UID: references[i].UID,
APIVersion: references[i].APIVersion,
Controller: references[i].Controller,
})
}
if len(references) == 0 {
objectMeta.OwnerReferences = make([]api.OwnerReference, 0)
}
return objectMeta, metaOwnerReferences
}
func testGetOwnerReferences(t *testing.T) {
meta, expected := getObjectMetaAndOwnerReferences()
refs := meta.GetOwnerReferences()
if !reflect.DeepEqual(refs, expected) {
t.Errorf("expect %v\n got %v", expected, refs)
}
}
func testSetOwnerReferences(t *testing.T) {
expected, newRefs := getObjectMetaAndOwnerReferences()
objectMeta := &api.ObjectMeta{}
objectMeta.SetOwnerReferences(newRefs)
if !reflect.DeepEqual(expected.OwnerReferences, objectMeta.OwnerReferences) {
t.Errorf("expect: %#v\n got: %#v", expected.OwnerReferences, objectMeta.OwnerReferences)
}
}
func TestAccessOwnerReferences(t *testing.T) {
fuzzIter := 5
for i := 0; i < fuzzIter; i++ {
testGetOwnerReferences(t)
testSetOwnerReferences(t)
}
}
func TestAccessorImplementations(t *testing.T) {
for _, gv := range registered.EnabledVersions() {
internalGV := schema.GroupVersion{Group: gv.Group, Version: runtime.APIVersionInternal}
for _, gv := range []schema.GroupVersion{gv, internalGV} {
for kind, knownType := range api.Scheme.KnownTypes(gv) {
value := reflect.New(knownType)
obj := value.Interface()
if _, ok := obj.(runtime.Object); !ok {
t.Errorf("%v (%v) does not implement runtime.Object", gv.WithKind(kind), knownType)
}
lm, isLM := obj.(meta.ListMetaAccessor)
om, isOM := obj.(meta.ObjectMetaAccessor)
switch {
case isLM && isOM:
t.Errorf("%v (%v) implements ListMetaAccessor and ObjectMetaAccessor", gv.WithKind(kind), knownType)
continue
case isLM:
m := lm.GetListMeta()
if m == nil {
t.Errorf("%v (%v) returns nil ListMeta", gv.WithKind(kind), knownType)
continue
}
m.SetResourceVersion("102030")
if m.GetResourceVersion() != "102030" {
t.Errorf("%v (%v) did not preserve resource version", gv.WithKind(kind), knownType)
continue
}
m.SetSelfLink("102030")
if m.GetSelfLink() != "102030" {
t.Errorf("%v (%v) did not preserve self link", gv.WithKind(kind), knownType)
continue
}
case isOM:
m := om.GetObjectMeta()
if m == nil {
t.Errorf("%v (%v) returns nil ObjectMeta", gv.WithKind(kind), knownType)
continue
}
m.SetResourceVersion("102030")
if m.GetResourceVersion() != "102030" {
t.Errorf("%v (%v) did not preserve resource version", gv.WithKind(kind), knownType)
continue
}
m.SetSelfLink("102030")
if m.GetSelfLink() != "102030" {
t.Errorf("%v (%v) did not preserve self link", gv.WithKind(kind), knownType)
continue
}
labels := map[string]string{"a": "b"}
m.SetLabels(labels)
if !reflect.DeepEqual(m.GetLabels(), labels) {
t.Errorf("%v (%v) did not preserve labels", gv.WithKind(kind), knownType)
continue
}
default:
if _, ok := obj.(metav1.ListMetaAccessor); ok {
continue
}
if _, ok := value.Elem().Type().FieldByName("ObjectMeta"); ok {
t.Errorf("%v (%v) has ObjectMeta but does not implement ObjectMetaAccessor", gv.WithKind(kind), knownType)
continue
}
if _, ok := value.Elem().Type().FieldByName("ListMeta"); ok {
t.Errorf("%v (%v) has ListMeta but does not implement ListMetaAccessor", gv.WithKind(kind), knownType)
continue
}
t.Logf("%v (%v) does not implement ListMetaAccessor or ObjectMetaAccessor", gv.WithKind(kind), knownType)
}
}
}
}
}

49
vendor/k8s.io/kubernetes/pkg/api/node_example.json generated vendored Normal file
View file

@ -0,0 +1,49 @@
{
"kind": "Node",
"apiVersion": "v1",
"metadata": {
"name": "e2e-test-wojtekt-node-etd6",
"selfLink": "/api/v1/nodes/e2e-test-wojtekt-node-etd6",
"uid": "a7e89222-e8e5-11e4-8fde-42010af09327",
"resourceVersion": "379",
"creationTimestamp": "2015-04-22T11:49:39Z"
},
"spec": {
"externalID": "15488322946290398375"
},
"status": {
"capacity": {
"cpu": "1",
"memory": "1745152Ki"
},
"conditions": [
{
"type": "Ready",
"status": "True",
"lastHeartbeatTime": "2015-04-22T11:58:17Z",
"lastTransitionTime": "2015-04-22T11:49:52Z",
"reason": "kubelet is posting ready status"
}
],
"addresses": [
{
"type": "ExternalIP",
"address": "104.197.49.213"
},
{
"type": "LegacyHostIP",
"address": "104.197.20.11"
}
],
"nodeInfo": {
"machineID": "",
"systemUUID": "D59FA3FA-7B5B-7287-5E1A-1D79F13CB577",
"bootID": "44a832f3-8cfb-4de5-b7d2-d66030b6cd95",
"kernelVersion": "3.16.0-0.bpo.4-amd64",
"osImage": "Debian GNU/Linux 7 (wheezy)",
"containerRuntimeVersion": "docker://1.5.0",
"kubeletVersion": "v0.15.0-484-g0c8ee980d705a3-dirty",
"kubeProxyVersion": "v0.15.0-484-g0c8ee980d705a3-dirty"
}
}
}

17
vendor/k8s.io/kubernetes/pkg/api/pod/BUILD generated vendored Normal file
View file

@ -0,0 +1,17 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
"go_test",
"cgo_library",
)
go_library(
name = "go_default_library",
srcs = ["util.go"],
tags = ["automanaged"],
)

4
vendor/k8s.io/kubernetes/pkg/api/pod/OWNERS generated vendored Executable file
View file

@ -0,0 +1,4 @@
reviewers:
- smarterclayton
- kargakis
- david-mcmahon

31
vendor/k8s.io/kubernetes/pkg/api/pod/util.go generated vendored Normal file
View file

@ -0,0 +1,31 @@
/*
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 pod
const (
// TODO: to be de!eted after v1.3 is released. PodSpec has a dedicated Hostname field.
// The annotation value is a string specifying the hostname to be used for the pod e.g 'my-webserver-1'
PodHostnameAnnotation = "pod.beta.kubernetes.io/hostname"
// TODO: to be de!eted after v1.3 is released. PodSpec has a dedicated Subdomain field.
// The annotation value is a string specifying the subdomain e.g. "my-web-service"
// If specified, on the pod itself, "<hostname>.my-web-service.<namespace>.svc.<cluster domain>" would resolve to
// the pod's IP.
// If there is a headless service named "my-web-service" in the same namespace as the pod, then,
// <hostname>.my-web-service.<namespace>.svc.<cluster domain>" would be resolved by the cluster DNS Server.
PodSubdomainAnnotation = "pod.beta.kubernetes.io/subdomain"
)

132
vendor/k8s.io/kubernetes/pkg/api/ref.go generated vendored Normal file
View file

@ -0,0 +1,132 @@
/*
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 api
import (
"errors"
"fmt"
"net/url"
"strings"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/runtime/schema"
)
var (
// Errors that could be returned by GetReference.
ErrNilObject = errors.New("can't reference a nil object")
ErrNoSelfLink = errors.New("selfLink was empty, can't make reference")
)
// GetReference returns an ObjectReference which refers to the given
// object, or an error if the object doesn't follow the conventions
// that would allow this.
// TODO: should take a meta.Interface see http://issue.k8s.io/7127
func GetReference(obj runtime.Object) (*ObjectReference, error) {
if obj == nil {
return nil, ErrNilObject
}
if ref, ok := obj.(*ObjectReference); ok {
// Don't make a reference to a reference.
return ref, nil
}
gvk := obj.GetObjectKind().GroupVersionKind()
// if the object referenced is actually persisted, we can just get kind from meta
// if we are building an object reference to something not yet persisted, we should fallback to scheme
kind := gvk.Kind
if len(kind) == 0 {
// TODO: this is wrong
gvks, _, err := Scheme.ObjectKinds(obj)
if err != nil {
return nil, err
}
kind = gvks[0].Kind
}
// An object that implements only List has enough metadata to build a reference
var listMeta meta.List
objectMeta, err := meta.Accessor(obj)
if err != nil {
listMeta, err = meta.ListAccessor(obj)
if err != nil {
return nil, err
}
} else {
listMeta = objectMeta
}
// if the object referenced is actually persisted, we can also get version from meta
version := gvk.GroupVersion().String()
if len(version) == 0 {
selfLink := listMeta.GetSelfLink()
if len(selfLink) == 0 {
return nil, ErrNoSelfLink
}
selfLinkUrl, err := url.Parse(selfLink)
if err != nil {
return nil, err
}
// example paths: /<prefix>/<version>/*
parts := strings.Split(selfLinkUrl.Path, "/")
if len(parts) < 3 {
return nil, fmt.Errorf("unexpected self link format: '%v'; got version '%v'", selfLink, version)
}
version = parts[2]
}
// only has list metadata
if objectMeta == nil {
return &ObjectReference{
Kind: kind,
APIVersion: version,
ResourceVersion: listMeta.GetResourceVersion(),
}, nil
}
return &ObjectReference{
Kind: kind,
APIVersion: version,
Name: objectMeta.GetName(),
Namespace: objectMeta.GetNamespace(),
UID: objectMeta.GetUID(),
ResourceVersion: objectMeta.GetResourceVersion(),
}, nil
}
// GetPartialReference is exactly like GetReference, but allows you to set the FieldPath.
func GetPartialReference(obj runtime.Object, fieldPath string) (*ObjectReference, error) {
ref, err := GetReference(obj)
if err != nil {
return nil, err
}
ref.FieldPath = fieldPath
return ref, nil
}
// IsAnAPIObject allows clients to preemptively get a reference to an API object and pass it to places that
// intend only to get a reference to that object. This simplifies the event recording interface.
func (obj *ObjectReference) SetGroupVersionKind(gvk schema.GroupVersionKind) {
obj.APIVersion, obj.Kind = gvk.ToAPIVersionAndKind()
}
func (obj *ObjectReference) GroupVersionKind() schema.GroupVersionKind {
return schema.FromAPIVersionAndKind(obj.APIVersion, obj.Kind)
}
func (obj *ObjectReference) GetObjectKind() schema.ObjectKind { return obj }

137
vendor/k8s.io/kubernetes/pkg/api/ref_test.go generated vendored Normal file
View file

@ -0,0 +1,137 @@
/*
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 api
import (
"reflect"
"testing"
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/runtime/schema"
)
type FakeAPIObject struct{}
func (obj *FakeAPIObject) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind }
type ExtensionAPIObject struct {
metav1.TypeMeta
ObjectMeta
}
func (obj *ExtensionAPIObject) GetObjectKind() schema.ObjectKind { return &obj.TypeMeta }
func TestGetReference(t *testing.T) {
// when vendoring kube, if you don't force the set of registered versions (like make test does)
// then you run into trouble because the types aren't registered in the scheme by anything. This does the
// register manually to allow unit test execution
if _, _, err := Scheme.ObjectKinds(&Pod{}); err != nil {
AddToScheme(Scheme)
}
table := map[string]struct {
obj runtime.Object
ref *ObjectReference
fieldPath string
shouldErr bool
}{
"pod": {
obj: &Pod{
ObjectMeta: ObjectMeta{
Name: "foo",
UID: "bar",
ResourceVersion: "42",
SelfLink: "/api/version1/pods/foo",
},
},
fieldPath: ".desiredState.containers[0]",
ref: &ObjectReference{
Kind: "Pod",
APIVersion: "version1",
Name: "foo",
UID: "bar",
ResourceVersion: "42",
FieldPath: ".desiredState.containers[0]",
},
},
"serviceList": {
obj: &ServiceList{
ListMeta: metav1.ListMeta{
ResourceVersion: "42",
SelfLink: "/api/version2/services",
},
},
ref: &ObjectReference{
Kind: "ServiceList",
APIVersion: "version2",
ResourceVersion: "42",
},
},
"extensionAPIObject": {
obj: &ExtensionAPIObject{
TypeMeta: metav1.TypeMeta{
Kind: "ExtensionAPIObject",
},
ObjectMeta: ObjectMeta{
Name: "foo",
UID: "bar",
ResourceVersion: "42",
SelfLink: "/custom_prefix/version1/extensions/foo",
},
},
ref: &ObjectReference{
Kind: "ExtensionAPIObject",
APIVersion: "version1",
Name: "foo",
UID: "bar",
ResourceVersion: "42",
},
},
"badSelfLink": {
obj: &ServiceList{
ListMeta: metav1.ListMeta{
ResourceVersion: "42",
SelfLink: "version2/services",
},
},
shouldErr: true,
},
"error": {
obj: &FakeAPIObject{},
ref: nil,
shouldErr: true,
},
"errorNil": {
obj: nil,
ref: nil,
shouldErr: true,
},
}
for name, item := range table {
ref, err := GetPartialReference(item.obj, item.fieldPath)
if e, a := item.shouldErr, (err != nil); e != a {
t.Errorf("%v: expected %v, got %v, err %v", name, e, a, err)
continue
}
if e, a := item.ref, ref; !reflect.DeepEqual(e, a) {
t.Errorf("%v: expected %#v, got %#v", name, e, a)
}
}
}

139
vendor/k8s.io/kubernetes/pkg/api/register.go generated vendored Normal file
View file

@ -0,0 +1,139 @@
/*
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 api
import (
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/runtime/serializer"
)
// Scheme is the default instance of runtime.Scheme to which types in the Kubernetes API are already registered.
// NOTE: If you are copying this file to start a new api group, STOP! Copy the
// extensions group instead. This Scheme is special and should appear ONLY in
// the api group, unless you really know what you're doing.
// TODO(lavalamp): make the above error impossible.
var Scheme = runtime.NewScheme()
// Codecs provides access to encoding and decoding for the scheme
var Codecs = serializer.NewCodecFactory(Scheme)
// GroupName is the group name use in this package
const GroupName = ""
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal}
// Unversioned is group version for unversioned API objects
// TODO: this should be v1 probably
var Unversioned = schema.GroupVersion{Group: "", Version: "v1"}
// 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()
}
// Resource takes an unqualified resource and returns a Group qualified GroupResource
func Resource(resource string) schema.GroupResource {
return SchemeGroupVersion.WithResource(resource).GroupResource()
}
var (
SchemeBuilder = runtime.NewSchemeBuilder(addKnownTypes, addDefaultingFuncs)
AddToScheme = SchemeBuilder.AddToScheme
)
func init() {
// TODO(lavalamp): move this call to scheme builder above. Can't
// remove it from here because lots of people inappropriately rely on it
// (specifically the unversioned time conversion). Can't have it in
// both places because then it gets double registered. Consequence of
// current state is that it only ever gets registered in the main
// api.Scheme, even though everyone that uses anything from unversioned
// needs these.
if err := addConversionFuncs(Scheme); err != nil {
// Programmer error.
panic(err)
}
}
func addKnownTypes(scheme *runtime.Scheme) error {
if err := scheme.AddIgnoredConversionType(&metav1.TypeMeta{}, &metav1.TypeMeta{}); err != nil {
return err
}
scheme.AddKnownTypes(SchemeGroupVersion,
&Pod{},
&PodList{},
&PodStatusResult{},
&PodTemplate{},
&PodTemplateList{},
&ReplicationControllerList{},
&ReplicationController{},
&ServiceList{},
&Service{},
&ServiceProxyOptions{},
&NodeList{},
&Node{},
&NodeProxyOptions{},
&Endpoints{},
&EndpointsList{},
&Binding{},
&Event{},
&EventList{},
&List{},
&LimitRange{},
&LimitRangeList{},
&ResourceQuota{},
&ResourceQuotaList{},
&Namespace{},
&NamespaceList{},
&ServiceAccount{},
&ServiceAccountList{},
&Secret{},
&SecretList{},
&PersistentVolume{},
&PersistentVolumeList{},
&PersistentVolumeClaim{},
&PersistentVolumeClaimList{},
&DeleteOptions{},
&ListOptions{},
&PodAttachOptions{},
&PodLogOptions{},
&PodExecOptions{},
&PodProxyOptions{},
&ComponentStatus{},
&ComponentStatusList{},
&SerializedReference{},
&RangeAllocation{},
&ConfigMap{},
&ConfigMapList{},
)
// Register Unversioned types under their own special group
scheme.AddUnversionedTypes(Unversioned,
&metav1.Status{},
&metav1.APIVersions{},
&metav1.APIGroupList{},
&metav1.APIGroup{},
&metav1.APIResourceList{},
)
return nil
}

View file

@ -0,0 +1,83 @@
{
"kind": "ReplicationController",
"apiVersion": "v1",
"metadata": {
"name": "elasticsearch-logging-controller",
"namespace": "default",
"selfLink": "/api/v1/namespaces/default/replicationcontrollers/elasticsearch-logging-controller",
"uid": "aa76f162-e8e5-11e4-8fde-42010af09327",
"resourceVersion": "98",
"creationTimestamp": "2015-04-22T11:49:43Z",
"labels": {
"kubernetes.io/cluster-service": "true",
"name": "elasticsearch-logging"
}
},
"spec": {
"replicas": 1,
"selector": {
"name": "elasticsearch-logging"
},
"template": {
"metadata": {
"creationTimestamp": null,
"labels": {
"kubernetes.io/cluster-service": "true",
"name": "elasticsearch-logging"
}
},
"spec": {
"volumes": [
{
"name": "es-persistent-storage",
"hostPath": null,
"emptyDir": {
"medium": ""
},
"gcePersistentDisk": null,
"awsElasticBlockStore": null,
"gitRepo": null,
"secret": null,
"nfs": null,
"iscsi": null,
"glusterfs": null,
"quobyte": null
}
],
"containers": [
{
"name": "elasticsearch-logging",
"image": "gcr.io/google_containers/elasticsearch:1.0",
"ports": [
{
"name": "db",
"containerPort": 9200,
"protocol": "TCP"
},
{
"name": "transport",
"containerPort": 9300,
"protocol": "TCP"
}
],
"resources": {},
"volumeMounts": [
{
"name": "es-persistent-storage",
"mountPath": "/data"
}
],
"terminationMessagePath": "/dev/termination-log",
"imagePullPolicy": "IfNotPresent",
"capabilities": {}
}
],
"restartPolicy": "Always",
"dnsPolicy": "ClusterFirst"
}
}
},
"status": {
"replicas": 1
}
}

117
vendor/k8s.io/kubernetes/pkg/api/requestcontext.go generated vendored Normal file
View file

@ -0,0 +1,117 @@
/*
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 api
import (
"errors"
"net/http"
"sync"
"github.com/golang/glog"
)
// RequestContextMapper keeps track of the context associated with a particular request
type RequestContextMapper interface {
// Get returns the context associated with the given request (if any), and true if the request has an associated context, and false if it does not.
Get(req *http.Request) (Context, bool)
// Update maps the request to the given context. If no context was previously associated with the request, an error is returned.
// Update should only be called with a descendant context of the previously associated context.
// Updating to an unrelated context may return an error in the future.
// The context associated with a request should only be updated by a limited set of callers.
// Valid examples include the authentication layer, or an audit/tracing layer.
Update(req *http.Request, context Context) error
}
type requestContextMap struct {
contexts map[*http.Request]Context
lock sync.Mutex
}
// NewRequestContextMapper returns a new RequestContextMapper.
// The returned mapper must be added as a request filter using NewRequestContextFilter.
func NewRequestContextMapper() RequestContextMapper {
return &requestContextMap{
contexts: make(map[*http.Request]Context),
}
}
// Get returns the context associated with the given request (if any), and true if the request has an associated context, and false if it does not.
// Get will only return a valid context when called from inside the filter chain set up by NewRequestContextFilter()
func (c *requestContextMap) Get(req *http.Request) (Context, bool) {
c.lock.Lock()
defer c.lock.Unlock()
context, ok := c.contexts[req]
return context, ok
}
// Update maps the request to the given context.
// If no context was previously associated with the request, an error is returned and the context is ignored.
func (c *requestContextMap) Update(req *http.Request, context Context) error {
c.lock.Lock()
defer c.lock.Unlock()
if _, ok := c.contexts[req]; !ok {
return errors.New("No context associated")
}
// TODO: ensure the new context is a descendant of the existing one
c.contexts[req] = context
return nil
}
// init maps the request to the given context and returns true if there was no context associated with the request already.
// if a context was already associated with the request, it ignores the given context and returns false.
// init is intentionally unexported to ensure that all init calls are paired with a remove after a request is handled
func (c *requestContextMap) init(req *http.Request, context Context) bool {
c.lock.Lock()
defer c.lock.Unlock()
if _, exists := c.contexts[req]; exists {
return false
}
c.contexts[req] = context
return true
}
// remove is intentionally unexported to ensure that the context is not removed until a request is handled
func (c *requestContextMap) remove(req *http.Request) {
c.lock.Lock()
defer c.lock.Unlock()
delete(c.contexts, req)
}
// WithRequestContext ensures there is a Context object associated with the request before calling the passed handler.
// After the passed handler runs, the context is cleaned up.
func WithRequestContext(handler http.Handler, mapper RequestContextMapper) http.Handler {
rcMap, ok := mapper.(*requestContextMap)
if !ok {
glog.Fatal("Unknown RequestContextMapper implementation.")
}
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
if rcMap.init(req, NewContext()) {
// If we were the ones to successfully initialize, pair with a remove
defer rcMap.remove(req)
}
handler.ServeHTTP(w, req)
})
}
// IsEmpty returns true if there are no contexts registered, or an error if it could not be determined. Intended for use by tests.
func IsEmpty(requestsToContexts RequestContextMapper) (bool, error) {
if requestsToContexts, ok := requestsToContexts.(*requestContextMap); ok {
return len(requestsToContexts.contexts) == 0, nil
}
return true, errors.New("Unknown RequestContextMapper implementation")
}

56
vendor/k8s.io/kubernetes/pkg/api/resource/BUILD generated vendored Normal file
View file

@ -0,0 +1,56 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
"go_test",
"cgo_library",
)
go_library(
name = "go_default_library",
srcs = [
"amount.go",
"generated.pb.go",
"math.go",
"quantity.go",
"quantity_proto.go",
"scale_int.go",
"suffix.go",
],
tags = ["automanaged"],
deps = [
"//pkg/genericapiserver/openapi/common:go_default_library",
"//vendor:github.com/go-openapi/spec",
"//vendor:github.com/gogo/protobuf/proto",
"//vendor:github.com/spf13/pflag",
"//vendor:gopkg.in/inf.v0",
],
)
go_test(
name = "go_default_test",
srcs = [
"amount_test.go",
"math_test.go",
"quantity_test.go",
"scale_int_test.go",
],
library = "go_default_library",
tags = ["automanaged"],
deps = [
"//vendor:github.com/google/gofuzz",
"//vendor:github.com/spf13/pflag",
"//vendor:gopkg.in/inf.v0",
],
)
go_test(
name = "go_default_xtest",
srcs = ["quantity_example_test.go"],
tags = ["automanaged"],
deps = ["//pkg/api/resource:go_default_library"],
)

17
vendor/k8s.io/kubernetes/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/kubernetes/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,70 @@
/*
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.
*/
// Code generated by protoc-gen-gogo.
// source: k8s.io/kubernetes/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/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.kubernetes.pkg.api.resource.Quantity")
}
var fileDescriptorGenerated = []byte{
// 236 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0x6c, 0x8f, 0xa1, 0x4e, 0x03, 0x41,
0x10, 0x86, 0x67, 0x0d, 0x29, 0x27, 0x1b, 0x42, 0x48, 0xc5, 0x5e, 0x53, 0x45, 0x48, 0xd8, 0x09,
0xa8, 0x06, 0xc9, 0x1b, 0x80, 0xc4, 0xdd, 0x95, 0x61, 0x99, 0x1c, 0xec, 0x6e, 0x76, 0x67, 0x05,
0xae, 0x12, 0x59, 0x89, 0xec, 0xbd, 0x4d, 0x65, 0x25, 0x02, 0xc1, 0x1d, 0x2f, 0x42, 0x72, 0xa5,
0x21, 0x21, 0xb8, 0xf9, 0xc4, 0x37, 0xf9, 0xfe, 0xe2, 0xb2, 0x99, 0x27, 0xc3, 0x1e, 0x9b, 0x5c,
0x53, 0x74, 0x24, 0x94, 0x30, 0x34, 0x16, 0xab, 0xc0, 0x18, 0x29, 0xf9, 0x1c, 0x17, 0x84, 0x96,
0x1c, 0xc5, 0x4a, 0xe8, 0xde, 0x84, 0xe8, 0xc5, 0x8f, 0x67, 0x3b, 0xc7, 0xfc, 0x3a, 0x26, 0x34,
0xd6, 0x54, 0x81, 0xcd, 0xde, 0x99, 0x9c, 0x5b, 0x96, 0xc7, 0x5c, 0x9b, 0x85, 0x7f, 0x46, 0xeb,
0xad, 0xc7, 0x41, 0xad, 0xf3, 0xc3, 0x40, 0x03, 0x0c, 0xd7, 0xee, 0xe5, 0xe4, 0xe2, 0xff, 0x8c,
0x2c, 0xfc, 0x84, 0xec, 0x24, 0x49, 0xfc, 0x5b, 0x31, 0x9b, 0x17, 0xa3, 0x9b, 0x5c, 0x39, 0x61,
0x79, 0x19, 0x1f, 0x17, 0x07, 0x49, 0x22, 0x3b, 0x7b, 0xa2, 0xa6, 0xea, 0xf4, 0xf0, 0xf6, 0x87,
0xae, 0x8e, 0xde, 0xd6, 0x25, 0xbc, 0xb6, 0x25, 0xac, 0xda, 0x12, 0xd6, 0x6d, 0x09, 0xcb, 0x8f,
0x29, 0x5c, 0x9f, 0x6d, 0x3a, 0x0d, 0xdb, 0x4e, 0xc3, 0x7b, 0xa7, 0x61, 0xd9, 0x6b, 0xb5, 0xe9,
0xb5, 0xda, 0xf6, 0x5a, 0x7d, 0xf6, 0x5a, 0xad, 0xbe, 0x34, 0xdc, 0x8d, 0xf6, 0x3b, 0xbe, 0x03,
0x00, 0x00, 0xff, 0xff, 0x90, 0x1c, 0x7f, 0xff, 0x20, 0x01, 0x00, 0x00,
}

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.
*/
// This file was autogenerated by go-to-protobuf. Do not edit it manually!
syntax = 'proto2';
package k8s.io.kubernetes.pkg.api.resource;
import "k8s.io/kubernetes/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/kubernetes/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
}

211
vendor/k8s.io/kubernetes/pkg/api/resource/math_test.go generated vendored Normal file
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/kubernetes/pkg/api/resource/quantity.go generated vendored Normal file

File diff suppressed because it is too large Load diff

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/kubernetes/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

95
vendor/k8s.io/kubernetes/pkg/api/resource/scale_int.go generated vendored Normal file
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/kubernetes/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
}

229
vendor/k8s.io/kubernetes/pkg/api/resource_helpers.go generated vendored Normal file
View file

@ -0,0 +1,229 @@
/*
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 api
import (
"time"
"k8s.io/kubernetes/pkg/api/resource"
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
)
// Returns string version of ResourceName.
func (self ResourceName) String() string {
return string(self)
}
// Returns the CPU limit if specified.
func (self *ResourceList) Cpu() *resource.Quantity {
if val, ok := (*self)[ResourceCPU]; ok {
return &val
}
return &resource.Quantity{Format: resource.DecimalSI}
}
// Returns the Memory limit if specified.
func (self *ResourceList) Memory() *resource.Quantity {
if val, ok := (*self)[ResourceMemory]; ok {
return &val
}
return &resource.Quantity{Format: resource.BinarySI}
}
func (self *ResourceList) Pods() *resource.Quantity {
if val, ok := (*self)[ResourcePods]; ok {
return &val
}
return &resource.Quantity{}
}
func (self *ResourceList) NvidiaGPU() *resource.Quantity {
if val, ok := (*self)[ResourceNvidiaGPU]; ok {
return &val
}
return &resource.Quantity{}
}
func GetContainerStatus(statuses []ContainerStatus, name string) (ContainerStatus, bool) {
for i := range statuses {
if statuses[i].Name == name {
return statuses[i], true
}
}
return ContainerStatus{}, false
}
func GetExistingContainerStatus(statuses []ContainerStatus, name string) ContainerStatus {
for i := range statuses {
if statuses[i].Name == name {
return statuses[i]
}
}
return ContainerStatus{}
}
// IsPodAvailable returns true if a pod is available; false otherwise.
// Precondition for an available pod is that it must be ready. On top
// of that, there are two cases when a pod can be considered available:
// 1. minReadySeconds == 0, or
// 2. LastTransitionTime (is set) + minReadySeconds < current time
func IsPodAvailable(pod *Pod, minReadySeconds int32, now metav1.Time) bool {
if !IsPodReady(pod) {
return false
}
c := GetPodReadyCondition(pod.Status)
minReadySecondsDuration := time.Duration(minReadySeconds) * time.Second
if minReadySeconds == 0 || !c.LastTransitionTime.IsZero() && c.LastTransitionTime.Add(minReadySecondsDuration).Before(now.Time) {
return true
}
return false
}
// IsPodReady returns true if a pod is ready; false otherwise.
func IsPodReady(pod *Pod) bool {
return IsPodReadyConditionTrue(pod.Status)
}
// IsPodReady retruns true if a pod is ready; false otherwise.
func IsPodReadyConditionTrue(status PodStatus) bool {
condition := GetPodReadyCondition(status)
return condition != nil && condition.Status == ConditionTrue
}
// Extracts the pod ready condition from the given status and returns that.
// Returns nil if the condition is not present.
func GetPodReadyCondition(status PodStatus) *PodCondition {
_, condition := GetPodCondition(&status, PodReady)
return condition
}
// GetPodCondition extracts the provided condition from the given status and returns that.
// Returns nil and -1 if the condition is not present, and the index of the located condition.
func GetPodCondition(status *PodStatus, conditionType PodConditionType) (int, *PodCondition) {
if status == nil {
return -1, nil
}
for i := range status.Conditions {
if status.Conditions[i].Type == conditionType {
return i, &status.Conditions[i]
}
}
return -1, nil
}
// GetNodeCondition extracts the provided condition from the given status and returns that.
// Returns nil and -1 if the condition is not present, and the index of the located condition.
func GetNodeCondition(status *NodeStatus, conditionType NodeConditionType) (int, *NodeCondition) {
if status == nil {
return -1, nil
}
for i := range status.Conditions {
if status.Conditions[i].Type == conditionType {
return i, &status.Conditions[i]
}
}
return -1, nil
}
// Updates existing pod condition or creates a new one. Sets LastTransitionTime to now if the
// status has changed.
// Returns true if pod condition has changed or has been added.
func UpdatePodCondition(status *PodStatus, condition *PodCondition) bool {
condition.LastTransitionTime = metav1.Now()
// Try to find this pod condition.
conditionIndex, oldCondition := GetPodCondition(status, condition.Type)
if oldCondition == nil {
// We are adding new pod condition.
status.Conditions = append(status.Conditions, *condition)
return true
} else {
// We are updating an existing condition, so we need to check if it has changed.
if condition.Status == oldCondition.Status {
condition.LastTransitionTime = oldCondition.LastTransitionTime
}
isEqual := condition.Status == oldCondition.Status &&
condition.Reason == oldCondition.Reason &&
condition.Message == oldCondition.Message &&
condition.LastProbeTime.Equal(oldCondition.LastProbeTime) &&
condition.LastTransitionTime.Equal(oldCondition.LastTransitionTime)
status.Conditions[conditionIndex] = *condition
// Return true if one of the fields have changed.
return !isEqual
}
}
// IsNodeReady returns true if a node is ready; false otherwise.
func IsNodeReady(node *Node) bool {
for _, c := range node.Status.Conditions {
if c.Type == NodeReady {
return c.Status == ConditionTrue
}
}
return false
}
// PodRequestsAndLimits returns a dictionary of all defined resources summed up for all
// containers of the pod.
func PodRequestsAndLimits(pod *Pod) (reqs map[ResourceName]resource.Quantity, limits map[ResourceName]resource.Quantity, err error) {
reqs, limits = map[ResourceName]resource.Quantity{}, map[ResourceName]resource.Quantity{}
for _, container := range pod.Spec.Containers {
for name, quantity := range container.Resources.Requests {
if value, ok := reqs[name]; !ok {
reqs[name] = *quantity.Copy()
} else {
value.Add(quantity)
reqs[name] = value
}
}
for name, quantity := range container.Resources.Limits {
if value, ok := limits[name]; !ok {
limits[name] = *quantity.Copy()
} else {
value.Add(quantity)
limits[name] = value
}
}
}
// init containers define the minimum of any resource
for _, container := range pod.Spec.InitContainers {
for name, quantity := range container.Resources.Requests {
value, ok := reqs[name]
if !ok {
reqs[name] = *quantity.Copy()
continue
}
if quantity.Cmp(value) > 0 {
reqs[name] = *quantity.Copy()
}
}
for name, quantity := range container.Resources.Limits {
value, ok := limits[name]
if !ok {
limits[name] = *quantity.Copy()
continue
}
if quantity.Cmp(value) > 0 {
limits[name] = *quantity.Copy()
}
}
}
return
}

View file

@ -0,0 +1,120 @@
/*
Copyright 2015 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package api
import (
"testing"
"time"
"k8s.io/kubernetes/pkg/api/resource"
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
)
func TestResourceHelpers(t *testing.T) {
cpuLimit := resource.MustParse("10")
memoryLimit := resource.MustParse("10G")
resourceSpec := ResourceRequirements{
Limits: ResourceList{
"cpu": cpuLimit,
"memory": memoryLimit,
"kube.io/storage": memoryLimit,
},
}
if res := resourceSpec.Limits.Cpu(); res.Cmp(cpuLimit) != 0 {
t.Errorf("expected cpulimit %v, got %v", cpuLimit, res)
}
if res := resourceSpec.Limits.Memory(); res.Cmp(memoryLimit) != 0 {
t.Errorf("expected memorylimit %v, got %v", memoryLimit, res)
}
resourceSpec = ResourceRequirements{
Limits: ResourceList{
"memory": memoryLimit,
"kube.io/storage": memoryLimit,
},
}
if res := resourceSpec.Limits.Cpu(); res.Value() != 0 {
t.Errorf("expected cpulimit %v, got %v", 0, res)
}
if res := resourceSpec.Limits.Memory(); res.Cmp(memoryLimit) != 0 {
t.Errorf("expected memorylimit %v, got %v", memoryLimit, res)
}
}
func TestDefaultResourceHelpers(t *testing.T) {
resourceList := ResourceList{}
if resourceList.Cpu().Format != resource.DecimalSI {
t.Errorf("expected %v, actual %v", resource.DecimalSI, resourceList.Cpu().Format)
}
if resourceList.Memory().Format != resource.BinarySI {
t.Errorf("expected %v, actual %v", resource.BinarySI, resourceList.Memory().Format)
}
}
func newPod(now metav1.Time, ready bool, beforeSec int) *Pod {
conditionStatus := ConditionFalse
if ready {
conditionStatus = ConditionTrue
}
return &Pod{
Status: PodStatus{
Conditions: []PodCondition{
{
Type: PodReady,
LastTransitionTime: metav1.NewTime(now.Time.Add(-1 * time.Duration(beforeSec) * time.Second)),
Status: conditionStatus,
},
},
},
}
}
func TestIsPodAvailable(t *testing.T) {
now := metav1.Now()
tests := []struct {
pod *Pod
minReadySeconds int32
expected bool
}{
{
pod: newPod(now, false, 0),
minReadySeconds: 0,
expected: false,
},
{
pod: newPod(now, true, 0),
minReadySeconds: 1,
expected: false,
},
{
pod: newPod(now, true, 0),
minReadySeconds: 0,
expected: true,
},
{
pod: newPod(now, true, 51),
minReadySeconds: 50,
expected: true,
},
}
for i, test := range tests {
isAvailable := IsPodAvailable(test.pod, test.minReadySeconds, now)
if isAvailable != test.expected {
t.Errorf("[tc #%d] expected available pod: %t, got: %t", i, test.expected, isAvailable)
}
}
}

37
vendor/k8s.io/kubernetes/pkg/api/rest/BUILD generated vendored Normal file
View file

@ -0,0 +1,37 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
"go_test",
"cgo_library",
)
go_library(
name = "go_default_library",
srcs = [
"create.go",
"delete.go",
"doc.go",
"export.go",
"rest.go",
"types.go",
"update.go",
],
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/api/errors:go_default_library",
"//pkg/api/meta:go_default_library",
"//pkg/api/validation:go_default_library",
"//pkg/api/validation/path:go_default_library",
"//pkg/apis/meta/v1:go_default_library",
"//pkg/runtime:go_default_library",
"//pkg/runtime/schema:go_default_library",
"//pkg/util/validation/field:go_default_library",
"//pkg/watch:go_default_library",
],
)

35
vendor/k8s.io/kubernetes/pkg/api/rest/OWNERS generated vendored Executable file
View file

@ -0,0 +1,35 @@
reviewers:
- thockin
- smarterclayton
- wojtek-t
- deads2k
- brendandburns
- derekwaynecarr
- caesarxuchao
- mikedanese
- liggitt
- nikhiljindal
- bprashanth
- gmarek
- kargakis
- justinsb
- roberthbailey
- ncdc
- eparis
- timothysc
- dims
- hongchaodeng
- krousey
- jszczepkowski
- euank
- markturansky
- fgrzadkowski
- satnam6502
- fabioy
- ingvagabund
- david-mcmahon
- jianhuiz
- nhlfr
- feihujiang
- sdminonne
- goltermann

130
vendor/k8s.io/kubernetes/pkg/api/rest/create.go generated vendored Normal file
View file

@ -0,0 +1,130 @@
/*
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 rest
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/validation"
path "k8s.io/kubernetes/pkg/api/validation/path"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/util/validation/field"
)
// RESTCreateStrategy defines the minimum validation, accepted input, and
// name generation behavior to create an object that follows Kubernetes
// API conventions.
type RESTCreateStrategy interface {
runtime.ObjectTyper
// The name generate is used when the standard GenerateName field is set.
// The NameGenerator will be invoked prior to validation.
api.NameGenerator
// NamespaceScoped returns true if the object must be within a namespace.
NamespaceScoped() bool
// PrepareForCreate is invoked on create before validation to normalize
// the object. For example: remove fields that are not to be persisted,
// sort order-insensitive list fields, etc. This should not remove fields
// whose presence would be considered a validation error.
PrepareForCreate(ctx api.Context, obj runtime.Object)
// Validate is invoked after default fields in the object have been filled in before
// the object is persisted. This method should not mutate the object.
Validate(ctx api.Context, obj runtime.Object) field.ErrorList
// Canonicalize is invoked after validation has succeeded but before the
// object has been persisted. This method may mutate the object.
Canonicalize(obj runtime.Object)
}
// BeforeCreate ensures that common operations for all resources are performed on creation. It only returns
// errors that can be converted to api.Status. It invokes PrepareForCreate, then GenerateName, then Validate.
// It returns nil if the object should be created.
func BeforeCreate(strategy RESTCreateStrategy, ctx api.Context, obj runtime.Object) error {
objectMeta, kind, kerr := objectMetaAndKind(strategy, obj)
if kerr != nil {
return kerr
}
if strategy.NamespaceScoped() {
if !api.ValidNamespace(ctx, objectMeta) {
return errors.NewBadRequest("the namespace of the provided object does not match the namespace sent on the request")
}
} else {
objectMeta.Namespace = api.NamespaceNone
}
objectMeta.DeletionTimestamp = nil
objectMeta.DeletionGracePeriodSeconds = nil
strategy.PrepareForCreate(ctx, obj)
api.FillObjectMetaSystemFields(ctx, objectMeta)
api.GenerateName(strategy, objectMeta)
// ClusterName is ignored and should not be saved
objectMeta.ClusterName = ""
if errs := strategy.Validate(ctx, obj); len(errs) > 0 {
return errors.NewInvalid(kind.GroupKind(), objectMeta.Name, errs)
}
// Custom validation (including name validation) passed
// Now run common validation on object meta
// Do this *after* custom validation so that specific error messages are shown whenever possible
if errs := validation.ValidateObjectMeta(objectMeta, strategy.NamespaceScoped(), path.ValidatePathSegmentName, field.NewPath("metadata")); len(errs) > 0 {
return errors.NewInvalid(kind.GroupKind(), objectMeta.Name, errs)
}
strategy.Canonicalize(obj)
return nil
}
// CheckGeneratedNameError checks whether an error that occurred creating a resource is due
// to generation being unable to pick a valid name.
func CheckGeneratedNameError(strategy RESTCreateStrategy, err error, obj runtime.Object) error {
if !errors.IsAlreadyExists(err) {
return err
}
objectMeta, kind, kerr := objectMetaAndKind(strategy, obj)
if kerr != nil {
return kerr
}
if len(objectMeta.GenerateName) == 0 {
return err
}
return errors.NewServerTimeoutForKind(kind.GroupKind(), "POST", 0)
}
// objectMetaAndKind retrieves kind and ObjectMeta from a runtime object, or returns an error.
func objectMetaAndKind(typer runtime.ObjectTyper, obj runtime.Object) (*api.ObjectMeta, schema.GroupVersionKind, error) {
objectMeta, err := api.ObjectMetaFor(obj)
if err != nil {
return nil, schema.GroupVersionKind{}, errors.NewInternalError(err)
}
kinds, _, err := typer.ObjectKinds(obj)
if err != nil {
return nil, schema.GroupVersionKind{}, errors.NewInternalError(err)
}
return objectMeta, kinds[0], nil
}
// NamespaceScopedStrategy has a method to tell if the object must be in a namespace.
type NamespaceScopedStrategy interface {
// NamespaceScoped returns if the object must be in a namespace.
NamespaceScoped() bool
}

127
vendor/k8s.io/kubernetes/pkg/api/rest/delete.go generated vendored Normal file
View file

@ -0,0 +1,127 @@
/*
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 rest
import (
"fmt"
"time"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/runtime/schema"
)
// RESTDeleteStrategy defines deletion behavior on an object that follows Kubernetes
// API conventions.
type RESTDeleteStrategy interface {
runtime.ObjectTyper
}
type GarbageCollectionPolicy string
const (
DeleteDependents GarbageCollectionPolicy = "DeleteDependents"
OrphanDependents GarbageCollectionPolicy = "OrphanDependents"
)
// GarbageCollectionDeleteStrategy must be implemented by the registry that wants to
// orphan dependents by default.
type GarbageCollectionDeleteStrategy interface {
// DefaultGarbageCollectionPolicy returns the default garbage collection behavior.
DefaultGarbageCollectionPolicy() GarbageCollectionPolicy
}
// RESTGracefulDeleteStrategy must be implemented by the registry that supports
// graceful deletion.
type RESTGracefulDeleteStrategy interface {
// CheckGracefulDelete should return true if the object can be gracefully deleted and set
// any default values on the DeleteOptions.
CheckGracefulDelete(ctx api.Context, obj runtime.Object, options *api.DeleteOptions) bool
}
// BeforeDelete tests whether the object can be gracefully deleted. If graceful is set the object
// should be gracefully deleted, if gracefulPending is set the object has already been gracefully deleted
// (and the provided grace period is longer than the time to deletion), and an error is returned if the
// condition cannot be checked or the gracePeriodSeconds is invalid. The options argument may be updated with
// default values if graceful is true. Second place where we set deletionTimestamp is pkg/registry/generic/registry/store.go
// this function is responsible for setting deletionTimestamp during gracefulDeletion, other one for cascading deletions.
func BeforeDelete(strategy RESTDeleteStrategy, ctx api.Context, obj runtime.Object, options *api.DeleteOptions) (graceful, gracefulPending bool, err error) {
objectMeta, gvk, kerr := objectMetaAndKind(strategy, obj)
if kerr != nil {
return false, false, kerr
}
// Checking the Preconditions here to fail early. They'll be enforced later on when we actually do the deletion, too.
if options.Preconditions != nil && options.Preconditions.UID != nil && *options.Preconditions.UID != objectMeta.UID {
return false, false, errors.NewConflict(schema.GroupResource{Group: gvk.Group, Resource: gvk.Kind}, objectMeta.Name, fmt.Errorf("the UID in the precondition (%s) does not match the UID in record (%s). The object might have been deleted and then recreated", *options.Preconditions.UID, objectMeta.UID))
}
gracefulStrategy, ok := strategy.(RESTGracefulDeleteStrategy)
if !ok {
// If we're not deleting gracefully there's no point in updating Generation, as we won't update
// the obcject before deleting it.
return false, false, nil
}
// if the object is already being deleted, no need to update generation.
if objectMeta.DeletionTimestamp != nil {
// if we are already being deleted, we may only shorten the deletion grace period
// this means the object was gracefully deleted previously but deletionGracePeriodSeconds was not set,
// so we force deletion immediately
// IMPORTANT:
// The deletion operation happens in two phases.
// 1. Update to set DeletionGracePeriodSeconds and DeletionTimestamp
// 2. Delete the object from storage.
// If the update succeeds, but the delete fails (network error, internal storage error, etc.),
// a resource was previously left in a state that was non-recoverable. We
// check if the existing stored resource has a grace period as 0 and if so
// attempt to delete immediately in order to recover from this scenario.
if objectMeta.DeletionGracePeriodSeconds == nil || *objectMeta.DeletionGracePeriodSeconds == 0 {
return false, false, nil
}
// only a shorter grace period may be provided by a user
if options.GracePeriodSeconds != nil {
period := int64(*options.GracePeriodSeconds)
if period >= *objectMeta.DeletionGracePeriodSeconds {
return false, true, nil
}
newDeletionTimestamp := metav1.NewTime(
objectMeta.DeletionTimestamp.Add(-time.Second * time.Duration(*objectMeta.DeletionGracePeriodSeconds)).
Add(time.Second * time.Duration(*options.GracePeriodSeconds)))
objectMeta.DeletionTimestamp = &newDeletionTimestamp
objectMeta.DeletionGracePeriodSeconds = &period
return true, false, nil
}
// graceful deletion is pending, do nothing
options.GracePeriodSeconds = objectMeta.DeletionGracePeriodSeconds
return false, true, nil
}
if !gracefulStrategy.CheckGracefulDelete(ctx, obj, options) {
return false, false, nil
}
now := metav1.NewTime(metav1.Now().Add(time.Second * time.Duration(*options.GracePeriodSeconds)))
objectMeta.DeletionTimestamp = &now
objectMeta.DeletionGracePeriodSeconds = options.GracePeriodSeconds
// If it's the first graceful deletion we are going to set the DeletionTimestamp to non-nil.
// Controllers of the object that's being deleted shouldn't take any nontrivial actions, hence its behavior changes.
// Thus we need to bump object's Generation (if set). This handles generation bump during graceful deletion.
// The bump for objects that don't support graceful deletion is handled in pkg/registry/generic/registry/store.go.
if objectMeta.Generation > 0 {
objectMeta.Generation++
}
return true, false, nil
}

18
vendor/k8s.io/kubernetes/pkg/api/rest/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 rest defines common logic around changes to Kubernetes resources.
package rest // import "k8s.io/kubernetes/pkg/api/rest"

29
vendor/k8s.io/kubernetes/pkg/api/rest/export.go generated vendored Normal file
View file

@ -0,0 +1,29 @@
/*
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 rest
import (
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/runtime"
)
// RESTExportStrategy is the interface that defines how to export a Kubernetes object
type RESTExportStrategy interface {
// Export strips fields that can not be set by the user. If 'exact' is false
// fields specific to the cluster are also stripped
Export(ctx api.Context, obj runtime.Object, exact bool) error
}

307
vendor/k8s.io/kubernetes/pkg/api/rest/rest.go generated vendored Normal file
View file

@ -0,0 +1,307 @@
/*
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 rest
import (
"io"
"net/http"
"net/url"
"k8s.io/kubernetes/pkg/api"
metav1 "k8s.io/kubernetes/pkg/apis/meta/v1"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/runtime/schema"
"k8s.io/kubernetes/pkg/watch"
)
//TODO:
// Storage interfaces need to be separated into two groups; those that operate
// on collections and those that operate on individually named items.
// Collection interfaces:
// (Method: Current -> Proposed)
// GET: Lister -> CollectionGetter
// WATCH: Watcher -> CollectionWatcher
// CREATE: Creater -> CollectionCreater
// DELETE: (n/a) -> CollectionDeleter
// UPDATE: (n/a) -> CollectionUpdater
//
// Single item interfaces:
// (Method: Current -> Proposed)
// GET: Getter -> NamedGetter
// WATCH: (n/a) -> NamedWatcher
// CREATE: (n/a) -> NamedCreater
// DELETE: Deleter -> NamedDeleter
// UPDATE: Update -> NamedUpdater
// Storage is a generic interface for RESTful storage services.
// Resources which are exported to the RESTful API of apiserver need to implement this interface. It is expected
// that objects may implement any of the below interfaces.
type Storage interface {
// New returns an empty object that can be used with Create and Update after request data has been put into it.
// This object must be a pointer type for use with Codec.DecodeInto([]byte, runtime.Object)
New() runtime.Object
}
// KindProvider specifies a different kind for its API than for its internal storage. This is necessary for external
// objects that are not compiled into the api server. For such objects, there is no in-memory representation for
// the object, so they must be represented as generic objects (e.g. runtime.Unknown), but when we present the object as part of
// API discovery we want to present the specific kind, not the generic internal representation.
type KindProvider interface {
Kind() string
}
// Lister is an object that can retrieve resources that match the provided field and label criteria.
type Lister interface {
// NewList returns an empty object that can be used with the List call.
// This object must be a pointer type for use with Codec.DecodeInto([]byte, runtime.Object)
NewList() runtime.Object
// List selects resources in the storage which match to the selector. 'options' can be nil.
List(ctx api.Context, options *api.ListOptions) (runtime.Object, error)
}
// Exporter is an object that knows how to strip a RESTful resource for export
type Exporter interface {
// Export an object. Fields that are not user specified (e.g. Status, ObjectMeta.ResourceVersion) are stripped out
// Returns the stripped object. If 'exact' is true, fields that are specific to the cluster (e.g. namespace) are
// retained, otherwise they are stripped also.
Export(ctx api.Context, name string, opts metav1.ExportOptions) (runtime.Object, error)
}
// Getter is an object that can retrieve a named RESTful resource.
type Getter interface {
// Get finds a resource in the storage by name and returns it.
// Although it can return an arbitrary error value, IsNotFound(err) is true for the
// returned error value err when the specified resource is not found.
Get(ctx api.Context, name string) (runtime.Object, error)
}
// GetterWithOptions is an object that retrieve a named RESTful resource and takes
// additional options on the get request. It allows a caller to also receive the
// subpath of the GET request.
type GetterWithOptions interface {
// Get finds a resource in the storage by name and returns it.
// Although it can return an arbitrary error value, IsNotFound(err) is true for the
// returned error value err when the specified resource is not found.
// The options object passed to it is of the same type returned by the NewGetOptions
// method.
Get(ctx api.Context, name string, options runtime.Object) (runtime.Object, error)
// NewGetOptions returns an empty options object that will be used to pass
// options to the Get method. It may return a bool and a string, if true, the
// value of the request path below the object will be included as the named
// string in the serialization of the runtime object. E.g., returning "path"
// will convert the trailing request scheme value to "path" in the map[string][]string
// passed to the converter.
NewGetOptions() (runtime.Object, bool, string)
}
// Deleter is an object that can delete a named RESTful resource.
type Deleter interface {
// Delete finds a resource in the storage and deletes it.
// Although it can return an arbitrary error value, IsNotFound(err) is true for the
// returned error value err when the specified resource is not found.
// Delete *may* return the object that was deleted, or a status object indicating additional
// information about deletion.
Delete(ctx api.Context, name string) (runtime.Object, error)
}
// GracefulDeleter knows how to pass deletion options to allow delayed deletion of a
// RESTful object.
type GracefulDeleter interface {
// Delete finds a resource in the storage and deletes it.
// If options are provided, the resource will attempt to honor them or return an invalid
// request error.
// Although it can return an arbitrary error value, IsNotFound(err) is true for the
// returned error value err when the specified resource is not found.
// Delete *may* return the object that was deleted, or a status object indicating additional
// information about deletion.
Delete(ctx api.Context, name string, options *api.DeleteOptions) (runtime.Object, error)
}
// GracefulDeleteAdapter adapts the Deleter interface to GracefulDeleter
type GracefulDeleteAdapter struct {
Deleter
}
// Delete implements RESTGracefulDeleter in terms of Deleter
func (w GracefulDeleteAdapter) Delete(ctx api.Context, name string, options *api.DeleteOptions) (runtime.Object, error) {
return w.Deleter.Delete(ctx, name)
}
// CollectionDeleter is an object that can delete a collection
// of RESTful resources.
type CollectionDeleter interface {
// DeleteCollection selects all resources in the storage matching given 'listOptions'
// and deletes them. If 'options' are provided, the resource will attempt to honor
// them or return an invalid request error.
// DeleteCollection may not be atomic - i.e. it may delete some objects and still
// return an error after it. On success, returns a list of deleted objects.
DeleteCollection(ctx api.Context, options *api.DeleteOptions, listOptions *api.ListOptions) (runtime.Object, error)
}
// Creater is an object that can create an instance of a RESTful object.
type Creater interface {
// New returns an empty object that can be used with Create after request data has been put into it.
// This object must be a pointer type for use with Codec.DecodeInto([]byte, runtime.Object)
New() runtime.Object
// Create creates a new version of a resource.
Create(ctx api.Context, obj runtime.Object) (runtime.Object, error)
}
// NamedCreater is an object that can create an instance of a RESTful object using a name parameter.
type NamedCreater interface {
// New returns an empty object that can be used with Create after request data has been put into it.
// This object must be a pointer type for use with Codec.DecodeInto([]byte, runtime.Object)
New() runtime.Object
// Create creates a new version of a resource. It expects a name parameter from the path.
// This is needed for create operations on subresources which include the name of the parent
// resource in the path.
Create(ctx api.Context, name string, obj runtime.Object) (runtime.Object, error)
}
// UpdatedObjectInfo provides information about an updated object to an Updater.
// It requires access to the old object in order to return the newly updated object.
type UpdatedObjectInfo interface {
// Returns preconditions built from the updated object, if applicable.
// May return nil, or a preconditions object containing nil fields,
// if no preconditions can be determined from the updated object.
Preconditions() *api.Preconditions
// UpdatedObject returns the updated object, given a context and old object.
// The only time an empty oldObj should be passed in is if a "create on update" is occurring (there is no oldObj).
UpdatedObject(ctx api.Context, oldObj runtime.Object) (newObj runtime.Object, err error)
}
// Updater is an object that can update an instance of a RESTful object.
type Updater interface {
// New returns an empty object that can be used with Update after request data has been put into it.
// This object must be a pointer type for use with Codec.DecodeInto([]byte, runtime.Object)
New() runtime.Object
// Update finds a resource in the storage and updates it. Some implementations
// may allow updates creates the object - they should set the created boolean
// to true.
Update(ctx api.Context, name string, objInfo UpdatedObjectInfo) (runtime.Object, bool, error)
}
// CreaterUpdater is a storage object that must support both create and update.
// Go prevents embedded interfaces that implement the same method.
type CreaterUpdater interface {
Creater
Update(ctx api.Context, name string, objInfo UpdatedObjectInfo) (runtime.Object, bool, error)
}
// CreaterUpdater must satisfy the Updater interface.
var _ Updater = CreaterUpdater(nil)
// Patcher is a storage object that supports both get and update.
type Patcher interface {
Getter
Updater
}
// Watcher should be implemented by all Storage objects that
// want to offer the ability to watch for changes through the watch api.
type Watcher interface {
// 'label' selects on labels; 'field' selects on the object's fields. Not all fields
// are supported; an error should be returned if 'field' tries to select on a field that
// isn't supported. 'resourceVersion' allows for continuing/starting a watch at a
// particular version.
Watch(ctx api.Context, options *api.ListOptions) (watch.Interface, error)
}
// StandardStorage is an interface covering the common verbs. Provided for testing whether a
// resource satisfies the normal storage methods. Use Storage when passing opaque storage objects.
type StandardStorage interface {
Getter
Lister
CreaterUpdater
GracefulDeleter
CollectionDeleter
Watcher
}
// Redirector know how to return a remote resource's location.
type Redirector interface {
// ResourceLocation should return the remote location of the given resource, and an optional transport to use to request it, or an error.
ResourceLocation(ctx api.Context, id string) (remoteLocation *url.URL, transport http.RoundTripper, err error)
}
// Responder abstracts the normal response behavior for a REST method and is passed to callers that
// may wish to handle the response directly in some cases, but delegate to the normal error or object
// behavior in other cases.
type Responder interface {
// Object writes the provided object to the response. Invoking this method multiple times is undefined.
Object(statusCode int, obj runtime.Object)
// Error writes the provided error to the response. This method may only be invoked once.
Error(err error)
}
// Connecter is a storage object that responds to a connection request.
type Connecter interface {
// Connect returns an http.Handler that will handle the request/response for a given API invocation.
// The provided responder may be used for common API responses. The responder will write both status
// code and body, so the ServeHTTP method should exit after invoking the responder. The Handler will
// be used for a single API request and then discarded. The Responder is guaranteed to write to the
// same http.ResponseWriter passed to ServeHTTP.
Connect(ctx api.Context, id string, options runtime.Object, r Responder) (http.Handler, error)
// NewConnectOptions returns an empty options object that will be used to pass
// options to the Connect method. If nil, then a nil options object is passed to
// Connect. It may return a bool and a string. If true, the value of the request
// path below the object will be included as the named string in the serialization
// of the runtime object.
NewConnectOptions() (runtime.Object, bool, string)
// ConnectMethods returns the list of HTTP methods handled by Connect
ConnectMethods() []string
}
// ResourceStreamer is an interface implemented by objects that prefer to be streamed from the server
// instead of decoded directly.
type ResourceStreamer interface {
// InputStream should return an io.ReadCloser if the provided object supports streaming. The desired
// api version and an accept header (may be empty) are passed to the call. If no error occurs,
// the caller may return a flag indicating whether the result should be flushed as writes occur
// and a content type string that indicates the type of the stream.
// If a null stream is returned, a StatusNoContent response wil be generated.
InputStream(apiVersion, acceptHeader string) (stream io.ReadCloser, flush bool, mimeType string, err error)
}
// StorageMetadata is an optional interface that callers can implement to provide additional
// information about their Storage objects.
type StorageMetadata interface {
// ProducesMIMETypes returns a list of the MIME types the specified HTTP verb (GET, POST, DELETE,
// PATCH) can respond with.
ProducesMIMETypes(verb string) []string
}
// ConnectRequest is an object passed to admission control for Connect operations
type ConnectRequest struct {
// Name is the name of the object on which the connect request was made
Name string
// Options is the options object passed to the connect request. See the NewConnectOptions method on Connecter
Options runtime.Object
// ResourcePath is the path for the resource in the REST server (ie. "pods/proxy")
ResourcePath string
}
func (obj *ConnectRequest) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind }

30
vendor/k8s.io/kubernetes/pkg/api/rest/resttest/BUILD generated vendored Normal file
View file

@ -0,0 +1,30 @@
package(default_visibility = ["//visibility:public"])
licenses(["notice"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_binary",
"go_library",
"go_test",
"cgo_library",
)
go_library(
name = "go_default_library",
srcs = ["resttest.go"],
tags = ["automanaged"],
deps = [
"//pkg/api:go_default_library",
"//pkg/api/errors:go_default_library",
"//pkg/api/rest:go_default_library",
"//pkg/api/validation/path:go_default_library",
"//pkg/apis/meta/v1:go_default_library",
"//pkg/conversion:go_default_library",
"//pkg/fields:go_default_library",
"//pkg/labels:go_default_library",
"//pkg/runtime:go_default_library",
"//pkg/types:go_default_library",
"//pkg/util/wait:go_default_library",
],
)

File diff suppressed because it is too large Load diff

42
vendor/k8s.io/kubernetes/pkg/api/rest/types.go generated vendored Normal file
View file

@ -0,0 +1,42 @@
/*
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 rest
import (
"k8s.io/kubernetes/pkg/runtime"
)
// ObjectFunc is a function to act on a given object. An error may be returned
// if the hook cannot be completed. An ObjectFunc may transform the provided
// object.
type ObjectFunc func(obj runtime.Object) error
// AllFuncs returns an ObjectFunc that attempts to run all of the provided functions
// in order, returning early if there are any errors.
func AllFuncs(fns ...ObjectFunc) ObjectFunc {
return func(obj runtime.Object) error {
for _, fn := range fns {
if fn == nil {
continue
}
if err := fn(obj); err != nil {
return err
}
}
return nil
}
}

225
vendor/k8s.io/kubernetes/pkg/api/rest/update.go generated vendored Normal file
View file

@ -0,0 +1,225 @@
/*
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 rest
import (
"fmt"
"k8s.io/kubernetes/pkg/api"
"k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/api/meta"
"k8s.io/kubernetes/pkg/api/validation"
"k8s.io/kubernetes/pkg/runtime"
"k8s.io/kubernetes/pkg/util/validation/field"
)
// RESTUpdateStrategy defines the minimum validation, accepted input, and
// name generation behavior to update an object that follows Kubernetes
// API conventions. A resource may have many UpdateStrategies, depending on
// the call pattern in use.
type RESTUpdateStrategy interface {
runtime.ObjectTyper
// NamespaceScoped returns true if the object must be within a namespace.
NamespaceScoped() bool
// AllowCreateOnUpdate returns true if the object can be created by a PUT.
AllowCreateOnUpdate() bool
// PrepareForUpdate is invoked on update before validation to normalize
// the object. For example: remove fields that are not to be persisted,
// sort order-insensitive list fields, etc. This should not remove fields
// whose presence would be considered a validation error.
PrepareForUpdate(ctx api.Context, obj, old runtime.Object)
// ValidateUpdate is invoked after default fields in the object have been
// filled in before the object is persisted. This method should not mutate
// the object.
ValidateUpdate(ctx api.Context, obj, old runtime.Object) field.ErrorList
// Canonicalize is invoked after validation has succeeded but before the
// object has been persisted. This method may mutate the object.
Canonicalize(obj runtime.Object)
// AllowUnconditionalUpdate returns true if the object can be updated
// unconditionally (irrespective of the latest resource version), when
// there is no resource version specified in the object.
AllowUnconditionalUpdate() bool
}
// TODO: add other common fields that require global validation.
func validateCommonFields(obj, old runtime.Object) (field.ErrorList, error) {
allErrs := field.ErrorList{}
objectMeta, err := api.ObjectMetaFor(obj)
if err != nil {
return nil, fmt.Errorf("failed to get new object metadata: %v", err)
}
oldObjectMeta, err := api.ObjectMetaFor(old)
if err != nil {
return nil, fmt.Errorf("failed to get old object metadata: %v", err)
}
allErrs = append(allErrs, validation.ValidateObjectMetaUpdate(objectMeta, oldObjectMeta, field.NewPath("metadata"))...)
return allErrs, nil
}
// BeforeUpdate ensures that common operations for all resources are performed on update. It only returns
// errors that can be converted to api.Status. It will invoke update validation with the provided existing
// and updated objects.
func BeforeUpdate(strategy RESTUpdateStrategy, ctx api.Context, obj, old runtime.Object) error {
objectMeta, kind, kerr := objectMetaAndKind(strategy, obj)
if kerr != nil {
return kerr
}
if strategy.NamespaceScoped() {
if !api.ValidNamespace(ctx, objectMeta) {
return errors.NewBadRequest("the namespace of the provided object does not match the namespace sent on the request")
}
} else {
objectMeta.Namespace = api.NamespaceNone
}
// Ensure requests cannot update generation
oldMeta, err := api.ObjectMetaFor(old)
if err != nil {
return err
}
objectMeta.Generation = oldMeta.Generation
strategy.PrepareForUpdate(ctx, obj, old)
// ClusterName is ignored and should not be saved
objectMeta.ClusterName = ""
// Ensure some common fields, like UID, are validated for all resources.
errs, err := validateCommonFields(obj, old)
if err != nil {
return errors.NewInternalError(err)
}
errs = append(errs, strategy.ValidateUpdate(ctx, obj, old)...)
if len(errs) > 0 {
return errors.NewInvalid(kind.GroupKind(), objectMeta.Name, errs)
}
strategy.Canonicalize(obj)
return nil
}
// TransformFunc is a function to transform and return newObj
type TransformFunc func(ctx api.Context, newObj runtime.Object, oldObj runtime.Object) (transformedNewObj runtime.Object, err error)
// defaultUpdatedObjectInfo implements UpdatedObjectInfo
type defaultUpdatedObjectInfo struct {
// obj is the updated object
obj runtime.Object
// copier makes a copy of the object before returning it.
// this allows repeated calls to UpdatedObject() to return
// pristine data, even if the returned value is mutated.
copier runtime.ObjectCopier
// transformers is an optional list of transforming functions that modify or
// replace obj using information from the context, old object, or other sources.
transformers []TransformFunc
}
// DefaultUpdatedObjectInfo returns an UpdatedObjectInfo impl based on the specified object.
func DefaultUpdatedObjectInfo(obj runtime.Object, copier runtime.ObjectCopier, transformers ...TransformFunc) UpdatedObjectInfo {
return &defaultUpdatedObjectInfo{obj, copier, transformers}
}
// Preconditions satisfies the UpdatedObjectInfo interface.
func (i *defaultUpdatedObjectInfo) Preconditions() *api.Preconditions {
// Attempt to get the UID out of the object
accessor, err := meta.Accessor(i.obj)
if err != nil {
// If no UID can be read, no preconditions are possible
return nil
}
// If empty, no preconditions needed
uid := accessor.GetUID()
if len(uid) == 0 {
return nil
}
return &api.Preconditions{UID: &uid}
}
// UpdatedObject satisfies the UpdatedObjectInfo interface.
// It returns a copy of the held obj, passed through any configured transformers.
func (i *defaultUpdatedObjectInfo) UpdatedObject(ctx api.Context, oldObj runtime.Object) (runtime.Object, error) {
var err error
// Start with the configured object
newObj := i.obj
// If the original is non-nil (might be nil if the first transformer builds the object from the oldObj), make a copy,
// so we don't return the original. BeforeUpdate can mutate the returned object, doing things like clearing ResourceVersion.
// If we're re-called, we need to be able to return the pristine version.
if newObj != nil {
newObj, err = i.copier.Copy(newObj)
if err != nil {
return nil, err
}
}
// Allow any configured transformers to update the new object
for _, transformer := range i.transformers {
newObj, err = transformer(ctx, newObj, oldObj)
if err != nil {
return nil, err
}
}
return newObj, nil
}
// wrappedUpdatedObjectInfo allows wrapping an existing objInfo and
// chaining additional transformations/checks on the result of UpdatedObject()
type wrappedUpdatedObjectInfo struct {
// obj is the updated object
objInfo UpdatedObjectInfo
// transformers is an optional list of transforming functions that modify or
// replace obj using information from the context, old object, or other sources.
transformers []TransformFunc
}
// WrapUpdatedObjectInfo returns an UpdatedObjectInfo impl that delegates to
// the specified objInfo, then calls the passed transformers
func WrapUpdatedObjectInfo(objInfo UpdatedObjectInfo, transformers ...TransformFunc) UpdatedObjectInfo {
return &wrappedUpdatedObjectInfo{objInfo, transformers}
}
// Preconditions satisfies the UpdatedObjectInfo interface.
func (i *wrappedUpdatedObjectInfo) Preconditions() *api.Preconditions {
return i.objInfo.Preconditions()
}
// UpdatedObject satisfies the UpdatedObjectInfo interface.
// It delegates to the wrapped objInfo and passes the result through any configured transformers.
func (i *wrappedUpdatedObjectInfo) UpdatedObject(ctx api.Context, oldObj runtime.Object) (runtime.Object, error) {
newObj, err := i.objInfo.UpdatedObject(ctx, oldObj)
if err != nil {
return newObj, err
}
// Allow any configured transformers to update the new object or error
for _, transformer := range i.transformers {
newObj, err = transformer(ctx, newObj, oldObj)
if err != nil {
return nil, err
}
}
return newObj, nil
}

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