vendor: remove dep and use vndr

Signed-off-by: Antonio Murdaca <runcom@redhat.com>
This commit is contained in:
Antonio Murdaca 2017-06-06 09:19:04 +02:00
parent 16f44674a4
commit 148e72d81e
No known key found for this signature in database
GPG key ID: B2BEAD150DE936B9
16131 changed files with 73815 additions and 4235138 deletions

View file

@ -1 +0,0 @@
acda1e7401fbf79d89ba23df5e11d81f4a9d4e0d

View file

@ -1,61 +0,0 @@
#!/bin/bash
# Copyright 2017 The Kubernetes Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
# overall flow
# 1. fetch the current level of k8s.io/kubernetes
# 2. check out the k8s.io/kubernetes HEAD into a separate branch
# 3. rewrite the history on that branch to *only* include staging/src/k8s.io/apimachinery
# 4. locate all commits between the last time we sync'ed and now
# 5. switch back to the starting branch
# 6. for each commit, cherry-pick it (which will keep authorship) into current branch
# 7. update metadata files indicating which commits we've sync'ed to
set -e
dir=$(mktemp -d "${TMPDIR:-/tmp/}$(basename 0).XXXXXXXXXXXX")
git remote add upstream-kube git@github.com:kubernetes/kubernetes.git || true
git fetch upstream-kube
currBranch=$(git rev-parse --abbrev-ref HEAD)
previousKubeSHA=$(cat kubernetes-sha)
previousBranchSHA=$(cat filter-branch-sha)
git branch -D kube-sync || true
git checkout upstream-kube/master -b kube-sync
git reset --hard upstream-kube/master
newKubeSHA=$(git log --oneline --format='%H' kube-sync -1)
# this command rewrite git history to *only* include staging/src/k8s.io/apimachinery
git filter-branch -f --subdirectory-filter staging/src/k8s.io/apimachinery HEAD
newBranchSHA=$(git log --oneline --format='%H' kube-sync -1)
git log --no-merges --format='%H' --reverse ${previousBranchSHA}..HEAD > ${dir}/commits
git checkout ${currBranch}
while read commitSHA; do
echo "working ${commitSHA}"
git cherry-pick ${commitSHA}
done <${dir}/commits
# track the k8s.io/kubernetes commit SHA so we can always determine which level of kube this repo matches
# track the filtered branch commit SHA so that we can determine which commits need to be picked
echo ${newKubeSHA} > kubernetes-sha
echo ${newBranchSHA} > filter-branch-sha
git commit -m "sync(k8s.io/kubernetes): ${newKubeSHA}" -- kubernetes-sha filter-branch-sha

View file

@ -1 +0,0 @@
424a0e9e827a39c714ee03e1891c43635adabcdc

View file

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

View file

@ -1,46 +0,0 @@
/*
Copyright 2014 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package equality
import (
"k8s.io/apimachinery/pkg/api/resource"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/conversion"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
)
// Semantic can do semantic deep equality checks for api objects.
// Example: apiequality.Semantic.DeepEqual(aPod, aPodWithNonNilButEmptyMaps) == true
var Semantic = conversion.EqualitiesOrDie(
func(a, b resource.Quantity) bool {
// Ignore formatting, only care that numeric value stayed the same.
// TODO: if we decide it's important, it should be safe to start comparing the format.
//
// Uninitialized quantities are equivalent to 0 quantities.
return a.Cmp(b) == 0
},
func(a, b metav1.Time) bool {
return a.UTC() == b.UTC()
},
func(a, b labels.Selector) bool {
return a.String() == b.String()
},
func(a, b fields.Selector) bool {
return a.String() == b.String()
},
)

View file

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

View file

@ -1,196 +0,0 @@
/*
Copyright 2014 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package errors
import (
"errors"
"fmt"
"reflect"
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/validation/field"
)
func resource(resource string) schema.GroupResource {
return schema.GroupResource{Group: "", Resource: resource}
}
func kind(kind string) schema.GroupKind {
return schema.GroupKind{Group: "", Kind: kind}
}
func TestErrorNew(t *testing.T) {
err := NewAlreadyExists(resource("tests"), "1")
if !IsAlreadyExists(err) {
t.Errorf("expected to be %s", metav1.StatusReasonAlreadyExists)
}
if IsConflict(err) {
t.Errorf("expected to not be %s", metav1.StatusReasonConflict)
}
if IsNotFound(err) {
t.Errorf(fmt.Sprintf("expected to not be %s", metav1.StatusReasonNotFound))
}
if IsInvalid(err) {
t.Errorf("expected to not be %s", metav1.StatusReasonInvalid)
}
if IsBadRequest(err) {
t.Errorf("expected to not be %s", metav1.StatusReasonBadRequest)
}
if IsForbidden(err) {
t.Errorf("expected to not be %s", metav1.StatusReasonForbidden)
}
if IsServerTimeout(err) {
t.Errorf("expected to not be %s", metav1.StatusReasonServerTimeout)
}
if IsMethodNotSupported(err) {
t.Errorf("expected to not be %s", metav1.StatusReasonMethodNotAllowed)
}
if !IsConflict(NewConflict(resource("tests"), "2", errors.New("message"))) {
t.Errorf("expected to be conflict")
}
if !IsNotFound(NewNotFound(resource("tests"), "3")) {
t.Errorf("expected to be %s", metav1.StatusReasonNotFound)
}
if !IsInvalid(NewInvalid(kind("Test"), "2", nil)) {
t.Errorf("expected to be %s", metav1.StatusReasonInvalid)
}
if !IsBadRequest(NewBadRequest("reason")) {
t.Errorf("expected to be %s", metav1.StatusReasonBadRequest)
}
if !IsForbidden(NewForbidden(resource("tests"), "2", errors.New("reason"))) {
t.Errorf("expected to be %s", metav1.StatusReasonForbidden)
}
if !IsUnauthorized(NewUnauthorized("reason")) {
t.Errorf("expected to be %s", metav1.StatusReasonUnauthorized)
}
if !IsServerTimeout(NewServerTimeout(resource("tests"), "reason", 0)) {
t.Errorf("expected to be %s", metav1.StatusReasonServerTimeout)
}
if time, ok := SuggestsClientDelay(NewServerTimeout(resource("tests"), "doing something", 10)); time != 10 || !ok {
t.Errorf("expected to be %s", metav1.StatusReasonServerTimeout)
}
if time, ok := SuggestsClientDelay(NewTimeoutError("test reason", 10)); time != 10 || !ok {
t.Errorf("expected to be %s", metav1.StatusReasonTimeout)
}
if !IsMethodNotSupported(NewMethodNotSupported(resource("foos"), "delete")) {
t.Errorf("expected to be %s", metav1.StatusReasonMethodNotAllowed)
}
}
func TestNewInvalid(t *testing.T) {
testCases := []struct {
Err *field.Error
Details *metav1.StatusDetails
}{
{
field.Duplicate(field.NewPath("field[0].name"), "bar"),
&metav1.StatusDetails{
Kind: "Kind",
Name: "name",
Causes: []metav1.StatusCause{{
Type: metav1.CauseTypeFieldValueDuplicate,
Field: "field[0].name",
}},
},
},
{
field.Invalid(field.NewPath("field[0].name"), "bar", "detail"),
&metav1.StatusDetails{
Kind: "Kind",
Name: "name",
Causes: []metav1.StatusCause{{
Type: metav1.CauseTypeFieldValueInvalid,
Field: "field[0].name",
}},
},
},
{
field.NotFound(field.NewPath("field[0].name"), "bar"),
&metav1.StatusDetails{
Kind: "Kind",
Name: "name",
Causes: []metav1.StatusCause{{
Type: metav1.CauseTypeFieldValueNotFound,
Field: "field[0].name",
}},
},
},
{
field.NotSupported(field.NewPath("field[0].name"), "bar", nil),
&metav1.StatusDetails{
Kind: "Kind",
Name: "name",
Causes: []metav1.StatusCause{{
Type: metav1.CauseTypeFieldValueNotSupported,
Field: "field[0].name",
}},
},
},
{
field.Required(field.NewPath("field[0].name"), ""),
&metav1.StatusDetails{
Kind: "Kind",
Name: "name",
Causes: []metav1.StatusCause{{
Type: metav1.CauseTypeFieldValueRequired,
Field: "field[0].name",
}},
},
},
}
for i, testCase := range testCases {
vErr, expected := testCase.Err, testCase.Details
expected.Causes[0].Message = vErr.ErrorBody()
err := NewInvalid(kind("Kind"), "name", field.ErrorList{vErr})
status := err.ErrStatus
if status.Code != 422 || status.Reason != metav1.StatusReasonInvalid {
t.Errorf("%d: unexpected status: %#v", i, status)
}
if !reflect.DeepEqual(expected, status.Details) {
t.Errorf("%d: expected %#v, got %#v", i, expected, status.Details)
}
}
}
func Test_reasonForError(t *testing.T) {
if e, a := metav1.StatusReasonUnknown, reasonForError(nil); e != a {
t.Errorf("unexpected reason type: %#v", a)
}
}
type TestType struct{}
func (obj *TestType) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind }
func TestFromObject(t *testing.T) {
table := []struct {
obj runtime.Object
message string
}{
{&metav1.Status{Message: "foobar"}, "foobar"},
{&TestType{}, "unexpected object: &{}"},
}
for _, item := range table {
if e, a := item.message, FromObject(item.obj).Error(); e != a {
t.Errorf("Expected %v, got %v", e, a)
}
}
}

View file

@ -1,26 +0,0 @@
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

View file

@ -334,6 +334,14 @@ func extractFromOwnerReference(v reflect.Value, o *metav1.OwnerReference) error
controller := *controllerPtr
o.Controller = &controller
}
var blockOwnerDeletionPtr *bool
if err := runtime.Field(v, "BlockOwnerDeletion", &blockOwnerDeletionPtr); err != nil {
return err
}
if blockOwnerDeletionPtr != nil {
block := *blockOwnerDeletionPtr
o.BlockOwnerDeletion = &block
}
return nil
}
@ -357,6 +365,12 @@ func setOwnerReference(v reflect.Value, o *metav1.OwnerReference) error {
return err
}
}
if o.BlockOwnerDeletion != nil {
block := *(o.BlockOwnerDeletion)
if err := runtime.SetField(&block, v, "BlockOwnerDeletion"); err != nil {
return err
}
}
return nil
}

View file

@ -1,359 +0,0 @@
/*
Copyright 2014 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package meta
import (
"errors"
"reflect"
"testing"
"k8s.io/apimachinery/pkg/runtime/schema"
)
func TestMultiRESTMapperResourceFor(t *testing.T) {
tcs := []struct {
name string
mapper MultiRESTMapper
input schema.GroupVersionResource
result schema.GroupVersionResource
err error
}{
{
name: "empty",
mapper: MultiRESTMapper{},
input: schema.GroupVersionResource{Resource: "foo"},
result: schema.GroupVersionResource{},
err: &NoResourceMatchError{PartialResource: schema.GroupVersionResource{Resource: "foo"}},
},
{
name: "ignore not found",
mapper: MultiRESTMapper{fixedRESTMapper{err: &NoResourceMatchError{PartialResource: schema.GroupVersionResource{Resource: "IGNORE_THIS"}}}},
input: schema.GroupVersionResource{Resource: "foo"},
result: schema.GroupVersionResource{},
err: &NoResourceMatchError{PartialResource: schema.GroupVersionResource{Resource: "foo"}},
},
{
name: "accept first failure",
mapper: MultiRESTMapper{fixedRESTMapper{err: errors.New("fail on this")}, fixedRESTMapper{resourcesFor: []schema.GroupVersionResource{{Resource: "unused"}}}},
input: schema.GroupVersionResource{Resource: "foo"},
result: schema.GroupVersionResource{},
err: errors.New("fail on this"),
},
}
for _, tc := range tcs {
actualResult, actualErr := tc.mapper.ResourceFor(tc.input)
if e, a := tc.result, actualResult; e != a {
t.Errorf("%s: expected %v, got %v", tc.name, e, a)
}
switch {
case tc.err == nil && actualErr == nil:
case tc.err == nil:
t.Errorf("%s: unexpected error: %v", tc.name, actualErr)
case actualErr == nil:
t.Errorf("%s: expected error: %v got nil", tc.name, tc.err)
case tc.err.Error() != actualErr.Error():
t.Errorf("%s: expected %v, got %v", tc.name, tc.err, actualErr)
}
}
}
func TestMultiRESTMapperResourcesFor(t *testing.T) {
tcs := []struct {
name string
mapper MultiRESTMapper
input schema.GroupVersionResource
result []schema.GroupVersionResource
err error
}{
{
name: "empty",
mapper: MultiRESTMapper{},
input: schema.GroupVersionResource{Resource: "foo"},
result: nil,
err: &NoResourceMatchError{PartialResource: schema.GroupVersionResource{Resource: "foo"}},
},
{
name: "ignore not found",
mapper: MultiRESTMapper{fixedRESTMapper{err: &NoResourceMatchError{PartialResource: schema.GroupVersionResource{Resource: "IGNORE_THIS"}}}},
input: schema.GroupVersionResource{Resource: "foo"},
result: nil,
err: &NoResourceMatchError{PartialResource: schema.GroupVersionResource{Resource: "foo"}},
},
{
name: "accept first failure",
mapper: MultiRESTMapper{fixedRESTMapper{err: errors.New("fail on this")}, fixedRESTMapper{resourcesFor: []schema.GroupVersionResource{{Resource: "unused"}}}},
input: schema.GroupVersionResource{Resource: "foo"},
result: nil,
err: errors.New("fail on this"),
},
{
name: "union and dedup",
mapper: MultiRESTMapper{
fixedRESTMapper{resourcesFor: []schema.GroupVersionResource{{Resource: "dupe"}, {Resource: "first"}}},
fixedRESTMapper{resourcesFor: []schema.GroupVersionResource{{Resource: "dupe"}, {Resource: "second"}}},
},
input: schema.GroupVersionResource{Resource: "foo"},
result: []schema.GroupVersionResource{{Resource: "dupe"}, {Resource: "first"}, {Resource: "second"}},
},
{
name: "skip not and continue",
mapper: MultiRESTMapper{
fixedRESTMapper{err: &NoResourceMatchError{PartialResource: schema.GroupVersionResource{Resource: "IGNORE_THIS"}}},
fixedRESTMapper{resourcesFor: []schema.GroupVersionResource{{Resource: "first"}, {Resource: "second"}}},
},
input: schema.GroupVersionResource{Resource: "foo"},
result: []schema.GroupVersionResource{{Resource: "first"}, {Resource: "second"}},
},
}
for _, tc := range tcs {
actualResult, actualErr := tc.mapper.ResourcesFor(tc.input)
if e, a := tc.result, actualResult; !reflect.DeepEqual(e, a) {
t.Errorf("%s: expected %v, got %v", tc.name, e, a)
}
switch {
case tc.err == nil && actualErr == nil:
case tc.err == nil:
t.Errorf("%s: unexpected error: %v", tc.name, actualErr)
case actualErr == nil:
t.Errorf("%s: expected error: %v got nil", tc.name, tc.err)
case tc.err.Error() != actualErr.Error():
t.Errorf("%s: expected %v, got %v", tc.name, tc.err, actualErr)
}
}
}
func TestMultiRESTMapperKindsFor(t *testing.T) {
tcs := []struct {
name string
mapper MultiRESTMapper
input schema.GroupVersionResource
result []schema.GroupVersionKind
err error
}{
{
name: "empty",
mapper: MultiRESTMapper{},
input: schema.GroupVersionResource{Resource: "foo"},
result: nil,
err: &NoResourceMatchError{PartialResource: schema.GroupVersionResource{Resource: "foo"}},
},
{
name: "ignore not found",
mapper: MultiRESTMapper{fixedRESTMapper{err: &NoResourceMatchError{PartialResource: schema.GroupVersionResource{Resource: "IGNORE_THIS"}}}},
input: schema.GroupVersionResource{Resource: "foo"},
result: nil,
err: &NoResourceMatchError{PartialResource: schema.GroupVersionResource{Resource: "foo"}},
},
{
name: "accept first failure",
mapper: MultiRESTMapper{fixedRESTMapper{err: errors.New("fail on this")}, fixedRESTMapper{kindsFor: []schema.GroupVersionKind{{Kind: "unused"}}}},
input: schema.GroupVersionResource{Resource: "foo"},
result: nil,
err: errors.New("fail on this"),
},
{
name: "union and dedup",
mapper: MultiRESTMapper{
fixedRESTMapper{kindsFor: []schema.GroupVersionKind{{Kind: "dupe"}, {Kind: "first"}}},
fixedRESTMapper{kindsFor: []schema.GroupVersionKind{{Kind: "dupe"}, {Kind: "second"}}},
},
input: schema.GroupVersionResource{Resource: "foo"},
result: []schema.GroupVersionKind{{Kind: "dupe"}, {Kind: "first"}, {Kind: "second"}},
},
{
name: "skip not and continue",
mapper: MultiRESTMapper{
fixedRESTMapper{err: &NoResourceMatchError{PartialResource: schema.GroupVersionResource{Resource: "IGNORE_THIS"}}},
fixedRESTMapper{kindsFor: []schema.GroupVersionKind{{Kind: "first"}, {Kind: "second"}}},
},
input: schema.GroupVersionResource{Resource: "foo"},
result: []schema.GroupVersionKind{{Kind: "first"}, {Kind: "second"}},
},
}
for _, tc := range tcs {
actualResult, actualErr := tc.mapper.KindsFor(tc.input)
if e, a := tc.result, actualResult; !reflect.DeepEqual(e, a) {
t.Errorf("%s: expected %v, got %v", tc.name, e, a)
}
switch {
case tc.err == nil && actualErr == nil:
case tc.err == nil:
t.Errorf("%s: unexpected error: %v", tc.name, actualErr)
case actualErr == nil:
t.Errorf("%s: expected error: %v got nil", tc.name, tc.err)
case tc.err.Error() != actualErr.Error():
t.Errorf("%s: expected %v, got %v", tc.name, tc.err, actualErr)
}
}
}
func TestMultiRESTMapperKindFor(t *testing.T) {
tcs := []struct {
name string
mapper MultiRESTMapper
input schema.GroupVersionResource
result schema.GroupVersionKind
err error
}{
{
name: "empty",
mapper: MultiRESTMapper{},
input: schema.GroupVersionResource{Resource: "foo"},
result: schema.GroupVersionKind{},
err: &NoResourceMatchError{PartialResource: schema.GroupVersionResource{Resource: "foo"}},
},
{
name: "ignore not found",
mapper: MultiRESTMapper{fixedRESTMapper{err: &NoResourceMatchError{PartialResource: schema.GroupVersionResource{Resource: "IGNORE_THIS"}}}},
input: schema.GroupVersionResource{Resource: "foo"},
result: schema.GroupVersionKind{},
err: &NoResourceMatchError{PartialResource: schema.GroupVersionResource{Resource: "foo"}},
},
{
name: "accept first failure",
mapper: MultiRESTMapper{fixedRESTMapper{err: errors.New("fail on this")}, fixedRESTMapper{kindsFor: []schema.GroupVersionKind{{Kind: "unused"}}}},
input: schema.GroupVersionResource{Resource: "foo"},
result: schema.GroupVersionKind{},
err: errors.New("fail on this"),
},
}
for _, tc := range tcs {
actualResult, actualErr := tc.mapper.KindFor(tc.input)
if e, a := tc.result, actualResult; e != a {
t.Errorf("%s: expected %v, got %v", tc.name, e, a)
}
switch {
case tc.err == nil && actualErr == nil:
case tc.err == nil:
t.Errorf("%s: unexpected error: %v", tc.name, actualErr)
case actualErr == nil:
t.Errorf("%s: expected error: %v got nil", tc.name, tc.err)
case tc.err.Error() != actualErr.Error():
t.Errorf("%s: expected %v, got %v", tc.name, tc.err, actualErr)
}
}
}
func TestMultiRESTMapperRESTMappings(t *testing.T) {
mapping1, mapping2 := &RESTMapping{}, &RESTMapping{}
tcs := []struct {
name string
mapper MultiRESTMapper
input schema.GroupKind
result []*RESTMapping
err error
}{
{
name: "empty",
mapper: MultiRESTMapper{},
input: schema.GroupKind{Kind: "Foo"},
result: nil,
err: &NoKindMatchError{PartialKind: schema.GroupVersionKind{Kind: "Foo"}},
},
{
name: "ignore not found",
mapper: MultiRESTMapper{fixedRESTMapper{err: &NoKindMatchError{PartialKind: schema.GroupVersionKind{Kind: "IGNORE_THIS"}}}},
input: schema.GroupKind{Kind: "Foo"},
result: nil,
err: &NoKindMatchError{PartialKind: schema.GroupVersionKind{Kind: "Foo"}},
},
{
name: "accept first failure",
mapper: MultiRESTMapper{fixedRESTMapper{err: errors.New("fail on this")}, fixedRESTMapper{mappings: []*RESTMapping{mapping1}}},
input: schema.GroupKind{Kind: "Foo"},
result: nil,
err: errors.New("fail on this"),
},
{
name: "return both",
mapper: MultiRESTMapper{fixedRESTMapper{mappings: []*RESTMapping{mapping1}}, fixedRESTMapper{mappings: []*RESTMapping{mapping2}}},
input: schema.GroupKind{Kind: "Foo"},
result: []*RESTMapping{mapping1, mapping2},
},
}
for _, tc := range tcs {
actualResult, actualErr := tc.mapper.RESTMappings(tc.input)
if e, a := tc.result, actualResult; !reflect.DeepEqual(e, a) {
t.Errorf("%s: expected %v, got %v", tc.name, e, a)
}
switch {
case tc.err == nil && actualErr == nil:
case tc.err == nil:
t.Errorf("%s: unexpected error: %v", tc.name, actualErr)
case actualErr == nil:
t.Errorf("%s: expected error: %v got nil", tc.name, tc.err)
case tc.err.Error() != actualErr.Error():
t.Errorf("%s: expected %v, got %v", tc.name, tc.err, actualErr)
}
}
}
type fixedRESTMapper struct {
resourcesFor []schema.GroupVersionResource
kindsFor []schema.GroupVersionKind
resourceFor schema.GroupVersionResource
kindFor schema.GroupVersionKind
mappings []*RESTMapping
err error
}
func (m fixedRESTMapper) ResourceSingularizer(resource string) (singular string, err error) {
return "", m.err
}
func (m fixedRESTMapper) ResourcesFor(resource schema.GroupVersionResource) ([]schema.GroupVersionResource, error) {
return m.resourcesFor, m.err
}
func (m fixedRESTMapper) KindsFor(resource schema.GroupVersionResource) (gvk []schema.GroupVersionKind, err error) {
return m.kindsFor, m.err
}
func (m fixedRESTMapper) ResourceFor(resource schema.GroupVersionResource) (schema.GroupVersionResource, error) {
return m.resourceFor, m.err
}
func (m fixedRESTMapper) KindFor(resource schema.GroupVersionResource) (schema.GroupVersionKind, error) {
return m.kindFor, m.err
}
func (m fixedRESTMapper) RESTMapping(gk schema.GroupKind, versions ...string) (mapping *RESTMapping, err error) {
return nil, m.err
}
func (m fixedRESTMapper) RESTMappings(gk schema.GroupKind, versions ...string) (mappings []*RESTMapping, err error) {
return m.mappings, m.err
}
func (m fixedRESTMapper) AliasesForResource(alias string) (aliases []string, ok bool) {
return nil, false
}
func (m fixedRESTMapper) ResourceIsValid(resource schema.GroupVersionResource) bool {
return false
}

View file

@ -1,346 +0,0 @@
/*
Copyright 2016 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package meta
import (
"errors"
"reflect"
"strings"
"testing"
"k8s.io/apimachinery/pkg/runtime/schema"
)
func TestPriorityRESTMapperResourceForErrorHandling(t *testing.T) {
tcs := []struct {
name string
delegate RESTMapper
resourcePatterns []schema.GroupVersionResource
result schema.GroupVersionResource
err string
}{
{
name: "single hit",
delegate: fixedRESTMapper{resourcesFor: []schema.GroupVersionResource{{Resource: "single-hit"}}},
result: schema.GroupVersionResource{Resource: "single-hit"},
},
{
name: "ambiguous match",
delegate: fixedRESTMapper{resourcesFor: []schema.GroupVersionResource{
{Group: "one", Version: "a", Resource: "first"},
{Group: "two", Version: "b", Resource: "second"},
}},
err: "matches multiple resources",
},
{
name: "group selection",
delegate: fixedRESTMapper{resourcesFor: []schema.GroupVersionResource{
{Group: "one", Version: "a", Resource: "first"},
{Group: "two", Version: "b", Resource: "second"},
}},
resourcePatterns: []schema.GroupVersionResource{
{Group: "one", Version: AnyVersion, Resource: AnyResource},
},
result: schema.GroupVersionResource{Group: "one", Version: "a", Resource: "first"},
},
{
name: "empty match continues",
delegate: fixedRESTMapper{resourcesFor: []schema.GroupVersionResource{
{Group: "one", Version: "a", Resource: "first"},
{Group: "two", Version: "b", Resource: "second"},
}},
resourcePatterns: []schema.GroupVersionResource{
{Group: "fail", Version: AnyVersion, Resource: AnyResource},
{Group: "one", Version: AnyVersion, Resource: AnyResource},
},
result: schema.GroupVersionResource{Group: "one", Version: "a", Resource: "first"},
},
{
name: "group followed by version selection",
delegate: fixedRESTMapper{resourcesFor: []schema.GroupVersionResource{
{Group: "one", Version: "a", Resource: "first"},
{Group: "two", Version: "b", Resource: "second"},
{Group: "one", Version: "c", Resource: "third"},
}},
resourcePatterns: []schema.GroupVersionResource{
{Group: "one", Version: AnyVersion, Resource: AnyResource},
{Group: AnyGroup, Version: "a", Resource: AnyResource},
},
result: schema.GroupVersionResource{Group: "one", Version: "a", Resource: "first"},
},
{
name: "resource selection",
delegate: fixedRESTMapper{resourcesFor: []schema.GroupVersionResource{
{Group: "one", Version: "a", Resource: "first"},
{Group: "one", Version: "a", Resource: "second"},
}},
resourcePatterns: []schema.GroupVersionResource{
{Group: AnyGroup, Version: AnyVersion, Resource: "second"},
},
result: schema.GroupVersionResource{Group: "one", Version: "a", Resource: "second"},
},
}
for _, tc := range tcs {
mapper := PriorityRESTMapper{Delegate: tc.delegate, ResourcePriority: tc.resourcePatterns}
actualResult, actualErr := mapper.ResourceFor(schema.GroupVersionResource{})
if e, a := tc.result, actualResult; e != a {
t.Errorf("%s: expected %v, got %v", tc.name, e, a)
}
if len(tc.err) == 0 && actualErr == nil {
continue
}
if len(tc.err) > 0 && actualErr == nil {
t.Errorf("%s: missing expected err: %v", tc.name, tc.err)
continue
}
if !strings.Contains(actualErr.Error(), tc.err) {
t.Errorf("%s: expected %v, got %v", tc.name, tc.err, actualErr)
}
}
}
func TestPriorityRESTMapperKindForErrorHandling(t *testing.T) {
tcs := []struct {
name string
delegate RESTMapper
kindPatterns []schema.GroupVersionKind
result schema.GroupVersionKind
err string
}{
{
name: "single hit",
delegate: fixedRESTMapper{kindsFor: []schema.GroupVersionKind{{Kind: "single-hit"}}},
result: schema.GroupVersionKind{Kind: "single-hit"},
},
{
name: "ambiguous match",
delegate: fixedRESTMapper{kindsFor: []schema.GroupVersionKind{
{Group: "one", Version: "a", Kind: "first"},
{Group: "two", Version: "b", Kind: "second"},
}},
err: "matches multiple kinds",
},
{
name: "group selection",
delegate: fixedRESTMapper{kindsFor: []schema.GroupVersionKind{
{Group: "one", Version: "a", Kind: "first"},
{Group: "two", Version: "b", Kind: "second"},
}},
kindPatterns: []schema.GroupVersionKind{
{Group: "one", Version: AnyVersion, Kind: AnyKind},
},
result: schema.GroupVersionKind{Group: "one", Version: "a", Kind: "first"},
},
{
name: "empty match continues",
delegate: fixedRESTMapper{kindsFor: []schema.GroupVersionKind{
{Group: "one", Version: "a", Kind: "first"},
{Group: "two", Version: "b", Kind: "second"},
}},
kindPatterns: []schema.GroupVersionKind{
{Group: "fail", Version: AnyVersion, Kind: AnyKind},
{Group: "one", Version: AnyVersion, Kind: AnyKind},
},
result: schema.GroupVersionKind{Group: "one", Version: "a", Kind: "first"},
},
{
name: "group followed by version selection",
delegate: fixedRESTMapper{kindsFor: []schema.GroupVersionKind{
{Group: "one", Version: "a", Kind: "first"},
{Group: "two", Version: "b", Kind: "second"},
{Group: "one", Version: "c", Kind: "third"},
}},
kindPatterns: []schema.GroupVersionKind{
{Group: "one", Version: AnyVersion, Kind: AnyKind},
{Group: AnyGroup, Version: "a", Kind: AnyKind},
},
result: schema.GroupVersionKind{Group: "one", Version: "a", Kind: "first"},
},
{
name: "kind selection",
delegate: fixedRESTMapper{kindsFor: []schema.GroupVersionKind{
{Group: "one", Version: "a", Kind: "first"},
{Group: "one", Version: "a", Kind: "second"},
}},
kindPatterns: []schema.GroupVersionKind{
{Group: AnyGroup, Version: AnyVersion, Kind: "second"},
},
result: schema.GroupVersionKind{Group: "one", Version: "a", Kind: "second"},
},
}
for _, tc := range tcs {
mapper := PriorityRESTMapper{Delegate: tc.delegate, KindPriority: tc.kindPatterns}
actualResult, actualErr := mapper.KindFor(schema.GroupVersionResource{})
if e, a := tc.result, actualResult; e != a {
t.Errorf("%s: expected %v, got %v", tc.name, e, a)
}
if len(tc.err) == 0 && actualErr == nil {
continue
}
if len(tc.err) > 0 && actualErr == nil {
t.Errorf("%s: missing expected err: %v", tc.name, tc.err)
continue
}
if !strings.Contains(actualErr.Error(), tc.err) {
t.Errorf("%s: expected %v, got %v", tc.name, tc.err, actualErr)
}
}
}
func TestPriorityRESTMapperRESTMapping(t *testing.T) {
mapping1 := &RESTMapping{
GroupVersionKind: schema.GroupVersionKind{Kind: "Foo", Version: "v1alpha1"},
}
mapping2 := &RESTMapping{
GroupVersionKind: schema.GroupVersionKind{Kind: "Foo", Version: "v1"},
}
mapping3 := &RESTMapping{
GroupVersionKind: schema.GroupVersionKind{Group: "other", Kind: "Foo", Version: "v1"},
}
allMappers := MultiRESTMapper{
fixedRESTMapper{mappings: []*RESTMapping{mapping1}},
fixedRESTMapper{mappings: []*RESTMapping{mapping2}},
fixedRESTMapper{mappings: []*RESTMapping{mapping3}},
}
tcs := []struct {
name string
mapper PriorityRESTMapper
input schema.GroupKind
result *RESTMapping
err error
}{
{
name: "empty",
mapper: PriorityRESTMapper{Delegate: MultiRESTMapper{}},
input: schema.GroupKind{Kind: "Foo"},
err: &NoKindMatchError{PartialKind: schema.GroupVersionKind{Kind: "Foo"}},
},
{
name: "ignore not found",
mapper: PriorityRESTMapper{Delegate: MultiRESTMapper{fixedRESTMapper{err: &NoKindMatchError{PartialKind: schema.GroupVersionKind{Kind: "IGNORE_THIS"}}}}},
input: schema.GroupKind{Kind: "Foo"},
err: &NoKindMatchError{PartialKind: schema.GroupVersionKind{Kind: "Foo"}},
},
{
name: "accept first failure",
mapper: PriorityRESTMapper{Delegate: MultiRESTMapper{fixedRESTMapper{err: errors.New("fail on this")}, fixedRESTMapper{mappings: []*RESTMapping{mapping1}}}},
input: schema.GroupKind{Kind: "Foo"},
err: errors.New("fail on this"),
},
{
name: "return error for ambiguous",
mapper: PriorityRESTMapper{
Delegate: allMappers,
},
input: schema.GroupKind{Kind: "Foo"},
err: &AmbiguousKindError{
PartialKind: schema.GroupVersionKind{Kind: "Foo"},
MatchingKinds: []schema.GroupVersionKind{
{Kind: "Foo", Version: "v1alpha1"},
{Kind: "Foo", Version: "v1"},
{Group: "other", Kind: "Foo", Version: "v1"},
},
},
},
{
name: "accept only item",
mapper: PriorityRESTMapper{
Delegate: fixedRESTMapper{mappings: []*RESTMapping{mapping1}},
},
input: schema.GroupKind{Kind: "Foo"},
result: mapping1,
},
{
name: "return single priority",
mapper: PriorityRESTMapper{
Delegate: allMappers,
KindPriority: []schema.GroupVersionKind{{Version: "v1", Kind: AnyKind}, {Version: "v1alpha1", Kind: AnyKind}},
},
input: schema.GroupKind{Kind: "Foo"},
result: mapping2,
},
{
name: "return out of group match",
mapper: PriorityRESTMapper{
Delegate: allMappers,
KindPriority: []schema.GroupVersionKind{{Group: AnyGroup, Version: "v1", Kind: AnyKind}, {Group: "other", Version: AnyVersion, Kind: AnyKind}},
},
input: schema.GroupKind{Kind: "Foo"},
result: mapping3,
},
}
for _, tc := range tcs {
actualResult, actualErr := tc.mapper.RESTMapping(tc.input)
if e, a := tc.result, actualResult; !reflect.DeepEqual(e, a) {
t.Errorf("%s: expected %v, got %v", tc.name, e, a)
}
switch {
case tc.err == nil && actualErr == nil:
case tc.err == nil:
t.Errorf("%s: unexpected error: %v", tc.name, actualErr)
case actualErr == nil:
t.Errorf("%s: expected error: %v got nil", tc.name, tc.err)
case tc.err.Error() != actualErr.Error():
t.Errorf("%s: expected %v, got %v", tc.name, tc.err, actualErr)
}
}
}
func TestPriorityRESTMapperRESTMappingHonorsUserVersion(t *testing.T) {
mappingV2alpha1 := &RESTMapping{
GroupVersionKind: schema.GroupVersionKind{Group: "Bar", Kind: "Foo", Version: "v2alpha1"},
}
mappingV1 := &RESTMapping{
GroupVersionKind: schema.GroupVersionKind{Group: "Bar", Kind: "Foo", Version: "v1"},
}
allMappers := MultiRESTMapper{
fixedRESTMapper{mappings: []*RESTMapping{mappingV2alpha1}},
fixedRESTMapper{mappings: []*RESTMapping{mappingV1}},
}
mapper := PriorityRESTMapper{
Delegate: allMappers,
KindPriority: []schema.GroupVersionKind{{Group: "Bar", Version: "v2alpha1", Kind: AnyKind}, {Group: "Bar", Version: AnyVersion, Kind: AnyKind}},
}
outMapping1, err := mapper.RESTMapping(schema.GroupKind{Group: "Bar", Kind: "Foo"}, "v1")
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if outMapping1 != mappingV1 {
t.Errorf("asked for version %v, expected mapping for %v, got mapping for %v", "v1", mappingV1.GroupVersionKind, outMapping1.GroupVersionKind)
}
outMapping2, err := mapper.RESTMapping(schema.GroupKind{Group: "Bar", Kind: "Foo"}, "v2alpha1")
if err != nil {
t.Errorf("unexpected error: %v", err)
}
if outMapping2 != mappingV2alpha1 {
t.Errorf("asked for version %v, expected mapping for %v, got mapping for %v", "v2alpha1", mappingV2alpha1.GroupVersionKind, outMapping2.GroupVersionKind)
}
}

View file

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

View file

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

View file

@ -1,111 +0,0 @@
/*
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

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

View file

@ -1,59 +0,0 @@
/*
Copyright 2014 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package resource_test
import (
"fmt"
"k8s.io/apimachinery/pkg/api/resource"
)
func ExampleFormat() {
memorySize := resource.NewQuantity(5*1024*1024*1024, resource.BinarySI)
fmt.Printf("memorySize = %v\n", memorySize)
diskSize := resource.NewQuantity(5*1000*1000*1000, resource.DecimalSI)
fmt.Printf("diskSize = %v\n", diskSize)
cores := resource.NewMilliQuantity(5300, resource.DecimalSI)
fmt.Printf("cores = %v\n", cores)
// Output:
// memorySize = 5Gi
// diskSize = 5G
// cores = 5300m
}
func ExampleMustParse() {
memorySize := resource.MustParse("5Gi")
fmt.Printf("memorySize = %v (%v)\n", memorySize.Value(), memorySize.Format)
diskSize := resource.MustParse("5G")
fmt.Printf("diskSize = %v (%v)\n", diskSize.Value(), diskSize.Format)
cores := resource.MustParse("5300m")
fmt.Printf("milliCores = %v (%v)\n", cores.MilliValue(), cores.Format)
cores2 := resource.MustParse("5.4")
fmt.Printf("milliCores = %v (%v)\n", cores2.MilliValue(), cores2.Format)
// Output:
// memorySize = 5368709120 (BinarySI)
// diskSize = 5000000000 (DecimalSI)
// milliCores = 5300 (DecimalSI)
// milliCores = 5400 (DecimalSI)
}

File diff suppressed because it is too large Load diff

View file

@ -1,85 +0,0 @@
/*
Copyright 2015 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package 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)
}
}

View file

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

View file

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

View file

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

View file

@ -1,85 +0,0 @@
/*
Copyright 2014 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package validation
import (
"strings"
"k8s.io/apimachinery/pkg/util/validation"
"k8s.io/apimachinery/pkg/util/validation/field"
)
const IsNegativeErrorMsg string = `must be greater than or equal to 0`
// ValidateNameFunc validates that the provided name is valid for a given resource type.
// Not all resources have the same validation rules for names. Prefix is true
// if the name will have a value appended to it. If the name is not valid,
// this returns a list of descriptions of individual characteristics of the
// value that were not valid. Otherwise this returns an empty list or nil.
type ValidateNameFunc func(name string, prefix bool) []string
// NameIsDNSSubdomain is a ValidateNameFunc for names that must be a DNS subdomain.
func NameIsDNSSubdomain(name string, prefix bool) []string {
if prefix {
name = maskTrailingDash(name)
}
return validation.IsDNS1123Subdomain(name)
}
// NameIsDNSLabel is a ValidateNameFunc for names that must be a DNS 1123 label.
func NameIsDNSLabel(name string, prefix bool) []string {
if prefix {
name = maskTrailingDash(name)
}
return validation.IsDNS1123Label(name)
}
// NameIsDNS1035Label is a ValidateNameFunc for names that must be a DNS 952 label.
func NameIsDNS1035Label(name string, prefix bool) []string {
if prefix {
name = maskTrailingDash(name)
}
return validation.IsDNS1035Label(name)
}
// ValidateNamespaceName can be used to check whether the given namespace name is valid.
// Prefix indicates this name will be used as part of generation, in which case
// trailing dashes are allowed.
var ValidateNamespaceName = NameIsDNSLabel
// ValidateServiceAccountName can be used to check whether the given service account name is valid.
// Prefix indicates this name will be used as part of generation, in which case
// trailing dashes are allowed.
var ValidateServiceAccountName = NameIsDNSSubdomain
// maskTrailingDash replaces the final character of a string with a subdomain safe
// value if is a dash.
func maskTrailingDash(name string) string {
if strings.HasSuffix(name, "-") {
return name[:len(name)-2] + "a"
}
return name
}
// Validates that given value is not negative.
func ValidateNonnegativeField(value int64, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if value < 0 {
allErrs = append(allErrs, field.Invalid(fldPath, value, IsNegativeErrorMsg))
}
return allErrs
}

View file

@ -1,68 +0,0 @@
/*
Copyright 2015 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package path
import (
"fmt"
"strings"
)
// NameMayNotBe specifies strings that cannot be used as names specified as path segments (like the REST API or etcd store)
var NameMayNotBe = []string{".", ".."}
// NameMayNotContain specifies substrings that cannot be used in names specified as path segments (like the REST API or etcd store)
var NameMayNotContain = []string{"/", "%"}
// IsValidPathSegmentName validates the name can be safely encoded as a path segment
func IsValidPathSegmentName(name string) []string {
for _, illegalName := range NameMayNotBe {
if name == illegalName {
return []string{fmt.Sprintf(`may not be '%s'`, illegalName)}
}
}
var errors []string
for _, illegalContent := range NameMayNotContain {
if strings.Contains(name, illegalContent) {
errors = append(errors, fmt.Sprintf(`may not contain '%s'`, illegalContent))
}
}
return errors
}
// IsValidPathSegmentPrefix validates the name can be used as a prefix for a name which will be encoded as a path segment
// It does not check for exact matches with disallowed names, since an arbitrary suffix might make the name valid
func IsValidPathSegmentPrefix(name string) []string {
var errors []string
for _, illegalContent := range NameMayNotContain {
if strings.Contains(name, illegalContent) {
errors = append(errors, fmt.Sprintf(`may not contain '%s'`, illegalContent))
}
}
return errors
}
// ValidatePathSegmentName validates the name can be safely encoded as a path segment
func ValidatePathSegmentName(name string, prefix bool) []string {
if prefix {
return IsValidPathSegmentPrefix(name)
} else {
return IsValidPathSegmentName(name)
}
}

View file

@ -1,168 +0,0 @@
/*
Copyright 2015 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package path
import (
"strings"
"testing"
)
func TestValidatePathSegmentName(t *testing.T) {
testcases := map[string]struct {
Name string
Prefix bool
ExpectedMsg string
}{
"empty": {
Name: "",
Prefix: false,
ExpectedMsg: "",
},
"empty,prefix": {
Name: "",
Prefix: true,
ExpectedMsg: "",
},
"valid": {
Name: "foo.bar.baz",
Prefix: false,
ExpectedMsg: "",
},
"valid,prefix": {
Name: "foo.bar.baz",
Prefix: true,
ExpectedMsg: "",
},
// Make sure mixed case, non DNS subdomain characters are tolerated
"valid complex": {
Name: "sha256:ABCDEF012345@ABCDEF012345",
Prefix: false,
ExpectedMsg: "",
},
// Make sure non-ascii characters are tolerated
"valid extended charset": {
Name: "Iñtërnâtiônàlizætiøn",
Prefix: false,
ExpectedMsg: "",
},
"dot": {
Name: ".",
Prefix: false,
ExpectedMsg: ".",
},
"dot leading": {
Name: ".test",
Prefix: false,
ExpectedMsg: "",
},
"dot,prefix": {
Name: ".",
Prefix: true,
ExpectedMsg: "",
},
"dot dot": {
Name: "..",
Prefix: false,
ExpectedMsg: "..",
},
"dot dot leading": {
Name: "..test",
Prefix: false,
ExpectedMsg: "",
},
"dot dot,prefix": {
Name: "..",
Prefix: true,
ExpectedMsg: "",
},
"slash": {
Name: "foo/bar",
Prefix: false,
ExpectedMsg: "/",
},
"slash,prefix": {
Name: "foo/bar",
Prefix: true,
ExpectedMsg: "/",
},
"percent": {
Name: "foo%bar",
Prefix: false,
ExpectedMsg: "%",
},
"percent,prefix": {
Name: "foo%bar",
Prefix: true,
ExpectedMsg: "%",
},
}
for k, tc := range testcases {
msgs := ValidatePathSegmentName(tc.Name, tc.Prefix)
if len(tc.ExpectedMsg) == 0 && len(msgs) > 0 {
t.Errorf("%s: expected no message, got %v", k, msgs)
}
if len(tc.ExpectedMsg) > 0 && len(msgs) == 0 {
t.Errorf("%s: expected error message, got none", k)
}
if len(tc.ExpectedMsg) > 0 && !strings.Contains(msgs[0], tc.ExpectedMsg) {
t.Errorf("%s: expected message containing %q, got %v", k, tc.ExpectedMsg, msgs[0])
}
}
}
func TestValidateWithMultiErrors(t *testing.T) {
testcases := map[string]struct {
Name string
Prefix bool
ExpectedMsg []string
}{
"slash,percent": {
Name: "foo//bar%",
Prefix: false,
ExpectedMsg: []string{"may not contain '/'", "may not contain '%'"},
},
"slash,percent,prefix": {
Name: "foo//bar%",
Prefix: true,
ExpectedMsg: []string{"may not contain '/'", "may not contain '%'"},
},
}
for k, tc := range testcases {
msgs := ValidatePathSegmentName(tc.Name, tc.Prefix)
if len(tc.ExpectedMsg) == 0 && len(msgs) > 0 {
t.Errorf("%s: expected no message, got %v", k, msgs)
}
if len(tc.ExpectedMsg) > 0 && len(msgs) == 0 {
t.Errorf("%s: expected error message, got none", k)
}
if len(tc.ExpectedMsg) > 0 {
for i := 0; i < len(tc.ExpectedMsg); i++ {
if msgs[i] != tc.ExpectedMsg[i] {
t.Errorf("%s: expected message containing %q, got %v", k, tc.ExpectedMsg[i], msgs[i])
}
}
}
}
}

View file

@ -1,66 +0,0 @@
/*
Copyright 2016 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package announced
import (
"reflect"
"testing"
"k8s.io/apimachinery/pkg/util/sets"
)
func TestFactoryRegistry(t *testing.T) {
regA := make(APIGroupFactoryRegistry)
regB := make(APIGroupFactoryRegistry)
if err := regA.AnnounceGroup(&GroupMetaFactoryArgs{
GroupName: "foo",
VersionPreferenceOrder: []string{"v2", "v1"},
ImportPrefix: "pkg/apis/foo",
RootScopedKinds: sets.NewString("namespaces"),
}); err != nil {
t.Fatalf("Unexpected error: %v", err)
}
if err := regA.AnnounceGroupVersion(&GroupVersionFactoryArgs{
GroupName: "foo",
VersionName: "v1",
}); err != nil {
t.Fatalf("Unexpected error: %v", err)
}
if err := regA.AnnounceGroupVersion(&GroupVersionFactoryArgs{
GroupName: "foo",
VersionName: "v2",
}); err != nil {
t.Fatalf("Unexpected error: %v", err)
}
if err := regB.AnnouncePreconstructedFactory(NewGroupMetaFactory(
&GroupMetaFactoryArgs{
GroupName: "foo",
VersionPreferenceOrder: []string{"v2", "v1"},
ImportPrefix: "pkg/apis/foo",
RootScopedKinds: sets.NewString("namespaces"),
},
VersionToSchemeFunc{"v1": nil, "v2": nil},
)); err != nil {
t.Fatalf("Unexpected error: %v", err)
}
if !reflect.DeepEqual(regA, regB) {
t.Errorf("Expected both ways of registering to be equivalent, but they were not.\n\n%#v\n\n%#v\n", regA, regB)
}
}

View file

@ -42,10 +42,16 @@ type GroupVersionFactoryArgs struct {
// GroupMetaFactoryArgs contains the group-level args of a GroupMetaFactory.
type GroupMetaFactoryArgs struct {
// GroupName is the name of the API-Group
//
// example: 'servicecatalog.k8s.io'
GroupName string
VersionPreferenceOrder []string
ImportPrefix string
// ImportPrefix is the base go package of the API-Group
//
// example: 'k8s.io/kubernetes/pkg/apis/autoscaling'
ImportPrefix string
// RootScopedKinds are resources that are not namespaced.
RootScopedKinds sets.String // nil is allowed
IgnoredKinds sets.String // nil is allowed

View file

@ -1,148 +0,0 @@
/*
Copyright 2015 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package registered
import (
"testing"
"k8s.io/apimachinery/pkg/apimachinery"
"k8s.io/apimachinery/pkg/runtime/schema"
)
func TestAddThirdPartyVersionsBasic(t *testing.T) {
m, err := NewAPIRegistrationManager("")
if err != nil {
t.Fatalf("Unexpected failure to make a manager: %v", err)
}
registered := []schema.GroupVersion{
{
Group: "",
Version: "v1",
},
}
skipped := registered
thirdParty := []schema.GroupVersion{
{
Group: "company.com",
Version: "v1",
},
{
Group: "company.com",
Version: "v2",
},
}
gvs := append(registered, thirdParty...)
m.RegisterVersions(registered)
wasSkipped := m.AddThirdPartyAPIGroupVersions(gvs...)
if len(wasSkipped) != len(skipped) {
t.Errorf("Expected %v, found %v", skipped, wasSkipped)
}
for ix := range wasSkipped {
found := false
for _, gv := range skipped {
if gv.String() == wasSkipped[ix].String() {
found = true
break
}
}
if !found {
t.Errorf("Couldn't find %v in %v", wasSkipped[ix], skipped)
}
}
for _, gv := range thirdParty {
if !m.IsThirdPartyAPIGroupVersion(gv) {
t.Errorf("Expected %v to be third party.", gv)
}
}
}
func TestAddThirdPartyVersionsMultiple(t *testing.T) {
thirdParty := []schema.GroupVersion{
{
Group: "company.com",
Version: "v1",
},
{
Group: "company.com",
Version: "v2",
},
}
m, err := NewAPIRegistrationManager("")
if err != nil {
t.Fatalf("Unexpected failure to make a manager: %v", err)
}
for _, gv := range thirdParty {
wasSkipped := m.AddThirdPartyAPIGroupVersions(gv)
if len(wasSkipped) != 0 {
t.Errorf("Expected length 0, found %v", wasSkipped)
}
}
for _, gv := range thirdParty {
if !m.IsThirdPartyAPIGroupVersion(gv) {
t.Errorf("Expected %v to be third party.", gv)
}
}
}
func TestAllPreferredGroupVersions(t *testing.T) {
testCases := []struct {
groupMetas []apimachinery.GroupMeta
expect string
}{
{
groupMetas: []apimachinery.GroupMeta{
{
GroupVersion: schema.GroupVersion{Group: "group1", Version: "v1"},
},
{
GroupVersion: schema.GroupVersion{Group: "group2", Version: "v2"},
},
{
GroupVersion: schema.GroupVersion{Group: "", Version: "v1"},
},
},
expect: "group1/v1,group2/v2,v1",
},
{
groupMetas: []apimachinery.GroupMeta{
{
GroupVersion: schema.GroupVersion{Group: "", Version: "v1"},
},
},
expect: "v1",
},
{
groupMetas: []apimachinery.GroupMeta{},
expect: "",
},
}
for _, testCase := range testCases {
m, err := NewAPIRegistrationManager("")
if err != nil {
t.Fatalf("Unexpected failure to make a manager: %v", err)
}
for _, groupMeta := range testCase.groupMetas {
m.RegisterGroup(groupMeta)
}
output := m.AllPreferredGroupVersions()
if testCase.expect != output {
t.Errorf("Error. expect: %s, got: %s", testCase.expect, output)
}
}
}

View file

@ -1,43 +0,0 @@
/*
Copyright 2016 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package apimachinery
import (
"testing"
"k8s.io/apimachinery/pkg/runtime/schema"
)
func TestAdd(t *testing.T) {
gm := GroupMeta{
GroupVersion: schema.GroupVersion{
Group: "test",
Version: "v1",
},
GroupVersions: []schema.GroupVersion{{Group: "test", Version: "v1"}},
}
gm.AddVersionInterfaces(schema.GroupVersion{Group: "test", Version: "v1"}, nil)
if e, a := 1, len(gm.InterfacesByVersion); e != a {
t.Errorf("expected %v, got %v", e, a)
}
// GroupVersions is unchanged
if e, a := 1, len(gm.GroupVersions); e != a {
t.Errorf("expected %v, got %v", e, a)
}
}

View file

@ -1,106 +0,0 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package internalversion
import (
"fmt"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/conversion"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/util/validation/field"
)
func Convert_internalversion_ListOptions_To_v1_ListOptions(in *ListOptions, out *metav1.ListOptions, s conversion.Scope) error {
if err := Convert_fields_Selector_To_string(&in.FieldSelector, &out.FieldSelector, s); err != nil {
return err
}
if err := Convert_labels_Selector_To_string(&in.LabelSelector, &out.LabelSelector, s); err != nil {
return err
}
out.ResourceVersion = in.ResourceVersion
out.TimeoutSeconds = in.TimeoutSeconds
out.Watch = in.Watch
return nil
}
func Convert_v1_ListOptions_To_internalversion_ListOptions(in *metav1.ListOptions, out *ListOptions, s conversion.Scope) error {
if err := Convert_string_To_fields_Selector(&in.FieldSelector, &out.FieldSelector, s); err != nil {
return err
}
if err := Convert_string_To_labels_Selector(&in.LabelSelector, &out.LabelSelector, s); err != nil {
return err
}
out.ResourceVersion = in.ResourceVersion
out.TimeoutSeconds = in.TimeoutSeconds
out.Watch = in.Watch
return nil
}
func Convert_string_To_labels_Selector(in *string, out *labels.Selector, s conversion.Scope) error {
selector, err := labels.Parse(*in)
if err != nil {
return err
}
*out = selector
return nil
}
func Convert_string_To_fields_Selector(in *string, out *fields.Selector, s conversion.Scope) error {
selector, err := fields.ParseSelector(*in)
if err != nil {
return err
}
*out = selector
return nil
}
func Convert_labels_Selector_To_string(in *labels.Selector, out *string, s conversion.Scope) error {
if *in == nil {
return nil
}
*out = (*in).String()
return nil
}
func Convert_fields_Selector_To_string(in *fields.Selector, out *string, s conversion.Scope) error {
if *in == nil {
return nil
}
*out = (*in).String()
return nil
}
func Convert_map_to_v1_LabelSelector(in *map[string]string, out *metav1.LabelSelector, s conversion.Scope) error {
if in == nil {
return nil
}
out = new(metav1.LabelSelector)
for labelKey, labelValue := range *in {
metav1.AddLabelToSelector(out, labelKey, labelValue)
}
return nil
}
func Convert_v1_LabelSelector_to_map(in *metav1.LabelSelector, out *map[string]string, s conversion.Scope) error {
var err error
*out, err = metav1.LabelSelectorAsMap(in)
if err != nil {
err = field.Invalid(field.NewPath("labelSelector"), *in, fmt.Sprintf("cannot convert to old selector: %v", err))
}
return err
}

View file

@ -1,84 +0,0 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package internalversion
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer"
)
// GroupName is the group name for this API.
const GroupName = "meta.k8s.io"
// Scheme is the registry for any type that adheres to the meta API spec.
var scheme = runtime.NewScheme()
// Codecs provides access to encoding and decoding for the scheme.
var Codecs = serializer.NewCodecFactory(scheme)
// SchemeGroupVersion is group version used to register these objects
var SchemeGroupVersion = schema.GroupVersion{Group: GroupName, Version: runtime.APIVersionInternal}
// ParameterCodec handles versioning of objects that are converted to query parameters.
var ParameterCodec = runtime.NewParameterCodec(scheme)
// Kind takes an unqualified kind and returns a Group qualified GroupKind
func Kind(kind string) schema.GroupKind {
return SchemeGroupVersion.WithKind(kind).GroupKind()
}
// addToGroupVersion registers common meta types into schemas.
func addToGroupVersion(scheme *runtime.Scheme, groupVersion schema.GroupVersion) error {
if err := scheme.AddIgnoredConversionType(&metav1.TypeMeta{}, &metav1.TypeMeta{}); err != nil {
return err
}
scheme.AddConversionFuncs(
Convert_string_To_labels_Selector,
Convert_labels_Selector_To_string,
Convert_string_To_fields_Selector,
Convert_fields_Selector_To_string,
Convert_map_to_v1_LabelSelector,
Convert_v1_LabelSelector_to_map,
Convert_internalversion_ListOptions_To_v1_ListOptions,
Convert_v1_ListOptions_To_internalversion_ListOptions,
)
// ListOptions is the only options struct which needs conversion (it exposes labels and fields
// as selectors for convenience). The other types have only a single representation today.
scheme.AddKnownTypes(SchemeGroupVersion,
&ListOptions{},
&metav1.GetOptions{},
&metav1.ExportOptions{},
&metav1.DeleteOptions{},
)
// Allow delete options to be decoded across all version in this scheme (we may want to be more clever than this)
scheme.AddUnversionedTypes(SchemeGroupVersion, &metav1.DeleteOptions{})
metav1.AddToGroupVersion(scheme, metav1.SchemeGroupVersion)
return nil
}
// Unlike other API groups, meta internal knows about all meta external versions, but keeps
// the logic for conversion private.
func init() {
if err := addToGroupVersion(scheme, SchemeGroupVersion); err != nil {
panic(err)
}
}

View file

@ -1,89 +0,0 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package internalversion
import (
"net/url"
"reflect"
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/diff"
)
func TestListOptions(t *testing.T) {
// verify round trip conversion
ten := int64(10)
in := &metav1.ListOptions{
LabelSelector: "a=1",
FieldSelector: "b=1",
ResourceVersion: "10",
TimeoutSeconds: &ten,
Watch: true,
}
out := &ListOptions{}
if err := scheme.Convert(in, out, nil); err != nil {
t.Fatal(err)
}
actual := &metav1.ListOptions{}
if err := scheme.Convert(out, actual, nil); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(in, actual) {
t.Errorf("unexpected: %s", diff.ObjectReflectDiff(in, actual))
}
// verify failing conversion
for i, failingObject := range []*metav1.ListOptions{
&metav1.ListOptions{LabelSelector: "a!!!"},
&metav1.ListOptions{FieldSelector: "a!!!"},
} {
out = &ListOptions{}
if err := scheme.Convert(failingObject, out, nil); err == nil {
t.Errorf("%d: unexpected conversion: %#v", i, out)
}
}
// verify kind registration
if gvk, unversioned, err := scheme.ObjectKind(in); err != nil || unversioned || gvk != metav1.SchemeGroupVersion.WithKind("ListOptions") {
t.Errorf("unexpected: %v %v %v", gvk, unversioned, err)
}
if gvk, unversioned, err := scheme.ObjectKind(out); err != nil || unversioned || gvk != SchemeGroupVersion.WithKind("ListOptions") {
t.Errorf("unexpected: %v %v %v", gvk, unversioned, err)
}
actual = &metav1.ListOptions{}
if err := ParameterCodec.DecodeParameters(url.Values{"watch": []string{"1"}}, metav1.SchemeGroupVersion, actual); err != nil {
t.Fatal(err)
}
if !actual.Watch {
t.Errorf("unexpected watch decode: %#v", actual)
}
// check ParameterCodec
query, err := ParameterCodec.EncodeParameters(in, metav1.SchemeGroupVersion)
if err != nil {
t.Fatal(err)
}
actual = &metav1.ListOptions{}
if err := ParameterCodec.DecodeParameters(query, metav1.SchemeGroupVersion, actual); err != nil {
t.Fatal(err)
}
if !reflect.DeepEqual(in, actual) {
t.Errorf("unexpected: %s", diff.ObjectReflectDiff(in, actual))
}
}

View file

@ -1,45 +0,0 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package internalversion
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
)
// ListOptions is the query options to a standard REST list call, and has future support for
// watch calls.
type ListOptions struct {
metav1.TypeMeta
// A selector based on labels
LabelSelector labels.Selector
// A selector based on fields
FieldSelector fields.Selector
// If true, watch for changes to this list
Watch bool
// When specified with a watch call, shows changes that occur after that particular version of a resource.
// Defaults to changes from the beginning of history.
// When specified for list:
// - if unset, then the result is returned from remote storage based on quorum-read flag;
// - if it's 0, then we simply return what we currently have in cache, no guarantee;
// - if set to non zero, then the result is at least as fresh as given rv.
ResourceVersion string
// Timeout for the list/watch call.
TimeoutSeconds *int64
}

View file

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

View file

@ -14,24 +14,22 @@ See the License for the specific language governing permissions and
limitations under the License.
*/
package api
package v1
import (
"fmt"
"strconv"
"strings"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/api/resource"
"k8s.io/apimachinery/pkg/conversion"
"k8s.io/apimachinery/pkg/fields"
"k8s.io/apimachinery/pkg/labels"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/client-go/pkg/api/resource"
"k8s.io/client-go/pkg/util/intstr"
utillabels "k8s.io/client-go/pkg/util/labels"
"k8s.io/apimachinery/pkg/util/intstr"
)
func addConversionFuncs(scheme *runtime.Scheme) error {
func AddConversionFuncs(scheme *runtime.Scheme) error {
return scheme.AddConversionFuncs(
Convert_v1_TypeMeta_To_v1_TypeMeta,
@ -157,7 +155,7 @@ func Convert_bool_To_Pointer_bool(in *bool, out **bool, s conversion.Scope) erro
}
// +k8s:conversion-fn=drop
func Convert_v1_TypeMeta_To_v1_TypeMeta(in, out *metav1.TypeMeta, s conversion.Scope) error {
func Convert_v1_TypeMeta_To_v1_TypeMeta(in, out *TypeMeta, s conversion.Scope) error {
// These values are explicitly not copied
//out.APIVersion = in.APIVersion
//out.Kind = in.Kind
@ -165,7 +163,7 @@ func Convert_v1_TypeMeta_To_v1_TypeMeta(in, out *metav1.TypeMeta, s conversion.S
}
// +k8s:conversion-fn=copy-only
func Convert_unversioned_ListMeta_To_unversioned_ListMeta(in, out *metav1.ListMeta, s conversion.Scope) error {
func Convert_unversioned_ListMeta_To_unversioned_ListMeta(in, out *ListMeta, s conversion.Scope) error {
*out = *in
return nil
}
@ -177,14 +175,14 @@ func Convert_intstr_IntOrString_To_intstr_IntOrString(in, out *intstr.IntOrStrin
}
// +k8s:conversion-fn=copy-only
func Convert_unversioned_Time_To_unversioned_Time(in *metav1.Time, out *metav1.Time, s conversion.Scope) error {
func Convert_unversioned_Time_To_unversioned_Time(in *Time, out *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 {
func Convert_Slice_string_To_unversioned_Time(input *[]string, out *Time, s conversion.Scope) error {
str := ""
if len(*input) > 0 {
str = (*input)[0]
@ -232,20 +230,20 @@ func Convert_resource_Quantity_To_resource_Quantity(in *resource.Quantity, out *
return nil
}
func Convert_map_to_unversioned_LabelSelector(in *map[string]string, out *metav1.LabelSelector, s conversion.Scope) error {
func Convert_map_to_unversioned_LabelSelector(in *map[string]string, out *LabelSelector, s conversion.Scope) error {
if in == nil {
return nil
}
out = new(metav1.LabelSelector)
out = new(LabelSelector)
for labelKey, labelValue := range *in {
utillabels.AddLabelToSelector(out, labelKey, labelValue)
AddLabelToSelector(out, labelKey, labelValue)
}
return nil
}
func Convert_unversioned_LabelSelector_to_map(in *metav1.LabelSelector, out *map[string]string, s conversion.Scope) error {
func Convert_unversioned_LabelSelector_to_map(in *LabelSelector, out *map[string]string, s conversion.Scope) error {
var err error
*out, err = metav1.LabelSelectorAsMap(in)
*out, err = LabelSelectorAsMap(in)
return err
}

View file

@ -1,153 +0,0 @@
/*
Copyright 2014 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1
import (
"encoding/json"
"testing"
"time"
"github.com/ghodss/yaml"
)
type DurationHolder struct {
D Duration `json:"d"`
}
func TestDurationMarshalYAML(t *testing.T) {
cases := []struct {
input Duration
result string
}{
{Duration{5 * time.Second}, "d: 5s\n"},
{Duration{2 * time.Minute}, "d: 2m0s\n"},
{Duration{time.Hour + 3*time.Millisecond}, "d: 1h0m0.003s\n"},
}
for _, c := range cases {
input := DurationHolder{c.input}
result, err := yaml.Marshal(&input)
if err != nil {
t.Errorf("Failed to marshal input: %q: %v", input, err)
}
if string(result) != c.result {
t.Errorf("Failed to marshal input: %q: expected %q, got %q", input, c.result, string(result))
}
}
}
func TestDurationUnmarshalYAML(t *testing.T) {
cases := []struct {
input string
result Duration
}{
{"d: 0s\n", Duration{}},
{"d: 5s\n", Duration{5 * time.Second}},
{"d: 2m0s\n", Duration{2 * time.Minute}},
{"d: 1h0m0.003s\n", Duration{time.Hour + 3*time.Millisecond}},
// Units with zero values can optionally be dropped
{"d: 2m\n", Duration{2 * time.Minute}},
{"d: 1h0.003s\n", Duration{time.Hour + 3*time.Millisecond}},
}
for _, c := range cases {
var result DurationHolder
if err := yaml.Unmarshal([]byte(c.input), &result); err != nil {
t.Errorf("Failed to unmarshal input %q: %v", c.input, err)
}
if result.D != c.result {
t.Errorf("Failed to unmarshal input %q: expected %q, got %q", c.input, c.result, result)
}
}
}
func TestDurationMarshalJSON(t *testing.T) {
cases := []struct {
input Duration
result string
}{
{Duration{5 * time.Second}, `{"d":"5s"}`},
{Duration{2 * time.Minute}, `{"d":"2m0s"}`},
{Duration{time.Hour + 3*time.Millisecond}, `{"d":"1h0m0.003s"}`},
}
for _, c := range cases {
input := DurationHolder{c.input}
result, err := json.Marshal(&input)
if err != nil {
t.Errorf("Failed to marshal input: %q: %v", input, err)
}
if string(result) != c.result {
t.Errorf("Failed to marshal input: %q: expected %q, got %q", input, c.result, string(result))
}
}
}
func TestDurationUnmarshalJSON(t *testing.T) {
cases := []struct {
input string
result Duration
}{
{`{"d":"0s"}`, Duration{}},
{`{"d":"5s"}`, Duration{5 * time.Second}},
{`{"d":"2m0s"}`, Duration{2 * time.Minute}},
{`{"d":"1h0m0.003s"}`, Duration{time.Hour + 3*time.Millisecond}},
// Units with zero values can optionally be dropped
{`{"d":"2m"}`, Duration{2 * time.Minute}},
{`{"d":"1h0.003s"}`, Duration{time.Hour + 3*time.Millisecond}},
}
for _, c := range cases {
var result DurationHolder
if err := json.Unmarshal([]byte(c.input), &result); err != nil {
t.Errorf("Failed to unmarshal input %q: %v", c.input, err)
}
if result.D != c.result {
t.Errorf("Failed to unmarshal input %q: expected %q, got %q", c.input, c.result, result)
}
}
}
func TestDurationMarshalJSONUnmarshalYAML(t *testing.T) {
cases := []struct {
input Duration
}{
{Duration{}},
{Duration{5 * time.Second}},
{Duration{2 * time.Minute}},
{Duration{time.Hour + 3*time.Millisecond}},
}
for i, c := range cases {
input := DurationHolder{c.input}
jsonMarshalled, err := json.Marshal(&input)
if err != nil {
t.Errorf("%d-1: Failed to marshal input: '%v': %v", i, input, err)
}
var result DurationHolder
if err := yaml.Unmarshal(jsonMarshalled, &result); err != nil {
t.Errorf("%d-2: Failed to unmarshal '%+v': %v", i, string(jsonMarshalled), err)
}
if input.D != result.D {
t.Errorf("%d-4: Failed to marshal input '%#v': got %#v", i, input, result)
}
}
}

View file

@ -512,6 +512,12 @@ func (m *DeleteOptions) MarshalTo(data []byte) (int, error) {
}
i++
}
if m.PropagationPolicy != nil {
data[i] = 0x22
i++
i = encodeVarintGenerated(data, i, uint64(len(*m.PropagationPolicy)))
i += copy(data[i:], *m.PropagationPolicy)
}
return i, nil
}
@ -1087,6 +1093,16 @@ func (m *OwnerReference) MarshalTo(data []byte) (int, error) {
}
i++
}
if m.BlockOwnerDeletion != nil {
data[i] = 0x38
i++
if *m.BlockOwnerDeletion {
data[i] = 1
} else {
data[i] = 0
}
i++
}
return i, nil
}
@ -1539,6 +1555,10 @@ func (m *DeleteOptions) Size() (n int) {
if m.OrphanDependents != nil {
n += 2
}
if m.PropagationPolicy != nil {
l = len(*m.PropagationPolicy)
n += 1 + l + sovGenerated(uint64(l))
}
return n
}
@ -1763,6 +1783,9 @@ func (m *OwnerReference) Size() (n int) {
if m.Controller != nil {
n += 2
}
if m.BlockOwnerDeletion != nil {
n += 2
}
return n
}
@ -1957,6 +1980,7 @@ func (this *DeleteOptions) String() string {
`GracePeriodSeconds:` + valueToStringGenerated(this.GracePeriodSeconds) + `,`,
`Preconditions:` + strings.Replace(fmt.Sprintf("%v", this.Preconditions), "Preconditions", "Preconditions", 1) + `,`,
`OrphanDependents:` + valueToStringGenerated(this.OrphanDependents) + `,`,
`PropagationPolicy:` + valueToStringGenerated(this.PropagationPolicy) + `,`,
`}`,
}, "")
return s
@ -2115,6 +2139,7 @@ func (this *OwnerReference) String() string {
`UID:` + fmt.Sprintf("%v", this.UID) + `,`,
`APIVersion:` + fmt.Sprintf("%v", this.APIVersion) + `,`,
`Controller:` + valueToStringGenerated(this.Controller) + `,`,
`BlockOwnerDeletion:` + valueToStringGenerated(this.BlockOwnerDeletion) + `,`,
`}`,
}, "")
return s
@ -2997,6 +3022,36 @@ func (m *DeleteOptions) Unmarshal(data []byte) error {
}
b := bool(v != 0)
m.OrphanDependents = &b
case 4:
if wireType != 2 {
return fmt.Errorf("proto: wrong wireType = %d for field PropagationPolicy", 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 := DeletionPropagation(data[iNdEx:postIndex])
m.PropagationPolicy = &s
iNdEx = postIndex
default:
iNdEx = preIndex
skippy, err := skipGenerated(data[iNdEx:])
@ -5379,6 +5434,27 @@ func (m *OwnerReference) Unmarshal(data []byte) error {
}
b := bool(v != 0)
m.Controller = &b
case 7:
if wireType != 0 {
return fmt.Errorf("proto: wrong wireType = %d for field BlockOwnerDeletion", wireType)
}
var v int
for shift := uint(0); ; shift += 7 {
if shift >= 64 {
return ErrIntOverflowGenerated
}
if iNdEx >= l {
return io.ErrUnexpectedEOF
}
b := data[iNdEx]
iNdEx++
v |= (int(b) & 0x7F) << shift
if b < 0x80 {
break
}
}
b := bool(v != 0)
m.BlockOwnerDeletion = &b
default:
iNdEx = preIndex
skippy, err := skipGenerated(data[iNdEx:])
@ -6700,137 +6776,140 @@ var (
)
var fileDescriptorGenerated = []byte{
// 2098 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xcc, 0x19, 0x4b, 0x6f, 0x23, 0x49,
0x39, 0x6d, 0xc7, 0x1e, 0xfb, 0x73, 0x9c, 0x47, 0x91, 0x01, 0x6f, 0x24, 0xec, 0x6c, 0xef, 0x0a,
0x65, 0x61, 0xd6, 0x26, 0x59, 0x58, 0x0d, 0x03, 0x0c, 0xc4, 0x71, 0x26, 0x8a, 0x76, 0x32, 0x89,
0x2a, 0x3b, 0x83, 0x58, 0x46, 0x88, 0x8e, 0xbb, 0xe2, 0x34, 0x69, 0x77, 0x37, 0x55, 0x65, 0x4f,
0xc2, 0x1e, 0x58, 0x69, 0x41, 0x70, 0x40, 0x68, 0x8e, 0x1c, 0x10, 0xda, 0x11, 0xdc, 0xb8, 0xf1,
0x27, 0x98, 0xe3, 0x4a, 0x5c, 0x38, 0x20, 0x8b, 0x09, 0x07, 0x8e, 0xdc, 0x23, 0x0e, 0xa8, 0xaa,
0xab, 0xfa, 0xe1, 0x8c, 0x37, 0x6d, 0x76, 0x0f, 0x9c, 0xe2, 0xfa, 0xde, 0xf5, 0xd5, 0xf7, 0xec,
0xc0, 0xde, 0xe9, 0x6d, 0xd6, 0x74, 0xfc, 0xd6, 0xe9, 0xe0, 0x88, 0x50, 0x8f, 0x70, 0xc2, 0x5a,
0x43, 0xe2, 0xd9, 0x3e, 0x6d, 0x29, 0x84, 0x15, 0x38, 0x7d, 0xab, 0x7b, 0xe2, 0x78, 0x84, 0x9e,
0xb7, 0x82, 0xd3, 0x9e, 0x00, 0xb0, 0x56, 0x9f, 0x70, 0xab, 0x35, 0x5c, 0x6f, 0xf5, 0x88, 0x47,
0xa8, 0xc5, 0x89, 0xdd, 0x0c, 0xa8, 0xcf, 0x7d, 0xf4, 0x7a, 0xc8, 0xd5, 0x4c, 0x72, 0x35, 0x83,
0xd3, 0x9e, 0x00, 0xb0, 0xa6, 0xe0, 0x6a, 0x0e, 0xd7, 0x57, 0xde, 0xec, 0x39, 0xfc, 0x64, 0x70,
0xd4, 0xec, 0xfa, 0xfd, 0x56, 0xcf, 0xef, 0xf9, 0x2d, 0xc9, 0x7c, 0x34, 0x38, 0x96, 0x27, 0x79,
0x90, 0xbf, 0x42, 0xa1, 0x2b, 0x13, 0x4d, 0xa1, 0x03, 0x8f, 0x3b, 0x7d, 0x32, 0x6e, 0xc5, 0xca,
0xdb, 0xd7, 0x31, 0xb0, 0xee, 0x09, 0xe9, 0x5b, 0x57, 0xf8, 0xde, 0x9a, 0xc4, 0x37, 0xe0, 0x8e,
0xdb, 0x72, 0x3c, 0xce, 0x38, 0x1d, 0x67, 0x32, 0xff, 0x92, 0x87, 0xd2, 0xe6, 0xc1, 0xee, 0x0e,
0xf5, 0x07, 0x01, 0x5a, 0x85, 0x59, 0xcf, 0xea, 0x93, 0x9a, 0xb1, 0x6a, 0xac, 0x95, 0xdb, 0x73,
0xcf, 0x47, 0x8d, 0x99, 0x8b, 0x51, 0x63, 0xf6, 0x81, 0xd5, 0x27, 0x58, 0x62, 0x90, 0x0b, 0xa5,
0x21, 0xa1, 0xcc, 0xf1, 0x3d, 0x56, 0xcb, 0xad, 0xe6, 0xd7, 0x2a, 0x1b, 0x77, 0x9b, 0x59, 0x9c,
0xd6, 0x94, 0x0a, 0x1e, 0x85, 0xac, 0xf7, 0x7c, 0xda, 0x71, 0x58, 0xd7, 0x1f, 0x12, 0x7a, 0xde,
0x5e, 0x54, 0x5a, 0x4a, 0x0a, 0xc9, 0x70, 0xa4, 0x01, 0xfd, 0xdc, 0x80, 0xc5, 0x80, 0x92, 0x63,
0x42, 0x29, 0xb1, 0x15, 0xbe, 0x96, 0x5f, 0x35, 0x3e, 0x03, 0xb5, 0x35, 0xa5, 0x76, 0xf1, 0x60,
0x4c, 0x3e, 0xbe, 0xa2, 0x11, 0xfd, 0xc1, 0x80, 0x15, 0x46, 0xe8, 0x90, 0xd0, 0x4d, 0xdb, 0xa6,
0x84, 0xb1, 0xf6, 0xf9, 0x96, 0xeb, 0x10, 0x8f, 0x6f, 0xed, 0x76, 0x30, 0xab, 0xcd, 0x4a, 0x3f,
0x7c, 0x27, 0x9b, 0x41, 0x87, 0x93, 0xe4, 0xb4, 0x4d, 0x65, 0xd1, 0xca, 0x44, 0x12, 0x86, 0x3f,
0xc1, 0x0c, 0xf3, 0x18, 0xe6, 0xf4, 0x43, 0xde, 0x77, 0x18, 0x47, 0x8f, 0xa0, 0xd8, 0x13, 0x07,
0x56, 0x33, 0xa4, 0x81, 0xcd, 0x6c, 0x06, 0x6a, 0x19, 0xed, 0x79, 0x65, 0x4f, 0x51, 0x1e, 0x19,
0x56, 0xd2, 0xcc, 0x0f, 0x73, 0x50, 0xd9, 0x3c, 0xd8, 0xc5, 0x84, 0xf9, 0x03, 0xda, 0x25, 0x19,
0x82, 0x66, 0x03, 0x40, 0xfc, 0x65, 0x81, 0xd5, 0x25, 0x76, 0x2d, 0xb7, 0x6a, 0xac, 0x95, 0xda,
0x48, 0xd1, 0xc1, 0x83, 0x08, 0x83, 0x13, 0x54, 0x42, 0xea, 0xa9, 0xe3, 0xd9, 0xf2, 0xb5, 0x13,
0x52, 0xdf, 0x71, 0x3c, 0x1b, 0x4b, 0x0c, 0xba, 0x0f, 0x85, 0x21, 0xa1, 0x47, 0xc2, 0xff, 0x22,
0x20, 0xbe, 0x92, 0xed, 0x7a, 0x8f, 0x04, 0x4b, 0xbb, 0x7c, 0x31, 0x6a, 0x14, 0xe4, 0x4f, 0x1c,
0x0a, 0x41, 0x4d, 0x00, 0x76, 0xe2, 0x53, 0x2e, 0xcd, 0xa9, 0x15, 0x56, 0xf3, 0x6b, 0xe5, 0xf6,
0xbc, 0xb0, 0xef, 0x30, 0x82, 0xe2, 0x04, 0x85, 0xf9, 0x67, 0x03, 0x16, 0x12, 0x5e, 0x90, 0x1e,
0xbf, 0x0d, 0x73, 0xbd, 0x44, 0xbc, 0x29, 0x8f, 0x2c, 0x2b, 0xdb, 0xe7, 0x92, 0xb1, 0x88, 0x53,
0x94, 0x88, 0x40, 0x99, 0x2a, 0x49, 0x3a, 0xaf, 0xd6, 0x33, 0x3f, 0x97, 0xb6, 0x21, 0xd6, 0x94,
0x00, 0x32, 0x1c, 0x4b, 0x36, 0xff, 0x65, 0xc8, 0xa7, 0xd3, 0x99, 0x86, 0xd6, 0x12, 0xd9, 0x6c,
0xc8, 0x2b, 0xcf, 0x4d, 0xc8, 0xc4, 0x6b, 0x52, 0x20, 0xf7, 0x7f, 0x91, 0x02, 0x77, 0x4a, 0xbf,
0xfd, 0xa8, 0x31, 0xf3, 0xc1, 0xdf, 0x57, 0x67, 0xcc, 0x5f, 0xe6, 0xa0, 0xda, 0x21, 0x2e, 0xe1,
0x64, 0x3f, 0xe0, 0xf2, 0x06, 0xf7, 0x00, 0xf5, 0xa8, 0xd5, 0x25, 0x07, 0x84, 0x3a, 0xbe, 0x7d,
0x48, 0xba, 0xbe, 0x67, 0x33, 0xf9, 0x44, 0xf9, 0xf6, 0xe7, 0x2f, 0x46, 0x0d, 0xb4, 0x73, 0x05,
0x8b, 0x5f, 0xc2, 0x81, 0x5c, 0xa8, 0x06, 0x54, 0xfe, 0x76, 0xb8, 0x2a, 0x83, 0x22, 0xfc, 0xde,
0xca, 0x76, 0xf7, 0x83, 0x24, 0x6b, 0x7b, 0xe9, 0x62, 0xd4, 0xa8, 0xa6, 0x40, 0x38, 0x2d, 0x1c,
0x7d, 0x17, 0x16, 0x7d, 0x1a, 0x9c, 0x58, 0x5e, 0x87, 0x04, 0xc4, 0xb3, 0x89, 0xc7, 0x99, 0x4c,
0x89, 0x52, 0x7b, 0x59, 0x14, 0xaf, 0xfd, 0x31, 0x1c, 0xbe, 0x42, 0x6d, 0xee, 0x42, 0xa9, 0x33,
0xa0, 0x96, 0x10, 0x87, 0xbe, 0x0d, 0x25, 0x5b, 0xfd, 0x56, 0x37, 0x7f, 0x55, 0x57, 0x5f, 0x4d,
0x73, 0x39, 0x6a, 0x54, 0x45, 0x93, 0x69, 0x6a, 0x00, 0x8e, 0x58, 0xcc, 0xc7, 0x50, 0xdd, 0x3e,
0x0b, 0x7c, 0xca, 0xb5, 0x4f, 0xbf, 0x04, 0x45, 0x22, 0x01, 0x52, 0x5a, 0x29, 0x2e, 0x19, 0x21,
0x19, 0x56, 0x58, 0xf4, 0x1a, 0x14, 0xc8, 0x99, 0xd5, 0xe5, 0x2a, 0xf7, 0xab, 0x8a, 0xac, 0xb0,
0x2d, 0x80, 0x38, 0xc4, 0x99, 0xfb, 0x00, 0x3b, 0x24, 0x12, 0xbd, 0x09, 0x0b, 0x3a, 0x6e, 0xd3,
0xe9, 0xf4, 0x05, 0xc5, 0xbc, 0x80, 0xd3, 0x68, 0x3c, 0x4e, 0x6f, 0x3e, 0x86, 0xb2, 0x4c, 0x39,
0x51, 0x33, 0x84, 0x09, 0x32, 0xe3, 0x94, 0x94, 0xc8, 0x04, 0x49, 0x81, 0x43, 0x5c, 0x54, 0x74,
0x72, 0x93, 0x8a, 0x4e, 0x22, 0xc2, 0x5c, 0xa8, 0x86, 0xbc, 0xba, 0x0e, 0x66, 0xd2, 0x70, 0x0b,
0x4a, 0xda, 0x4c, 0xa5, 0x25, 0xea, 0x7f, 0x5a, 0x10, 0x8e, 0x28, 0x12, 0xda, 0x4e, 0x20, 0x55,
0x3e, 0xb2, 0x29, 0x7b, 0x03, 0x6e, 0xa8, 0x04, 0x56, 0xba, 0x16, 0x14, 0xd9, 0x0d, 0xed, 0x33,
0x8d, 0x4f, 0x68, 0xfa, 0x19, 0xd4, 0x26, 0x35, 0xcd, 0x4f, 0x51, 0xe0, 0xb2, 0x9b, 0x62, 0xfe,
0xc6, 0x80, 0xc5, 0xa4, 0xa4, 0xec, 0xcf, 0x97, 0x5d, 0xc9, 0xf5, 0xed, 0x25, 0xe1, 0x91, 0xdf,
0x1b, 0xb0, 0x9c, 0xba, 0xda, 0x54, 0x2f, 0x3e, 0x85, 0x51, 0xc9, 0xe0, 0xc8, 0x4f, 0x11, 0x1c,
0x7f, 0xcd, 0x41, 0xf5, 0xbe, 0x75, 0x44, 0xdc, 0x43, 0xe2, 0x92, 0x2e, 0xf7, 0x29, 0x7a, 0x1f,
0x2a, 0x7d, 0x8b, 0x77, 0x4f, 0x24, 0x54, 0x0f, 0x00, 0x9d, 0x6c, 0x25, 0x2a, 0x25, 0xa9, 0xb9,
0x17, 0x8b, 0xd9, 0xf6, 0x38, 0x3d, 0x6f, 0x7f, 0x4e, 0x99, 0x54, 0x49, 0x60, 0x70, 0x52, 0x9b,
0x9c, 0xda, 0xe4, 0x79, 0xfb, 0x2c, 0x10, 0x35, 0x7a, 0xfa, 0x61, 0x31, 0x65, 0x02, 0x26, 0x3f,
0x19, 0x38, 0x94, 0xf4, 0x89, 0xc7, 0xe3, 0xa9, 0x6d, 0x6f, 0x4c, 0x3e, 0xbe, 0xa2, 0x71, 0xe5,
0x2e, 0x2c, 0x8e, 0x1b, 0x8f, 0x16, 0x21, 0x7f, 0x4a, 0xce, 0xc3, 0xf7, 0xc2, 0xe2, 0x27, 0x5a,
0x86, 0xc2, 0xd0, 0x72, 0x07, 0x2a, 0x1b, 0x71, 0x78, 0xb8, 0x93, 0xbb, 0x6d, 0x98, 0x7f, 0x34,
0xa0, 0x36, 0xc9, 0x10, 0xf4, 0xc5, 0x84, 0xa0, 0x76, 0x45, 0x59, 0x95, 0x7f, 0x87, 0x9c, 0x87,
0x52, 0xb7, 0xa1, 0xe4, 0x07, 0x62, 0xce, 0xf6, 0xa9, 0x7a, 0xf5, 0x37, 0xf4, 0x4b, 0xee, 0x2b,
0xf8, 0xe5, 0xa8, 0x71, 0x33, 0x25, 0x5e, 0x23, 0x70, 0xc4, 0x8a, 0x4c, 0x28, 0x4a, 0x7b, 0x44,
0xcd, 0x17, 0xdd, 0x19, 0x44, 0x6d, 0x7d, 0x24, 0x21, 0x58, 0x61, 0xcc, 0xf7, 0xa1, 0x24, 0x86,
0x8f, 0x3d, 0xc2, 0x2d, 0x11, 0x40, 0x8c, 0xb8, 0xc7, 0xf7, 0x1d, 0xef, 0x54, 0x99, 0x16, 0x05,
0xd0, 0xa1, 0x82, 0xe3, 0x88, 0xe2, 0x65, 0x25, 0x36, 0x37, 0x65, 0x89, 0xfd, 0x53, 0x0e, 0x2a,
0x42, 0xbb, 0xae, 0xda, 0xdf, 0x84, 0xaa, 0x9b, 0xbc, 0x93, 0xb2, 0xe2, 0xa6, 0x12, 0x98, 0x8e,
0x52, 0x9c, 0xa6, 0x15, 0xcc, 0xc7, 0x0e, 0x71, 0xed, 0x88, 0x39, 0x97, 0x66, 0xbe, 0x97, 0x44,
0xe2, 0x34, 0xad, 0xc8, 0xc5, 0x27, 0xe2, 0xb5, 0x55, 0x77, 0x8c, 0x72, 0xf1, 0x7b, 0x02, 0x88,
0x43, 0xdc, 0xcb, 0x6e, 0x3c, 0x3b, 0xdd, 0x8d, 0xd1, 0x1d, 0x98, 0x17, 0xed, 0xd1, 0x1f, 0x70,
0x3d, 0x42, 0x14, 0x64, 0x23, 0x45, 0x17, 0xa3, 0xc6, 0xfc, 0xbb, 0x29, 0x0c, 0x1e, 0xa3, 0x34,
0x3f, 0x04, 0x80, 0xfd, 0xa3, 0x1f, 0x93, 0x6e, 0xf8, 0x5a, 0xd7, 0x0f, 0xce, 0xa2, 0xde, 0xaa,
0x7d, 0x4d, 0x40, 0x95, 0x43, 0xe2, 0x7a, 0x9b, 0xc0, 0xe1, 0x14, 0x25, 0x6a, 0x41, 0x39, 0x1a,
0xa6, 0x55, 0x2d, 0x59, 0x52, 0x6c, 0xe5, 0x68, 0xe2, 0xc6, 0x31, 0x4d, 0x2a, 0x74, 0x66, 0xaf,
0x0d, 0x9d, 0x36, 0xe4, 0x07, 0x8e, 0x2d, 0xaf, 0x5e, 0x6e, 0x7f, 0x55, 0x87, 0xff, 0xc3, 0xdd,
0xce, 0xe5, 0xa8, 0xf1, 0xea, 0xa4, 0x35, 0x94, 0x9f, 0x07, 0x84, 0x35, 0x1f, 0xee, 0x76, 0xb0,
0x60, 0x7e, 0xd9, 0x63, 0x14, 0xa7, 0x7c, 0x8c, 0x0d, 0x00, 0x75, 0x6b, 0xc1, 0x7d, 0x23, 0x7c,
0x08, 0xbd, 0x58, 0xec, 0x44, 0x18, 0x9c, 0xa0, 0x42, 0x0c, 0x96, 0xba, 0x94, 0xc8, 0xdf, 0xe2,
0xb9, 0x18, 0xb7, 0xfa, 0x41, 0xad, 0x24, 0x67, 0xb8, 0x2f, 0x67, 0xab, 0x4e, 0x82, 0xad, 0xfd,
0x8a, 0x52, 0xb3, 0xb4, 0x35, 0x2e, 0x0c, 0x5f, 0x95, 0x8f, 0x7c, 0x58, 0xb2, 0xc5, 0x34, 0x9a,
0x52, 0x5a, 0x9e, 0x5a, 0xe9, 0x4d, 0xa1, 0xb0, 0x33, 0x2e, 0x08, 0x5f, 0x95, 0x8d, 0x7e, 0x08,
0x2b, 0x1a, 0x78, 0x75, 0xae, 0xad, 0x81, 0xf4, 0x54, 0x5d, 0x4c, 0xda, 0x9d, 0x89, 0x54, 0xf8,
0x13, 0x24, 0x20, 0x1b, 0x8a, 0x6e, 0xd8, 0x5b, 0x2a, 0xb2, 0xb0, 0x7f, 0x2b, 0xdb, 0x2d, 0xe2,
0xe8, 0x6f, 0x26, 0x7b, 0x4a, 0x34, 0x37, 0xaa, 0x76, 0xa2, 0x64, 0xa3, 0x33, 0xa8, 0x58, 0x9e,
0xe7, 0x73, 0x2b, 0x9c, 0xb4, 0xe7, 0xa4, 0xaa, 0xcd, 0xa9, 0x55, 0x6d, 0xc6, 0x32, 0xc6, 0x7a,
0x58, 0x02, 0x83, 0x93, 0xaa, 0xd0, 0x13, 0x58, 0xf0, 0x9f, 0x78, 0x84, 0x62, 0x72, 0x4c, 0x28,
0xf1, 0xc4, 0x5a, 0x56, 0x95, 0xda, 0xbf, 0x96, 0x51, 0x7b, 0x8a, 0x39, 0x0e, 0xe9, 0x34, 0x9c,
0xe1, 0x71, 0x2d, 0x62, 0x0f, 0x3d, 0x76, 0x3c, 0xcb, 0x75, 0x7e, 0x4a, 0x28, 0xab, 0xcd, 0xc7,
0x7b, 0xe8, 0xbd, 0x08, 0x8a, 0x13, 0x14, 0xe8, 0xeb, 0x50, 0xe9, 0xba, 0x03, 0xc6, 0x09, 0x95,
0x15, 0x62, 0x41, 0x66, 0x50, 0x74, 0xbf, 0xad, 0x18, 0x85, 0x93, 0x74, 0x2b, 0xdf, 0x80, 0xca,
0xff, 0xd8, 0x17, 0x45, 0x5f, 0x1d, 0x77, 0xe8, 0x54, 0x7d, 0xf5, 0x3f, 0x06, 0xcc, 0xa7, 0xdd,
0x10, 0x4d, 0x63, 0xc6, 0xc4, 0x65, 0x5f, 0xd7, 0xca, 0xfc, 0xc4, 0x5a, 0xa9, 0x4a, 0xd2, 0xec,
0xa7, 0x29, 0x49, 0x1b, 0x00, 0x56, 0xe0, 0xe8, 0x6a, 0x14, 0x56, 0xb7, 0xa8, 0x9e, 0xc4, 0x8b,
0x33, 0x4e, 0x50, 0x89, 0x07, 0xeb, 0xfa, 0x1e, 0xa7, 0xbe, 0xeb, 0x12, 0x2a, 0x2b, 0x58, 0x29,
0x7c, 0xb0, 0xad, 0x08, 0x8a, 0x13, 0x14, 0xe6, 0x3e, 0xa4, 0x37, 0x3e, 0x74, 0x37, 0x34, 0x3c,
0xbc, 0xfb, 0xad, 0xa9, 0x8d, 0x36, 0x6f, 0x41, 0x19, 0xfb, 0x3e, 0x3f, 0xb0, 0xf8, 0x09, 0x43,
0x0d, 0x28, 0x04, 0xe2, 0x87, 0x5a, 0xe7, 0xe5, 0x77, 0x0e, 0x89, 0xc1, 0x21, 0xdc, 0xfc, 0xb5,
0x01, 0xaf, 0x4c, 0xdc, 0xae, 0x85, 0x03, 0xba, 0xd1, 0x49, 0x99, 0x14, 0x39, 0x20, 0xa6, 0xc3,
0x09, 0x2a, 0xd1, 0xb6, 0x53, 0x2b, 0xf9, 0x78, 0xdb, 0x4e, 0x69, 0xc3, 0x69, 0x5a, 0xf3, 0xdf,
0x39, 0x28, 0x1e, 0x72, 0x8b, 0x0f, 0x18, 0x7a, 0x0c, 0x25, 0x91, 0x3d, 0xb6, 0xc5, 0x2d, 0xa9,
0x39, 0xf3, 0x17, 0x2b, 0x3d, 0xfe, 0xc4, 0x1d, 0x4b, 0x43, 0x70, 0x24, 0x51, 0xac, 0xaa, 0x4c,
0xea, 0x51, 0xe6, 0x45, 0x25, 0x27, 0xd4, 0x8e, 0x15, 0x56, 0x8c, 0xeb, 0x7d, 0xc2, 0x98, 0xd5,
0xd3, 0xb1, 0x16, 0x8d, 0xeb, 0x7b, 0x21, 0x18, 0x6b, 0x3c, 0x7a, 0x1b, 0x8a, 0x94, 0x58, 0x2c,
0x1a, 0x22, 0xea, 0x5a, 0x24, 0x96, 0xd0, 0xcb, 0x51, 0x63, 0x4e, 0x09, 0x97, 0x67, 0xac, 0xa8,
0xd1, 0x7b, 0x70, 0xc3, 0x26, 0xdc, 0x72, 0xdc, 0x70, 0x76, 0xc8, 0xfc, 0xed, 0x20, 0x14, 0xd6,
0x09, 0x59, 0xdb, 0x15, 0x61, 0x93, 0x3a, 0x60, 0x2d, 0x50, 0xe4, 0x49, 0xd7, 0xb7, 0x89, 0x8c,
0xc3, 0x42, 0x9c, 0x27, 0x5b, 0xbe, 0x4d, 0xb0, 0xc4, 0x98, 0x4f, 0x0d, 0xa8, 0x84, 0x92, 0xb6,
0xac, 0x01, 0x23, 0x68, 0x3d, 0xba, 0x45, 0xf8, 0xdc, 0xba, 0xb1, 0xcd, 0xbe, 0x7b, 0x1e, 0x90,
0xcb, 0x51, 0xa3, 0x2c, 0xc9, 0xc4, 0x21, 0xba, 0x40, 0xc2, 0x47, 0xb9, 0x6b, 0x7c, 0xf4, 0x1a,
0x14, 0xe4, 0x9c, 0xa6, 0x9c, 0x19, 0x8d, 0x65, 0x72, 0x96, 0xc3, 0x21, 0xce, 0xfc, 0x5d, 0x0e,
0xaa, 0xa9, 0xcb, 0x65, 0x18, 0x8d, 0xa2, 0xdd, 0x2b, 0x97, 0x61, 0x9f, 0x9f, 0xfc, 0x11, 0xf1,
0xfb, 0x50, 0xec, 0x8a, 0xfb, 0xe9, 0xaf, 0xb8, 0xeb, 0xd3, 0x3c, 0x85, 0xf4, 0x4c, 0x1c, 0x49,
0xf2, 0xc8, 0xb0, 0x12, 0x88, 0x76, 0x60, 0x89, 0x12, 0x4e, 0xcf, 0x37, 0x8f, 0x39, 0xa1, 0xc9,
0x61, 0xb1, 0x10, 0x0f, 0x0f, 0x78, 0x9c, 0x00, 0x5f, 0xe5, 0x31, 0x5d, 0x98, 0x15, 0x8d, 0x5d,
0xb8, 0x9d, 0xa5, 0x3e, 0x5b, 0x45, 0x6e, 0xd7, 0xcc, 0x1a, 0x2f, 0xbc, 0xe3, 0x59, 0x9e, 0x1f,
0x06, 0x7b, 0x21, 0xf6, 0xce, 0x03, 0x01, 0xc4, 0x21, 0xee, 0xce, 0xb2, 0x58, 0x20, 0x7f, 0xf5,
0xac, 0x31, 0xf3, 0xf4, 0x59, 0x63, 0xe6, 0xa3, 0x67, 0x6a, 0x99, 0xfc, 0x01, 0x94, 0xe3, 0x31,
0xe2, 0x33, 0x56, 0x69, 0xfe, 0x08, 0x4a, 0x22, 0x92, 0xf4, 0xf8, 0x7b, 0x4d, 0xd1, 0x4f, 0x97,
0xe3, 0x5c, 0x96, 0x72, 0x6c, 0x6e, 0x40, 0xf8, 0x5d, 0x57, 0x54, 0x42, 0x87, 0x93, 0x7e, 0xaa,
0x12, 0xee, 0x0a, 0x00, 0x0e, 0xe1, 0x89, 0xfd, 0xf9, 0x17, 0x06, 0x80, 0xdc, 0x13, 0xb6, 0x87,
0x62, 0xb7, 0x5b, 0x85, 0x59, 0x51, 0x62, 0xc7, 0x0d, 0x93, 0x29, 0x20, 0x31, 0xe8, 0x21, 0x14,
0x7d, 0x39, 0x5e, 0xa8, 0x8f, 0x7f, 0x6f, 0x4e, 0x8c, 0x1a, 0xf5, 0x2f, 0x9b, 0x26, 0xb6, 0x9e,
0x6c, 0x9f, 0x71, 0xe2, 0x09, 0x1b, 0xe3, 0x88, 0x09, 0x67, 0x14, 0xac, 0x84, 0xb5, 0x5f, 0x7f,
0xfe, 0xa2, 0x3e, 0xf3, 0xf1, 0x8b, 0xfa, 0xcc, 0xdf, 0x5e, 0xd4, 0x67, 0x3e, 0xb8, 0xa8, 0x1b,
0xcf, 0x2f, 0xea, 0xc6, 0xc7, 0x17, 0x75, 0xe3, 0x1f, 0x17, 0x75, 0xe3, 0xe9, 0x3f, 0xeb, 0x33,
0xef, 0xe5, 0x86, 0xeb, 0xff, 0x0d, 0x00, 0x00, 0xff, 0xff, 0x99, 0xeb, 0x4a, 0xdd, 0xf4, 0x1a,
0x00, 0x00,
// 2160 bytes of a gzipped FileDescriptorProto
0x1f, 0x8b, 0x08, 0x00, 0x00, 0x09, 0x6e, 0x88, 0x02, 0xff, 0xcc, 0x19, 0xcd, 0x6f, 0x23, 0x57,
0x3d, 0x63, 0xc7, 0x5e, 0xfb, 0xe7, 0x38, 0x1f, 0xaf, 0x59, 0x70, 0x23, 0x61, 0xa7, 0xd3, 0x0a,
0xa5, 0xb0, 0xb5, 0x49, 0x0a, 0xd5, 0xb2, 0xc0, 0x42, 0x26, 0xce, 0x46, 0x51, 0x37, 0x9b, 0xe8,
0xa5, 0xbb, 0x88, 0x65, 0x85, 0x98, 0xcc, 0xbc, 0x38, 0x43, 0xc6, 0x33, 0xc3, 0x7b, 0x63, 0x6f,
0x4c, 0x0f, 0x54, 0x2a, 0x48, 0x1c, 0x10, 0xda, 0x23, 0x07, 0x84, 0xba, 0x82, 0x1b, 0x37, 0xfe,
0x06, 0x24, 0xf6, 0x58, 0x89, 0x0b, 0x07, 0x64, 0xb1, 0xee, 0x81, 0x23, 0xf7, 0x9c, 0xd0, 0x7b,
0xf3, 0xe6, 0xcb, 0x8e, 0x9b, 0x31, 0xed, 0xa1, 0xa7, 0x78, 0x7e, 0xdf, 0xef, 0xf7, 0xfd, 0x5e,
0xe0, 0xe0, 0xfc, 0x36, 0x6b, 0x5a, 0x6e, 0xeb, 0xbc, 0x77, 0x42, 0xa8, 0x43, 0x7c, 0xc2, 0x5a,
0x7d, 0xe2, 0x98, 0x2e, 0x6d, 0x49, 0x84, 0xee, 0x59, 0x5d, 0xdd, 0x38, 0xb3, 0x1c, 0x42, 0x07,
0x2d, 0xef, 0xbc, 0xc3, 0x01, 0xac, 0xd5, 0x25, 0xbe, 0xde, 0xea, 0x6f, 0xb6, 0x3a, 0xc4, 0x21,
0x54, 0xf7, 0x89, 0xd9, 0xf4, 0xa8, 0xeb, 0xbb, 0xe8, 0x8d, 0x80, 0xab, 0x99, 0xe4, 0x6a, 0x7a,
0xe7, 0x1d, 0x0e, 0x60, 0x4d, 0xce, 0xd5, 0xec, 0x6f, 0xae, 0xbd, 0xd5, 0xb1, 0xfc, 0xb3, 0xde,
0x49, 0xd3, 0x70, 0xbb, 0xad, 0x8e, 0xdb, 0x71, 0x5b, 0x82, 0xf9, 0xa4, 0x77, 0x2a, 0xbe, 0xc4,
0x87, 0xf8, 0x15, 0x08, 0x5d, 0x9b, 0x6a, 0x0a, 0xed, 0x39, 0xbe, 0xd5, 0x25, 0xe3, 0x56, 0xac,
0xbd, 0x73, 0x1d, 0x03, 0x33, 0xce, 0x48, 0x57, 0x9f, 0xe0, 0x7b, 0x7b, 0x1a, 0x5f, 0xcf, 0xb7,
0xec, 0x96, 0xe5, 0xf8, 0xcc, 0xa7, 0xe3, 0x4c, 0xea, 0xdf, 0xf3, 0x50, 0xda, 0x3e, 0xda, 0xdf,
0xa3, 0x6e, 0xcf, 0x43, 0xeb, 0x30, 0xef, 0xe8, 0x5d, 0x52, 0x53, 0xd6, 0x95, 0x8d, 0xb2, 0xb6,
0xf0, 0x62, 0xd8, 0x98, 0x1b, 0x0d, 0x1b, 0xf3, 0x0f, 0xf4, 0x2e, 0xc1, 0x02, 0x83, 0x6c, 0x28,
0xf5, 0x09, 0x65, 0x96, 0xeb, 0xb0, 0x5a, 0x6e, 0x3d, 0xbf, 0x51, 0xd9, 0xba, 0xdb, 0xcc, 0xe2,
0xb4, 0xa6, 0x50, 0xf0, 0x28, 0x60, 0xbd, 0xe7, 0xd2, 0xb6, 0xc5, 0x0c, 0xb7, 0x4f, 0xe8, 0x40,
0x5b, 0x96, 0x5a, 0x4a, 0x12, 0xc9, 0x70, 0xa4, 0x01, 0xfd, 0x4a, 0x81, 0x65, 0x8f, 0x92, 0x53,
0x42, 0x29, 0x31, 0x25, 0xbe, 0x96, 0x5f, 0x57, 0x3e, 0x07, 0xb5, 0x35, 0xa9, 0x76, 0xf9, 0x68,
0x4c, 0x3e, 0x9e, 0xd0, 0x88, 0xfe, 0xa4, 0xc0, 0x1a, 0x23, 0xb4, 0x4f, 0xe8, 0xb6, 0x69, 0x52,
0xc2, 0x98, 0x36, 0xd8, 0xb1, 0x2d, 0xe2, 0xf8, 0x3b, 0xfb, 0x6d, 0xcc, 0x6a, 0xf3, 0xc2, 0x0f,
0xdf, 0xcf, 0x66, 0xd0, 0xf1, 0x34, 0x39, 0x9a, 0x2a, 0x2d, 0x5a, 0x9b, 0x4a, 0xc2, 0xf0, 0xa7,
0x98, 0xa1, 0x9e, 0xc2, 0x42, 0x18, 0xc8, 0xfb, 0x16, 0xf3, 0xd1, 0x23, 0x28, 0x76, 0xf8, 0x07,
0xab, 0x29, 0xc2, 0xc0, 0x66, 0x36, 0x03, 0x43, 0x19, 0xda, 0xa2, 0xb4, 0xa7, 0x28, 0x3e, 0x19,
0x96, 0xd2, 0xd4, 0x0f, 0x73, 0x50, 0xd9, 0x3e, 0xda, 0xc7, 0x84, 0xb9, 0x3d, 0x6a, 0x90, 0x0c,
0x49, 0xb3, 0x05, 0xc0, 0xff, 0x32, 0x4f, 0x37, 0x88, 0x59, 0xcb, 0xad, 0x2b, 0x1b, 0x25, 0x0d,
0x49, 0x3a, 0x78, 0x10, 0x61, 0x70, 0x82, 0x8a, 0x4b, 0x3d, 0xb7, 0x1c, 0x53, 0x44, 0x3b, 0x21,
0xf5, 0x5d, 0xcb, 0x31, 0xb1, 0xc0, 0xa0, 0xfb, 0x50, 0xe8, 0x13, 0x7a, 0xc2, 0xfd, 0xcf, 0x13,
0xe2, 0xeb, 0xd9, 0x8e, 0xf7, 0x88, 0xb3, 0x68, 0xe5, 0xd1, 0xb0, 0x51, 0x10, 0x3f, 0x71, 0x20,
0x04, 0x35, 0x01, 0xd8, 0x99, 0x4b, 0x7d, 0x61, 0x4e, 0xad, 0xb0, 0x9e, 0xdf, 0x28, 0x6b, 0x8b,
0xdc, 0xbe, 0xe3, 0x08, 0x8a, 0x13, 0x14, 0xea, 0x5f, 0x15, 0x58, 0x4a, 0x78, 0x41, 0x78, 0xfc,
0x36, 0x2c, 0x74, 0x12, 0xf9, 0x26, 0x3d, 0xb2, 0x2a, 0x6d, 0x5f, 0x48, 0xe6, 0x22, 0x4e, 0x51,
0x22, 0x02, 0x65, 0x2a, 0x25, 0x85, 0x75, 0xb5, 0x99, 0x39, 0x5c, 0xa1, 0x0d, 0xb1, 0xa6, 0x04,
0x90, 0xe1, 0x58, 0xb2, 0xfa, 0x1f, 0x45, 0x84, 0x2e, 0xac, 0x34, 0xb4, 0x91, 0xa8, 0x66, 0x45,
0x1c, 0x79, 0x61, 0x4a, 0x25, 0x5e, 0x53, 0x02, 0xb9, 0x2f, 0x44, 0x09, 0xdc, 0x29, 0xfd, 0xfe,
0xa3, 0xc6, 0xdc, 0x07, 0xff, 0x5a, 0x9f, 0x53, 0x3f, 0xc9, 0x41, 0xb5, 0x4d, 0x6c, 0xe2, 0x93,
0x43, 0xcf, 0x17, 0x27, 0xb8, 0x07, 0xa8, 0x43, 0x75, 0x83, 0x1c, 0x11, 0x6a, 0xb9, 0xe6, 0x31,
0x31, 0x5c, 0xc7, 0x64, 0x22, 0x44, 0x79, 0xed, 0x4b, 0xa3, 0x61, 0x03, 0xed, 0x4d, 0x60, 0xf1,
0x15, 0x1c, 0xc8, 0x86, 0xaa, 0x47, 0xc5, 0x6f, 0xcb, 0x97, 0x6d, 0x90, 0xa7, 0xdf, 0xdb, 0xd9,
0xce, 0x7e, 0x94, 0x64, 0xd5, 0x56, 0x46, 0xc3, 0x46, 0x35, 0x05, 0xc2, 0x69, 0xe1, 0xe8, 0x07,
0xb0, 0xec, 0x52, 0xef, 0x4c, 0x77, 0xda, 0xc4, 0x23, 0x8e, 0x49, 0x1c, 0x9f, 0x89, 0x92, 0x28,
0x69, 0xab, 0xbc, 0x79, 0x1d, 0x8e, 0xe1, 0xf0, 0x04, 0x35, 0x7a, 0x0c, 0x2b, 0x1e, 0x75, 0x3d,
0xbd, 0xa3, 0x73, 0x89, 0x47, 0xae, 0x6d, 0x19, 0x03, 0x51, 0x32, 0x65, 0xed, 0xd6, 0x68, 0xd8,
0x58, 0x39, 0x1a, 0x47, 0x5e, 0x0e, 0x1b, 0xaf, 0x08, 0xd7, 0x71, 0x48, 0x8c, 0xc4, 0x93, 0x62,
0xd4, 0x7d, 0x28, 0xb5, 0x7b, 0x54, 0x40, 0xd0, 0xf7, 0xa0, 0x64, 0xca, 0xdf, 0xd2, 0xab, 0xaf,
0x85, 0x9d, 0x3d, 0xa4, 0xb9, 0x1c, 0x36, 0xaa, 0x7c, 0x80, 0x35, 0x43, 0x00, 0x8e, 0x58, 0xd4,
0x27, 0x50, 0xdd, 0xbd, 0xf0, 0x5c, 0xea, 0x87, 0xf1, 0xfa, 0x2a, 0x14, 0x89, 0x00, 0x08, 0x69,
0xa5, 0xb8, 0x1d, 0x05, 0x64, 0x58, 0x62, 0xd1, 0xeb, 0x50, 0x20, 0x17, 0xba, 0xe1, 0xcb, 0xbe,
0x52, 0x95, 0x64, 0x85, 0x5d, 0x0e, 0xc4, 0x01, 0x4e, 0x3d, 0x04, 0xd8, 0x23, 0x91, 0xe8, 0x6d,
0x58, 0x0a, 0x6b, 0x22, 0x5d, 0xaa, 0x5f, 0x96, 0xcc, 0x4b, 0x38, 0x8d, 0xc6, 0xe3, 0xf4, 0xea,
0x13, 0x28, 0x8b, 0x72, 0xe6, 0xfd, 0x88, 0x9b, 0x20, 0xaa, 0x59, 0x4a, 0x89, 0x4c, 0x10, 0x14,
0x38, 0xc0, 0x45, 0x0d, 0x2d, 0x37, 0xad, 0xa1, 0x25, 0xb2, 0xd7, 0x86, 0x6a, 0xc0, 0x1b, 0xf6,
0xd8, 0x4c, 0x1a, 0x6e, 0x41, 0x29, 0x34, 0x53, 0x6a, 0x89, 0x66, 0x6b, 0x28, 0x08, 0x47, 0x14,
0x09, 0x6d, 0x67, 0x90, 0x6a, 0x4d, 0xd9, 0x94, 0xbd, 0x09, 0x37, 0x64, 0x73, 0x90, 0xba, 0x96,
0x24, 0xd9, 0x8d, 0xd0, 0x67, 0x21, 0x3e, 0xa1, 0xe9, 0x97, 0x50, 0x9b, 0x36, 0x90, 0x3f, 0x43,
0xf3, 0xcc, 0x6e, 0x8a, 0xfa, 0x3b, 0x05, 0x96, 0x93, 0x92, 0xb2, 0x87, 0x2f, 0xbb, 0x92, 0xeb,
0x47, 0x57, 0xc2, 0x23, 0x7f, 0x54, 0x60, 0x35, 0x75, 0xb4, 0x99, 0x22, 0x3e, 0x83, 0x51, 0xc9,
0xe4, 0xc8, 0xcf, 0x90, 0x1c, 0xff, 0xc8, 0x41, 0xf5, 0xbe, 0x7e, 0x42, 0xec, 0x63, 0x62, 0x13,
0xc3, 0x77, 0x29, 0x7a, 0x1f, 0x2a, 0x5d, 0xdd, 0x37, 0xce, 0x04, 0x34, 0x5c, 0x2e, 0xda, 0xd9,
0xda, 0x5f, 0x4a, 0x52, 0xf3, 0x20, 0x16, 0xb3, 0xeb, 0xf8, 0x74, 0xa0, 0xbd, 0x22, 0x4d, 0xaa,
0x24, 0x30, 0x38, 0xa9, 0x4d, 0x6c, 0x84, 0xe2, 0x7b, 0xf7, 0xc2, 0xe3, 0xfd, 0x7f, 0xf6, 0x45,
0x34, 0x65, 0x02, 0x26, 0x3f, 0xef, 0x59, 0x94, 0x74, 0x89, 0xe3, 0xc7, 0x1b, 0xe1, 0xc1, 0x98,
0x7c, 0x3c, 0xa1, 0x71, 0xed, 0x2e, 0x2c, 0x8f, 0x1b, 0x8f, 0x96, 0x21, 0x7f, 0x4e, 0x06, 0x41,
0xbc, 0x30, 0xff, 0x89, 0x56, 0xa1, 0xd0, 0xd7, 0xed, 0x9e, 0xac, 0x46, 0x1c, 0x7c, 0xdc, 0xc9,
0xdd, 0x56, 0xd4, 0x3f, 0x2b, 0x50, 0x9b, 0x66, 0x08, 0xfa, 0x4a, 0x42, 0x90, 0x56, 0x91, 0x56,
0xe5, 0xdf, 0x25, 0x83, 0x40, 0xea, 0x2e, 0x94, 0x5c, 0x8f, 0xef, 0xf0, 0x2e, 0x95, 0x51, 0x7f,
0x33, 0x8c, 0xe4, 0xa1, 0x84, 0x5f, 0x0e, 0x1b, 0x37, 0x53, 0xe2, 0x43, 0x04, 0x8e, 0x58, 0x91,
0x0a, 0x45, 0x61, 0x0f, 0x9f, 0x27, 0x7c, 0xf2, 0x03, 0xef, 0xad, 0x8f, 0x04, 0x04, 0x4b, 0x8c,
0xfa, 0x3e, 0x94, 0xf8, 0x62, 0x73, 0x40, 0x7c, 0x9d, 0x27, 0x10, 0x23, 0xf6, 0xe9, 0x7d, 0xcb,
0x39, 0x97, 0xa6, 0x45, 0x09, 0x74, 0x2c, 0xe1, 0x38, 0xa2, 0xb8, 0xaa, 0xc5, 0xe6, 0x66, 0x6c,
0xb1, 0x7f, 0xc9, 0x41, 0x85, 0x6b, 0x0f, 0xbb, 0xf6, 0x77, 0xa0, 0x6a, 0x27, 0xcf, 0x24, 0xad,
0xb8, 0x29, 0x05, 0xa6, 0xb3, 0x14, 0xa7, 0x69, 0x39, 0xf3, 0xa9, 0x45, 0x6c, 0x33, 0x62, 0xce,
0xa5, 0x99, 0xef, 0x25, 0x91, 0x38, 0x4d, 0xcb, 0x6b, 0xf1, 0x29, 0x8f, 0xb6, 0x9c, 0xbc, 0x51,
0x2d, 0xfe, 0x90, 0x03, 0x71, 0x80, 0xbb, 0xea, 0xc4, 0xf3, 0xb3, 0x9d, 0x18, 0xdd, 0x81, 0x45,
0x3e, 0x1e, 0xdd, 0x9e, 0x1f, 0xae, 0x27, 0x05, 0x31, 0x48, 0xd1, 0x68, 0xd8, 0x58, 0x7c, 0x2f,
0x85, 0xc1, 0x63, 0x94, 0xea, 0x87, 0x00, 0x70, 0x78, 0xf2, 0x33, 0x62, 0x04, 0xd1, 0xba, 0x7e,
0x29, 0xe7, 0xfd, 0x56, 0xde, 0x05, 0x39, 0x54, 0x3a, 0x24, 0xee, 0xb7, 0x09, 0x1c, 0x4e, 0x51,
0xa2, 0x16, 0x94, 0xa3, 0x45, 0x5d, 0xf6, 0x92, 0x15, 0xc9, 0x56, 0x8e, 0xb6, 0x79, 0x1c, 0xd3,
0xa4, 0x52, 0x67, 0xfe, 0xda, 0xd4, 0xd1, 0x20, 0xdf, 0xb3, 0x4c, 0x71, 0xf4, 0xb2, 0xf6, 0x8d,
0x30, 0xfd, 0x1f, 0xee, 0xb7, 0x2f, 0x87, 0x8d, 0xd7, 0xa6, 0x5d, 0x71, 0xfd, 0x81, 0x47, 0x58,
0xf3, 0xe1, 0x7e, 0x1b, 0x73, 0xe6, 0xab, 0x82, 0x51, 0x9c, 0x31, 0x18, 0x5b, 0x00, 0xf2, 0xd4,
0x9c, 0xfb, 0x46, 0x10, 0x88, 0xf0, 0xd2, 0xb2, 0x17, 0x61, 0x70, 0x82, 0x0a, 0x31, 0x58, 0x31,
0x28, 0x11, 0xbf, 0x79, 0xb8, 0x98, 0xaf, 0x77, 0xbd, 0x5a, 0x49, 0xec, 0x87, 0x5f, 0xcb, 0xd6,
0x9d, 0x38, 0x9b, 0xf6, 0xaa, 0x54, 0xb3, 0xb2, 0x33, 0x2e, 0x0c, 0x4f, 0xca, 0x47, 0x2e, 0xac,
0x98, 0x72, 0x5d, 0x8b, 0x95, 0x96, 0x67, 0x56, 0x7a, 0x93, 0x2b, 0x6c, 0x8f, 0x0b, 0xc2, 0x93,
0xb2, 0xd1, 0x4f, 0x60, 0x2d, 0x04, 0x4e, 0xee, 0xcc, 0x35, 0x10, 0x9e, 0xaa, 0xf3, 0x2d, 0xbe,
0x3d, 0x95, 0x0a, 0x7f, 0x8a, 0x04, 0x64, 0x42, 0xd1, 0x0e, 0x66, 0x4b, 0x45, 0x34, 0xf6, 0xef,
0x66, 0x3b, 0x45, 0x9c, 0xfd, 0xcd, 0xe4, 0x4c, 0x89, 0xf6, 0x46, 0x39, 0x4e, 0xa4, 0x6c, 0x74,
0x01, 0x15, 0xdd, 0x71, 0x5c, 0x5f, 0x0f, 0xb6, 0xf8, 0x05, 0xa1, 0x6a, 0x7b, 0x66, 0x55, 0xdb,
0xb1, 0x8c, 0xb1, 0x19, 0x96, 0xc0, 0xe0, 0xa4, 0x2a, 0xf4, 0x14, 0x96, 0xdc, 0xa7, 0x0e, 0xa1,
0x98, 0x9c, 0x12, 0x4a, 0x1c, 0x7e, 0xe5, 0xab, 0x0a, 0xed, 0xdf, 0xcc, 0xa8, 0x3d, 0xc5, 0x1c,
0xa7, 0x74, 0x1a, 0xce, 0xf0, 0xb8, 0x16, 0x7e, 0xc7, 0x3d, 0xb5, 0x1c, 0xdd, 0xb6, 0x7e, 0x41,
0x28, 0xab, 0x2d, 0xc6, 0x77, 0xdc, 0x7b, 0x11, 0x14, 0x27, 0x28, 0xd0, 0xb7, 0xa0, 0x62, 0xd8,
0x3d, 0xe6, 0x13, 0x2a, 0x3a, 0xc4, 0x92, 0xa8, 0xa0, 0xe8, 0x7c, 0x3b, 0x31, 0x0a, 0x27, 0xe9,
0xd6, 0xbe, 0x0d, 0x95, 0xff, 0x73, 0x2e, 0xf2, 0xb9, 0x3a, 0xee, 0xd0, 0x99, 0xe6, 0xea, 0xdf,
0x72, 0xb0, 0x98, 0x76, 0x43, 0xb4, 0x8d, 0x29, 0x53, 0x1f, 0x12, 0xc2, 0x5e, 0x99, 0x9f, 0xda,
0x2b, 0x65, 0x4b, 0x9a, 0xff, 0x2c, 0x2d, 0x69, 0x0b, 0x40, 0xf7, 0xac, 0xb0, 0x1b, 0x05, 0xdd,
0x2d, 0xea, 0x27, 0xf1, 0xa5, 0x1c, 0x27, 0xa8, 0x78, 0xc0, 0x0c, 0xd7, 0xf1, 0xa9, 0x6b, 0xdb,
0x84, 0x8a, 0x0e, 0x56, 0x0a, 0x02, 0xb6, 0x13, 0x41, 0x71, 0x82, 0x82, 0xdf, 0x71, 0x4f, 0x6c,
0xd7, 0x38, 0x17, 0x2e, 0x08, 0xab, 0x4f, 0xf4, 0xae, 0x52, 0x70, 0xc7, 0xd5, 0x26, 0xb0, 0xf8,
0x0a, 0x0e, 0xf5, 0x10, 0xd2, 0xb7, 0x52, 0x74, 0x37, 0x70, 0x80, 0x12, 0x5d, 0x1b, 0x67, 0x3b,
0xbc, 0x7a, 0x0b, 0xca, 0xd8, 0x75, 0xfd, 0x23, 0xdd, 0x3f, 0x63, 0xa8, 0x01, 0x05, 0x8f, 0xff,
0x90, 0x4f, 0x0e, 0xe2, 0x2d, 0x46, 0x60, 0x70, 0x00, 0x57, 0x7f, 0xab, 0xc0, 0xab, 0x53, 0x5f,
0x00, 0xb8, 0x23, 0x8d, 0xe8, 0x4b, 0x9a, 0x14, 0x39, 0x32, 0xa6, 0xc3, 0x09, 0x2a, 0x3e, 0xfe,
0x53, 0xcf, 0x06, 0xe3, 0xe3, 0x3f, 0xa5, 0x0d, 0xa7, 0x69, 0xd5, 0xff, 0xe6, 0xa0, 0x78, 0xec,
0xeb, 0x7e, 0x8f, 0xa1, 0x27, 0x50, 0xe2, 0x55, 0x68, 0xea, 0xbe, 0x2e, 0x34, 0x67, 0x7e, 0x55,
0x0b, 0xd7, 0xa8, 0x78, 0xf2, 0x85, 0x10, 0x1c, 0x49, 0xe4, 0x57, 0x5e, 0x26, 0xf4, 0x48, 0xf3,
0xa2, 0xd6, 0x15, 0x68, 0xc7, 0x12, 0xcb, 0xd7, 0xfe, 0x2e, 0x61, 0x4c, 0xef, 0x84, 0x39, 0x1b,
0xad, 0xfd, 0x07, 0x01, 0x18, 0x87, 0x78, 0xf4, 0x0e, 0x14, 0x29, 0xd1, 0x59, 0xb4, 0x8c, 0xd4,
0x43, 0x91, 0x58, 0x40, 0x2f, 0x87, 0x8d, 0x05, 0x29, 0x5c, 0x7c, 0x63, 0x49, 0x8d, 0x1e, 0xc3,
0x0d, 0x93, 0xf8, 0xba, 0x65, 0x07, 0x3b, 0x48, 0xe6, 0xf7, 0x8d, 0x40, 0x58, 0x3b, 0x60, 0xd5,
0x2a, 0xdc, 0x26, 0xf9, 0x81, 0x43, 0x81, 0xbc, 0xde, 0x0c, 0xd7, 0x24, 0x22, 0x9f, 0x0b, 0x71,
0xbd, 0xed, 0xb8, 0x26, 0xc1, 0x02, 0xa3, 0x3e, 0x53, 0xa0, 0x12, 0x48, 0xda, 0xd1, 0x7b, 0x8c,
0xa0, 0xcd, 0xe8, 0x14, 0x41, 0xb8, 0xc3, 0x01, 0x39, 0xff, 0xde, 0xc0, 0x23, 0x97, 0xc3, 0x46,
0x59, 0x90, 0xf1, 0x8f, 0xe8, 0x00, 0x09, 0x1f, 0xe5, 0xae, 0xf1, 0xd1, 0xeb, 0x50, 0x10, 0xfb,
0x9e, 0x74, 0x66, 0xb4, 0xde, 0x89, 0x9d, 0x10, 0x07, 0x38, 0xf5, 0x0f, 0x39, 0xa8, 0xa6, 0x0e,
0x97, 0x61, 0xc5, 0x8a, 0xee, 0x70, 0xb9, 0x0c, 0xef, 0x02, 0xd3, 0x1f, 0x3a, 0x7f, 0x04, 0x45,
0x83, 0x9f, 0x2f, 0x7c, 0x69, 0xde, 0x9c, 0x25, 0x14, 0xc2, 0x33, 0x71, 0x26, 0x89, 0x4f, 0x86,
0xa5, 0x40, 0xb4, 0x07, 0x2b, 0x94, 0xf8, 0x74, 0xb0, 0x7d, 0xea, 0x13, 0x9a, 0x5c, 0x3a, 0x0b,
0xf1, 0x12, 0x82, 0xc7, 0x09, 0xf0, 0x24, 0x8f, 0x6a, 0xc3, 0x3c, 0x5f, 0x10, 0xb8, 0xdb, 0x59,
0xea, 0x69, 0x2d, 0x72, 0x7b, 0xc8, 0x1c, 0xe2, 0xb9, 0x77, 0x1c, 0xdd, 0x71, 0x83, 0x64, 0x2f,
0xc4, 0xde, 0x79, 0xc0, 0x81, 0x38, 0xc0, 0xdd, 0x59, 0xe5, 0x17, 0xd1, 0xdf, 0x3c, 0x6f, 0xcc,
0x3d, 0x7b, 0xde, 0x98, 0xfb, 0xe8, 0xb9, 0xbc, 0x94, 0xfe, 0x18, 0xca, 0xf1, 0x3a, 0xf2, 0x39,
0xab, 0x54, 0x7f, 0x0a, 0x25, 0x9e, 0x49, 0xe1, 0x1a, 0x7d, 0xcd, 0xf0, 0x48, 0xb7, 0xf5, 0x5c,
0x96, 0xb6, 0xae, 0x6e, 0x41, 0xf0, 0xf6, 0xcc, 0x3b, 0xa1, 0xe5, 0x93, 0x6e, 0xaa, 0x13, 0xee,
0x73, 0x00, 0x0e, 0xe0, 0x89, 0x7b, 0xf8, 0xaf, 0x15, 0x00, 0x71, 0xdf, 0xd8, 0xed, 0xf3, 0x3b,
0xe2, 0x3a, 0xcc, 0xf3, 0x16, 0x3b, 0x6e, 0x98, 0x28, 0x01, 0x81, 0x41, 0x0f, 0xa1, 0xe8, 0x8a,
0x35, 0x45, 0x3e, 0x50, 0xbe, 0x35, 0x35, 0x6b, 0xe4, 0xbf, 0x95, 0x9a, 0x58, 0x7f, 0xba, 0x7b,
0xe1, 0x13, 0x87, 0xdb, 0x18, 0x67, 0x4c, 0xb0, 0xeb, 0x60, 0x29, 0x4c, 0x7b, 0xe3, 0xc5, 0xcb,
0xfa, 0xdc, 0xc7, 0x2f, 0xeb, 0x73, 0xff, 0x7c, 0x59, 0x9f, 0xfb, 0x60, 0x54, 0x57, 0x5e, 0x8c,
0xea, 0xca, 0xc7, 0xa3, 0xba, 0xf2, 0xef, 0x51, 0x5d, 0x79, 0xf6, 0x49, 0x7d, 0xee, 0x71, 0xae,
0xbf, 0xf9, 0xbf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xb1, 0xe8, 0xc1, 0x2f, 0x98, 0x1b, 0x00, 0x00,
}

View file

@ -121,10 +121,19 @@ message DeleteOptions {
// +optional
optional Preconditions preconditions = 2;
// Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7.
// Should the dependent objects be orphaned. If true/false, the "orphan"
// finalizer will be added to/removed from the object's finalizers list.
// Either this field or PropagationPolicy may be set, but not both.
// +optional
optional bool orphanDependents = 3;
// Whether and how garbage collection will be performed.
// Either this field or OrphanDependents may be set, but not both.
// The default policy is decided by the existing finalizer set in the
// metadata.finalizers and the resource-specific default policy.
// +optional
optional string propagationPolicy = 4;
}
// Duration is a wrapper around time.Duration which supports correct
@ -469,6 +478,15 @@ message OwnerReference {
// If true, this reference points to the managing controller.
// +optional
optional bool controller = 6;
// If true, AND if the owner has the "foregroundDeletion" finalizer, then
// the owner cannot be deleted from the key-value store until this
// reference is removed.
// Defaults to false.
// To set this field, a user needs "delete" permission of the owner,
// otherwise 422 (Unprocessable Entity) will be returned.
// +optional
optional bool blockOwnerDeletion = 7;
}
// Preconditions must be fulfilled before an operation (update, delete, etc.) is carried out.

View file

@ -1,78 +0,0 @@
/*
Copyright 2015 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1
import (
"encoding/json"
"reflect"
"testing"
"github.com/ugorji/go/codec"
)
type GroupVersionHolder struct {
GV GroupVersion `json:"val"`
}
func TestGroupVersionUnmarshalJSON(t *testing.T) {
cases := []struct {
input []byte
expect GroupVersion
}{
{[]byte(`{"val": "v1"}`), GroupVersion{"", "v1"}},
{[]byte(`{"val": "extensions/v1beta1"}`), GroupVersion{"extensions", "v1beta1"}},
}
for _, c := range cases {
var result GroupVersionHolder
// test golang lib's JSON codec
if err := json.Unmarshal([]byte(c.input), &result); err != nil {
t.Errorf("JSON codec failed to unmarshal input '%v': %v", c.input, err)
}
if !reflect.DeepEqual(result.GV, c.expect) {
t.Errorf("JSON codec failed to unmarshal input '%s': expected %+v, got %+v", c.input, c.expect, result.GV)
}
// test the Ugorji codec
if err := codec.NewDecoderBytes(c.input, new(codec.JsonHandle)).Decode(&result); err != nil {
t.Errorf("Ugorji codec failed to unmarshal input '%v': %v", c.input, err)
}
if !reflect.DeepEqual(result.GV, c.expect) {
t.Errorf("Ugorji codec failed to unmarshal input '%s': expected %+v, got %+v", c.input, c.expect, result.GV)
}
}
}
func TestGroupVersionMarshalJSON(t *testing.T) {
cases := []struct {
input GroupVersion
expect []byte
}{
{GroupVersion{"", "v1"}, []byte(`{"val":"v1"}`)},
{GroupVersion{"extensions", "v1beta1"}, []byte(`{"val":"extensions/v1beta1"}`)},
}
for _, c := range cases {
input := GroupVersionHolder{c.input}
result, err := json.Marshal(&input)
if err != nil {
t.Errorf("Failed to marshal input '%v': %v", input, err)
}
if !reflect.DeepEqual(result, c.expect) {
t.Errorf("Failed to marshal input '%+v': expected: %s, got: %s", input, c.expect, result)
}
}
}

View file

@ -226,3 +226,9 @@ func NewUIDPreconditions(uid string) *Preconditions {
u := types.UID(uid)
return &Preconditions{UID: &u}
}
// 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
}

View file

@ -1,154 +0,0 @@
/*
Copyright 2016 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1
import (
"reflect"
"strings"
"testing"
"k8s.io/apimachinery/pkg/labels"
)
func TestLabelSelectorAsSelector(t *testing.T) {
matchLabels := map[string]string{"foo": "bar"}
matchExpressions := []LabelSelectorRequirement{{
Key: "baz",
Operator: LabelSelectorOpIn,
Values: []string{"qux", "norf"},
}}
mustParse := func(s string) labels.Selector {
out, e := labels.Parse(s)
if e != nil {
panic(e)
}
return out
}
tc := []struct {
in *LabelSelector
out labels.Selector
expectErr bool
}{
{in: nil, out: labels.Nothing()},
{in: &LabelSelector{}, out: labels.Everything()},
{
in: &LabelSelector{MatchLabels: matchLabels},
out: mustParse("foo=bar"),
},
{
in: &LabelSelector{MatchExpressions: matchExpressions},
out: mustParse("baz in (norf,qux)"),
},
{
in: &LabelSelector{MatchLabels: matchLabels, MatchExpressions: matchExpressions},
out: mustParse("baz in (norf,qux),foo=bar"),
},
{
in: &LabelSelector{
MatchExpressions: []LabelSelectorRequirement{{
Key: "baz",
Operator: LabelSelectorOpExists,
Values: []string{"qux", "norf"},
}},
},
expectErr: true,
},
}
for i, tc := range tc {
out, err := LabelSelectorAsSelector(tc.in)
if err == nil && tc.expectErr {
t.Errorf("[%v]expected error but got none.", i)
}
if err != nil && !tc.expectErr {
t.Errorf("[%v]did not expect error but got: %v", i, err)
}
if !reflect.DeepEqual(out, tc.out) {
t.Errorf("[%v]expected:\n\t%+v\nbut got:\n\t%+v", i, tc.out, out)
}
}
}
func TestLabelSelectorAsMap(t *testing.T) {
matchLabels := map[string]string{"foo": "bar"}
matchExpressions := func(operator LabelSelectorOperator, values []string) []LabelSelectorRequirement {
return []LabelSelectorRequirement{{
Key: "baz",
Operator: operator,
Values: values,
}}
}
tests := []struct {
in *LabelSelector
out map[string]string
errString string
}{
{in: nil, out: nil},
{
in: &LabelSelector{MatchLabels: matchLabels},
out: map[string]string{"foo": "bar"},
},
{
in: &LabelSelector{MatchLabels: matchLabels, MatchExpressions: matchExpressions(LabelSelectorOpIn, []string{"norf"})},
out: map[string]string{"foo": "bar", "baz": "norf"},
},
{
in: &LabelSelector{MatchExpressions: matchExpressions(LabelSelectorOpIn, []string{"norf"})},
out: map[string]string{"baz": "norf"},
},
{
in: &LabelSelector{MatchLabels: matchLabels, MatchExpressions: matchExpressions(LabelSelectorOpIn, []string{"norf", "qux"})},
out: map[string]string{"foo": "bar"},
errString: "without a single value cannot be converted",
},
{
in: &LabelSelector{MatchExpressions: matchExpressions(LabelSelectorOpNotIn, []string{"norf", "qux"})},
out: map[string]string{},
errString: "cannot be converted",
},
{
in: &LabelSelector{MatchLabels: matchLabels, MatchExpressions: matchExpressions(LabelSelectorOpExists, []string{})},
out: map[string]string{"foo": "bar"},
errString: "cannot be converted",
},
{
in: &LabelSelector{MatchExpressions: matchExpressions(LabelSelectorOpDoesNotExist, []string{})},
out: map[string]string{},
errString: "cannot be converted",
},
}
for i, tc := range tests {
out, err := LabelSelectorAsMap(tc.in)
if err == nil && len(tc.errString) > 0 {
t.Errorf("[%v]expected error but got none.", i)
continue
}
if err != nil && len(tc.errString) == 0 {
t.Errorf("[%v]did not expect error but got: %v", i, err)
continue
}
if err != nil && len(tc.errString) > 0 && !strings.Contains(err.Error(), tc.errString) {
t.Errorf("[%v]expected error with %q but got: %v", i, tc.errString, err)
continue
}
if !reflect.DeepEqual(out, tc.out) {
t.Errorf("[%v]expected:\n\t%+v\nbut got:\n\t%+v", i, tc.out, out)
}
}
}

View file

@ -1,120 +0,0 @@
/*
Copyright 2016 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1
import (
"reflect"
"testing"
)
func TestCloneSelectorAndAddLabel(t *testing.T) {
labels := map[string]string{
"foo1": "bar1",
"foo2": "bar2",
"foo3": "bar3",
}
cases := []struct {
labels map[string]string
labelKey string
labelValue string
want map[string]string
}{
{
labels: labels,
want: labels,
},
{
labels: labels,
labelKey: "foo4",
labelValue: "89",
want: map[string]string{
"foo1": "bar1",
"foo2": "bar2",
"foo3": "bar3",
"foo4": "89",
},
},
{
labels: nil,
labelKey: "foo4",
labelValue: "12",
want: map[string]string{
"foo4": "12",
},
},
}
for _, tc := range cases {
ls_in := LabelSelector{MatchLabels: tc.labels}
ls_out := LabelSelector{MatchLabels: tc.want}
got := CloneSelectorAndAddLabel(&ls_in, tc.labelKey, tc.labelValue)
if !reflect.DeepEqual(got, &ls_out) {
t.Errorf("got %v, want %v", got, tc.want)
}
}
}
func TestAddLabelToSelector(t *testing.T) {
labels := map[string]string{
"foo1": "bar1",
"foo2": "bar2",
"foo3": "bar3",
}
cases := []struct {
labels map[string]string
labelKey string
labelValue string
want map[string]string
}{
{
labels: labels,
want: labels,
},
{
labels: labels,
labelKey: "foo4",
labelValue: "89",
want: map[string]string{
"foo1": "bar1",
"foo2": "bar2",
"foo3": "bar3",
"foo4": "89",
},
},
{
labels: nil,
labelKey: "foo4",
labelValue: "12",
want: map[string]string{
"foo4": "12",
},
},
}
for _, tc := range cases {
ls_in := LabelSelector{MatchLabels: tc.labels}
ls_out := LabelSelector{MatchLabels: tc.want}
got := AddLabelToSelector(&ls_in, tc.labelKey, tc.labelValue)
if !reflect.DeepEqual(got, &ls_out) {
t.Errorf("got %v, want %v", got, tc.want)
}
}
}

View file

@ -174,6 +174,10 @@ func (meta *ObjectMeta) GetOwnerReferences() []OwnerReference {
value := *meta.OwnerReferences[i].Controller
ret[i].Controller = &value
}
if meta.OwnerReferences[i].BlockOwnerDeletion != nil {
value := *meta.OwnerReferences[i].BlockOwnerDeletion
ret[i].BlockOwnerDeletion = &value
}
}
return ret
}
@ -189,6 +193,10 @@ func (meta *ObjectMeta) SetOwnerReferences(references []OwnerReference) {
value := *references[i].Controller
newReferences[i].Controller = &value
}
if references[i].BlockOwnerDeletion != nil {
value := *references[i].BlockOwnerDeletion
newReferences[i].BlockOwnerDeletion = &value
}
}
meta.OwnerReferences = newReferences
}

View file

@ -55,6 +55,11 @@ func AddToGroupVersion(scheme *runtime.Scheme, groupVersion schema.GroupVersion)
Convert_watch_Event_to_versioned_Event,
Convert_versioned_Event_to_versioned_InternalEvent,
)
// register manually. This usually goes through the SchemeBuilder, which we cannot use here.
scheme.AddGeneratedDeepCopyFuncs(GetGeneratedDeepCopyFuncs()...)
AddConversionFuncs(scheme)
RegisterDefaults(scheme)
}
// scheme is the registry for the common types that adhere to the meta v1 API spec.
@ -70,4 +75,8 @@ func init() {
&GetOptions{},
&DeleteOptions{},
)
// register manually. This usually goes through the SchemeBuilder, which we cannot use here.
scheme.AddGeneratedDeepCopyFuncs(GetGeneratedDeepCopyFuncs()...)
RegisterDefaults(scheme)
}

View file

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

View file

@ -71,6 +71,12 @@ type ListMeta struct {
ResourceVersion string `json:"resourceVersion,omitempty" protobuf:"bytes,2,opt,name=resourceVersion"`
}
// These are internal finalizer values for Kubernetes-like APIs, must be qualified name unless defined here
const (
FinalizerOrphanDependents string = "orphan"
FinalizerDeleteDependents string = "foregroundDeletion"
)
// ObjectMeta is metadata that all persisted resources must have, which includes all objects
// users must create.
type ObjectMeta struct {
@ -228,6 +234,8 @@ const (
NamespaceNone string = ""
// NamespaceSystem is the system namespace where we place system components.
NamespaceSystem string = "kube-system"
// NamespacePublic is the namespace where we place public info (ConfigMaps)
NamespacePublic string = "kube-public"
)
// OwnerReference contains enough information to let you identify an owning
@ -248,6 +256,14 @@ type OwnerReference struct {
// If true, this reference points to the managing controller.
// +optional
Controller *bool `json:"controller,omitempty" protobuf:"varint,6,opt,name=controller"`
// If true, AND if the owner has the "foregroundDeletion" finalizer, then
// the owner cannot be deleted from the key-value store until this
// reference is removed.
// Defaults to false.
// To set this field, a user needs "delete" permission of the owner,
// otherwise 422 (Unprocessable Entity) will be returned.
// +optional
BlockOwnerDeletion *bool `json:"blockOwnerDeletion,omitempty" protobuf:"varint,7,opt,name=blockOwnerDeletion"`
}
// ListOptions is the query options to a standard REST list call.
@ -298,6 +314,24 @@ type GetOptions struct {
ResourceVersion string `json:"resourceVersion,omitempty" protobuf:"bytes,1,opt,name=resourceVersion"`
}
// DeletionPropagation decides if a deletion will propagate to the dependents of
// the object, and how the garbage collector will handle the propagation.
type DeletionPropagation string
const (
// Orphans the dependents.
DeletePropagationOrphan DeletionPropagation = "Orphan"
// Deletes the object from the key-value store, the garbage collector will
// delete the dependents in the background.
DeletePropagationBackground DeletionPropagation = "Background"
// The object exists in the key-value store until the garbage collector
// deletes all the dependents whose ownerReference.blockOwnerDeletion=true
// from the key-value store. API sever will put the "foregroundDeletion"
// finalizer on the object, and sets its deletionTimestamp. This policy is
// cascading, i.e., the dependents will be deleted with Foreground.
DeletePropagationForeground DeletionPropagation = "Foreground"
)
// DeleteOptions may be provided when deleting an API object.
type DeleteOptions struct {
TypeMeta `json:",inline"`
@ -314,10 +348,19 @@ type DeleteOptions struct {
// +optional
Preconditions *Preconditions `json:"preconditions,omitempty" protobuf:"bytes,2,opt,name=preconditions"`
// Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7.
// Should the dependent objects be orphaned. If true/false, the "orphan"
// finalizer will be added to/removed from the object's finalizers list.
// Either this field or PropagationPolicy may be set, but not both.
// +optional
OrphanDependents *bool `json:"orphanDependents,omitempty" protobuf:"varint,3,opt,name=orphanDependents"`
// Whether and how garbage collection will be performed.
// Either this field or OrphanDependents may be set, but not both.
// The default policy is decided by the existing finalizer set in the
// metadata.finalizers and the resource-specific default policy.
// +optional
PropagationPolicy *DeletionPropagation `json:"propagationPolicy,omitempty" protobuf:"varint,4,opt,name=propagationPolicy"`
}
// Preconditions must be fulfilled before an operation (update, delete, etc.) is carried out.

View file

@ -85,7 +85,8 @@ var map_DeleteOptions = map[string]string{
"": "DeleteOptions may be provided when deleting an API object.",
"gracePeriodSeconds": "The duration in seconds before the object should be deleted. Value must be non-negative integer. The value zero indicates delete immediately. If this value is nil, the default grace period for the specified type will be used. Defaults to a per object value if not specified. zero means delete immediately.",
"preconditions": "Must be fulfilled before a deletion is carried out. If not possible, a 409 Conflict status will be returned.",
"orphanDependents": "Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list.",
"orphanDependents": "Deprecated: please use the PropagationPolicy, this field will be deprecated in 1.7. Should the dependent objects be orphaned. If true/false, the \"orphan\" finalizer will be added to/removed from the object's finalizers list. Either this field or PropagationPolicy may be set, but not both.",
"propagationPolicy": "Whether and how garbage collection will be performed. Either this field or OrphanDependents may be set, but not both. The default policy is decided by the existing finalizer set in the metadata.finalizers and the resource-specific default policy.",
}
func (DeleteOptions) SwaggerDoc() map[string]string {
@ -189,12 +190,13 @@ func (ObjectMeta) SwaggerDoc() map[string]string {
}
var map_OwnerReference = map[string]string{
"": "OwnerReference contains enough information to let you identify an owning object. Currently, an owning object must be in the same namespace, so there is no namespace field.",
"apiVersion": "API version of the referent.",
"kind": "Kind of the referent. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds",
"name": "Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names",
"uid": "UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids",
"controller": "If true, this reference points to the managing controller.",
"": "OwnerReference contains enough information to let you identify an owning object. Currently, an owning object must be in the same namespace, so there is no namespace field.",
"apiVersion": "API version of the referent.",
"kind": "Kind of the referent. More info: http://releases.k8s.io/HEAD/docs/devel/api-conventions.md#types-kinds",
"name": "Name of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#names",
"uid": "UID of the referent. More info: http://kubernetes.io/docs/user-guide/identifiers#uids",
"controller": "If true, this reference points to the managing controller.",
"blockOwnerDeletion": "If true, AND if the owner has the \"foregroundDeletion\" finalizer, then the owner cannot be deleted from the key-value store until this reference is removed. Defaults to false. To set this field, a user needs \"delete\" permission of the owner, otherwise 422 (Unprocessable Entity) will be returned.",
}
func (OwnerReference) SwaggerDoc() map[string]string {

View file

@ -1,134 +0,0 @@
/*
Copyright 2016 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package v1
import (
"encoding/json"
"reflect"
"testing"
"github.com/ugorji/go/codec"
)
func TestVerbsUgorjiMarshalJSON(t *testing.T) {
cases := []struct {
input APIResource
result string
}{
{APIResource{}, `{"name":"","namespaced":false,"kind":"","verbs":null}`},
{APIResource{Verbs: Verbs([]string{})}, `{"name":"","namespaced":false,"kind":"","verbs":[]}`},
{APIResource{Verbs: Verbs([]string{"delete"})}, `{"name":"","namespaced":false,"kind":"","verbs":["delete"]}`},
}
for i, c := range cases {
result, err := json.Marshal(&c.input)
if err != nil {
t.Errorf("[%d] Failed to marshal input: '%v': %v", i, c.input, err)
}
if string(result) != c.result {
t.Errorf("[%d] Failed to marshal input: '%v': expected '%v', got '%v'", i, c.input, c.result, string(result))
}
}
}
func TestVerbsUgorjiUnmarshalJSON(t *testing.T) {
cases := []struct {
input string
result APIResource
}{
{`{}`, APIResource{}},
{`{"verbs":null}`, APIResource{}},
{`{"verbs":[]}`, APIResource{Verbs: Verbs([]string{})}},
{`{"verbs":["delete"]}`, APIResource{Verbs: Verbs([]string{"delete"})}},
}
for i, c := range cases {
var result APIResource
if err := codec.NewDecoderBytes([]byte(c.input), new(codec.JsonHandle)).Decode(&result); err != nil {
t.Errorf("[%d] Failed to unmarshal input '%v': %v", i, c.input, err)
}
if !reflect.DeepEqual(result, c.result) {
t.Errorf("[%d] Failed to unmarshal input '%v': expected %+v, got %+v", i, c.input, c.result, result)
}
}
}
// TestUgorjiMarshalJSONWithOmit tests that we don't have regressions regarding nil and empty slices with "omit"
func TestUgorjiMarshalJSONWithOmit(t *testing.T) {
cases := []struct {
input LabelSelector
result string
}{
{LabelSelector{}, `{}`},
{LabelSelector{MatchExpressions: []LabelSelectorRequirement{}}, `{}`},
{LabelSelector{MatchExpressions: []LabelSelectorRequirement{{}}}, `{"matchExpressions":[{"key":"","operator":""}]}`},
}
for i, c := range cases {
result, err := json.Marshal(&c.input)
if err != nil {
t.Errorf("[%d] Failed to marshal input: '%v': %v", i, c.input, err)
}
if string(result) != c.result {
t.Errorf("[%d] Failed to marshal input: '%v': expected '%v', got '%v'", i, c.input, c.result, string(result))
}
}
}
func TestVerbsUnmarshalJSON(t *testing.T) {
cases := []struct {
input string
result APIResource
}{
{`{}`, APIResource{}},
{`{"verbs":null}`, APIResource{}},
{`{"verbs":[]}`, APIResource{Verbs: Verbs([]string{})}},
{`{"verbs":["delete"]}`, APIResource{Verbs: Verbs([]string{"delete"})}},
}
for i, c := range cases {
var result APIResource
if err := json.Unmarshal([]byte(c.input), &result); err != nil {
t.Errorf("[%d] Failed to unmarshal input '%v': %v", i, c.input, err)
}
if !reflect.DeepEqual(result, c.result) {
t.Errorf("[%d] Failed to unmarshal input '%v': expected %+v, got %+v", i, c.input, c.result, result)
}
}
}
func TestVerbsProto(t *testing.T) {
cases := []APIResource{
{},
{Verbs: Verbs([]string{})},
{Verbs: Verbs([]string{"delete"})},
}
for _, input := range cases {
data, err := input.Marshal()
if err != nil {
t.Fatalf("Failed to marshal input: '%v': %v", input, err)
}
resource := APIResource{}
if err := resource.Unmarshal(data); err != nil {
t.Fatalf("Failed to unmarshal output: '%v': %v", input, err)
}
if !reflect.DeepEqual(input, resource) {
t.Errorf("Marshal->Unmarshal is not idempotent: '%v' vs '%v'", input, resource)
}
}
}

View file

@ -73,11 +73,24 @@ func (obj *Unstructured) UnstructuredContent() map[string]interface{} {
}
return obj.Object
}
// UnstructuredContent returns a map contain an overlay of the Items field onto
// the Object field. Items always overwrites overlay. Changing "items" in the
// returned object will affect items in the underlying Items field, but changing
// the "items" slice itself will have no effect.
// TODO: expose SetUnstructuredContent on runtime.Unstructured that allows
// items to be changed.
func (obj *UnstructuredList) UnstructuredContent() map[string]interface{} {
if obj.Object == nil {
obj.Object = make(map[string]interface{})
out := obj.Object
if out == nil {
out = make(map[string]interface{})
}
return obj.Object
items := make([]interface{}, len(obj.Items))
for i, item := range obj.Items {
items[i] = item.Object
}
out["items"] = items
return out
}
// MarshalJSON ensures that the unstructured object produces proper
@ -191,21 +204,31 @@ func (u *Unstructured) setNestedMap(value map[string]string, fields ...string) {
func extractOwnerReference(src interface{}) metav1.OwnerReference {
v := src.(map[string]interface{})
controllerPtr, ok := (getNestedField(v, "controller")).(*bool)
// though this field is a *bool, but when decoded from JSON, it's
// unmarshalled as bool.
var controllerPtr *bool
controller, ok := (getNestedField(v, "controller")).(bool)
if !ok {
controllerPtr = nil
} else {
if controllerPtr != nil {
controller := *controllerPtr
controllerPtr = &controller
}
controllerCopy := controller
controllerPtr = &controllerCopy
}
var blockOwnerDeletionPtr *bool
blockOwnerDeletion, ok := (getNestedField(v, "blockOwnerDeletion")).(bool)
if !ok {
blockOwnerDeletionPtr = nil
} else {
blockOwnerDeletionCopy := blockOwnerDeletion
blockOwnerDeletionPtr = &blockOwnerDeletionCopy
}
return metav1.OwnerReference{
Kind: getNestedString(v, "kind"),
Name: getNestedString(v, "name"),
APIVersion: getNestedString(v, "apiVersion"),
UID: (types.UID)(getNestedString(v, "uid")),
Controller: controllerPtr,
Kind: getNestedString(v, "kind"),
Name: getNestedString(v, "name"),
APIVersion: getNestedString(v, "apiVersion"),
UID: (types.UID)(getNestedString(v, "uid")),
Controller: controllerPtr,
BlockOwnerDeletion: blockOwnerDeletionPtr,
}
}
@ -216,11 +239,17 @@ func setOwnerReference(src metav1.OwnerReference) map[string]interface{} {
controller := *controllerPtr
controllerPtr = &controller
}
blockOwnerDeletionPtr := src.BlockOwnerDeletion
if blockOwnerDeletionPtr != nil {
blockOwnerDeletion := *blockOwnerDeletionPtr
blockOwnerDeletionPtr = &blockOwnerDeletion
}
setNestedField(ret, src.Kind, "kind")
setNestedField(ret, src.Name, "name")
setNestedField(ret, src.APIVersion, "apiVersion")
setNestedField(ret, string(src.UID), "uid")
setNestedField(ret, controllerPtr, "controller")
setNestedField(ret, blockOwnerDeletionPtr, "blockOwnerDeletion")
return ret
}

View file

@ -1,74 +0,0 @@
/*
Copyright 2015 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package validation
import (
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/validation"
"k8s.io/apimachinery/pkg/util/validation/field"
)
func ValidateLabelSelector(ps *metav1.LabelSelector, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
if ps == nil {
return allErrs
}
allErrs = append(allErrs, ValidateLabels(ps.MatchLabels, fldPath.Child("matchLabels"))...)
for i, expr := range ps.MatchExpressions {
allErrs = append(allErrs, ValidateLabelSelectorRequirement(expr, fldPath.Child("matchExpressions").Index(i))...)
}
return allErrs
}
func ValidateLabelSelectorRequirement(sr metav1.LabelSelectorRequirement, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
switch sr.Operator {
case metav1.LabelSelectorOpIn, metav1.LabelSelectorOpNotIn:
if len(sr.Values) == 0 {
allErrs = append(allErrs, field.Required(fldPath.Child("values"), "must be specified when `operator` is 'In' or 'NotIn'"))
}
case metav1.LabelSelectorOpExists, metav1.LabelSelectorOpDoesNotExist:
if len(sr.Values) > 0 {
allErrs = append(allErrs, field.Forbidden(fldPath.Child("values"), "may not be specified when `operator` is 'Exists' or 'DoesNotExist'"))
}
default:
allErrs = append(allErrs, field.Invalid(fldPath.Child("operator"), sr.Operator, "not a valid selector operator"))
}
allErrs = append(allErrs, ValidateLabelName(sr.Key, fldPath.Child("key"))...)
return allErrs
}
// ValidateLabelName validates that the label name is correctly defined.
func ValidateLabelName(labelName string, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
for _, msg := range validation.IsQualifiedName(labelName) {
allErrs = append(allErrs, field.Invalid(fldPath, labelName, msg))
}
return allErrs
}
// ValidateLabels validates that a set of labels are correctly defined.
func ValidateLabels(labels map[string]string, fldPath *field.Path) field.ErrorList {
allErrs := field.ErrorList{}
for k, v := range labels {
allErrs = append(allErrs, ValidateLabelName(k, fldPath)...)
for _, msg := range validation.IsValidLabelValue(v) {
allErrs = append(allErrs, field.Invalid(fldPath, v, msg))
}
}
return allErrs
}

View file

@ -1,94 +0,0 @@
/*
Copyright 2016 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package validation
import (
"strings"
"testing"
"k8s.io/apimachinery/pkg/util/validation/field"
)
func TestValidateLabels(t *testing.T) {
successCases := []map[string]string{
{"simple": "bar"},
{"now-with-dashes": "bar"},
{"1-starts-with-num": "bar"},
{"1234": "bar"},
{"simple/simple": "bar"},
{"now-with-dashes/simple": "bar"},
{"now-with-dashes/now-with-dashes": "bar"},
{"now.with.dots/simple": "bar"},
{"now-with.dashes-and.dots/simple": "bar"},
{"1-num.2-num/3-num": "bar"},
{"1234/5678": "bar"},
{"1.2.3.4/5678": "bar"},
{"UpperCaseAreOK123": "bar"},
{"goodvalue": "123_-.BaR"},
}
for i := range successCases {
errs := ValidateLabels(successCases[i], field.NewPath("field"))
if len(errs) != 0 {
t.Errorf("case[%d] expected success, got %#v", i, errs)
}
}
namePartErrMsg := "name part must consist of"
nameErrMsg := "a qualified name must consist of"
labelErrMsg := "a valid label must be an empty string or consist of"
maxLengthErrMsg := "must be no more than"
labelNameErrorCases := []struct {
labels map[string]string
expect string
}{
{map[string]string{"nospecialchars^=@": "bar"}, namePartErrMsg},
{map[string]string{"cantendwithadash-": "bar"}, namePartErrMsg},
{map[string]string{"only/one/slash": "bar"}, nameErrMsg},
{map[string]string{strings.Repeat("a", 254): "bar"}, maxLengthErrMsg},
}
for i := range labelNameErrorCases {
errs := ValidateLabels(labelNameErrorCases[i].labels, field.NewPath("field"))
if len(errs) != 1 {
t.Errorf("case[%d]: expected failure", i)
} else {
if !strings.Contains(errs[0].Detail, labelNameErrorCases[i].expect) {
t.Errorf("case[%d]: error details do not include %q: %q", i, labelNameErrorCases[i].expect, errs[0].Detail)
}
}
}
labelValueErrorCases := []struct {
labels map[string]string
expect string
}{
{map[string]string{"toolongvalue": strings.Repeat("a", 64)}, maxLengthErrMsg},
{map[string]string{"backslashesinvalue": "some\\bad\\value"}, labelErrMsg},
{map[string]string{"nocommasallowed": "bad,value"}, labelErrMsg},
{map[string]string{"strangecharsinvalue": "?#$notsogood"}, labelErrMsg},
}
for i := range labelValueErrorCases {
errs := ValidateLabels(labelValueErrorCases[i].labels, field.NewPath("field"))
if len(errs) != 1 {
t.Errorf("case[%d]: expected failure", i)
} else {
if !strings.Contains(errs[0].Detail, labelValueErrorCases[i].expect) {
t.Errorf("case[%d]: error details do not include %q: %q", i, labelValueErrorCases[i].expect, errs[0].Detail)
}
}
}
}

View file

@ -34,12 +34,12 @@ const (
// fluentd in not running as a manifest pod with LabelFluentdDsReady.
LabelFluentdDsReady = "alpha.kubernetes.io/fluentd-ds-ready"
// When the --use-taint-based-evictions flag is enabled,
// When feature-gate for TaintBasedEvictions=true flag is enabled,
// TaintNodeNotReady would be automatically added by node controller
// when node is not ready, and removed when node becomes ready.
TaintNodeNotReady = "node.alpha.kubernetes.io/notReady"
// When the --use-taint-based-evictions flag is enabled,
// When feature-gate for TaintBasedEvictions=true flag is enabled,
// TaintNodeUnreachable would be automatically added by node controller
// when node becomes unreachable (corresponding to NodeReady status ConditionUnknown)
// and removed when node becomes reachable (NodeReady status ConditionTrue).

View file

@ -186,6 +186,11 @@ func DeepCopy_v1_DeleteOptions(in interface{}, out interface{}, c *conversion.Cl
*out = new(bool)
**out = **in
}
if in.PropagationPolicy != nil {
in, out := &in.PropagationPolicy, &out.PropagationPolicy
*out = new(DeletionPropagation)
**out = **in
}
return nil
}
}
@ -412,6 +417,11 @@ func DeepCopy_v1_OwnerReference(in interface{}, out interface{}, c *conversion.C
*out = new(bool)
**out = **in
}
if in.BlockOwnerDeletion != nil {
in, out := &in.BlockOwnerDeletion, &out.BlockOwnerDeletion
*out = new(bool)
**out = **in
}
return nil
}
}

View file

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

View file

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

View file

@ -1,161 +0,0 @@
/*
Copyright 2015 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package conversion
import (
"math/rand"
"reflect"
"testing"
"github.com/google/gofuzz"
)
func TestDeepCopy(t *testing.T) {
semantic := EqualitiesOrDie()
f := fuzz.New().NilChance(.5).NumElements(0, 100)
table := []interface{}{
map[string]string{},
int(5),
"hello world",
struct {
A, B, C struct {
D map[string]int
}
X []int
Y []byte
}{},
}
for _, obj := range table {
obj2, err := NewCloner().DeepCopy(obj)
if err != nil {
t.Errorf("Error: couldn't copy %#v", obj)
continue
}
if e, a := obj, obj2; !semantic.DeepEqual(e, a) {
t.Errorf("expected %#v\ngot %#v", e, a)
}
obj3 := reflect.New(reflect.TypeOf(obj)).Interface()
f.Fuzz(obj3)
obj4, err := NewCloner().DeepCopy(obj3)
if err != nil {
t.Errorf("Error: couldn't copy %#v", obj)
continue
}
if e, a := obj3, obj4; !semantic.DeepEqual(e, a) {
t.Errorf("expected %#v\ngot %#v", e, a)
}
f.Fuzz(obj3)
}
}
func copyOrDie(t *testing.T, in interface{}) interface{} {
out, err := NewCloner().DeepCopy(in)
if err != nil {
t.Fatalf("DeepCopy failed: %#q: %v", in, err)
}
return out
}
func TestDeepCopySliceSeparate(t *testing.T) {
x := []int{5}
y := copyOrDie(t, x).([]int)
x[0] = 3
if y[0] == 3 {
t.Errorf("deep copy wasn't deep: %#q %#q", x, y)
}
}
func TestDeepCopyArraySeparate(t *testing.T) {
x := [1]int{5}
y := copyOrDie(t, x).([1]int)
x[0] = 3
if y[0] == 3 {
t.Errorf("deep copy wasn't deep: %#q %#q", x, y)
}
}
func TestDeepCopyMapSeparate(t *testing.T) {
x := map[string]int{"foo": 5}
y := copyOrDie(t, x).(map[string]int)
x["foo"] = 3
if y["foo"] == 3 {
t.Errorf("deep copy wasn't deep: %#q %#q", x, y)
}
}
func TestDeepCopyPointerSeparate(t *testing.T) {
z := 5
x := &z
y := copyOrDie(t, x).(*int)
*x = 3
if *y == 3 {
t.Errorf("deep copy wasn't deep: %#q %#q", x, y)
}
}
func TestDeepCopyStruct(t *testing.T) {
type Foo struct {
A int
}
type Bar struct {
Foo
F *Foo
}
a := &Bar{Foo{1}, &Foo{2}}
b := copyOrDie(t, a).(*Bar)
a.A = 3
a.F.A = 4
if b.A != 1 || b.F.A != 2 {
t.Errorf("deep copy wasn't deep: %#v, %#v", a, b)
}
}
var result interface{}
func BenchmarkDeepCopy(b *testing.B) {
table := []interface{}{
map[string]string{},
int(5),
"hello world",
struct {
A, B, C struct {
D map[string]int
}
X []int
Y []byte
}{},
}
f := fuzz.New().RandSource(rand.NewSource(1)).NilChance(.5).NumElements(0, 100)
for i := range table {
out := table[i]
obj := reflect.New(reflect.TypeOf(out)).Interface()
f.Fuzz(obj)
table[i] = obj
}
b.ResetTimer()
var r interface{}
for i := 0; i < b.N; i++ {
for j := range table {
r, _ = NewCloner().DeepCopy(table[j])
}
}
result = r
}

View file

@ -1,38 +0,0 @@
/*
Copyright 2014 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package conversion
import "testing"
func TestInvalidPtrValueKind(t *testing.T) {
var simple interface{}
switch obj := simple.(type) {
default:
_, err := EnforcePtr(obj)
if err == nil {
t.Errorf("Expected error on invalid kind")
}
}
}
func TestEnforceNilPtr(t *testing.T) {
var nilPtr *struct{}
_, err := EnforcePtr(nilPtr)
if err == nil {
t.Errorf("Expected error on nil pointer")
}
}

View file

@ -1,212 +0,0 @@
/*
Copyright 2014 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package queryparams_test
import (
"net/url"
"reflect"
"testing"
"time"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/conversion/queryparams"
"k8s.io/apimachinery/pkg/runtime/schema"
)
type namedString string
type namedBool bool
type bar struct {
Float1 float32 `json:"float1"`
Float2 float64 `json:"float2"`
Int1 int64 `json:"int1,omitempty"`
Int2 int32 `json:"int2,omitempty"`
Int3 int16 `json:"int3,omitempty"`
Str1 string `json:"str1,omitempty"`
Ignored int
Ignored2 string
}
func (obj *bar) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind }
type foo struct {
Str string `json:"str"`
Integer int `json:"integer,omitempty"`
Slice []string `json:"slice,omitempty"`
Boolean bool `json:"boolean,omitempty"`
NamedStr namedString `json:"namedStr,omitempty"`
NamedBool namedBool `json:"namedBool,omitempty"`
Foobar bar `json:"foobar,omitempty"`
Testmap map[string]string `json:"testmap,omitempty"`
}
func (obj *foo) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind }
type baz struct {
Ptr *int `json:"ptr"`
Bptr *bool `json:"bptr,omitempty"`
}
func (obj *baz) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind }
// childStructs tests some of the types we serialize to query params for log API calls
// notably, the nested time struct
type childStructs struct {
Container string `json:"container,omitempty"`
Follow bool `json:"follow,omitempty"`
Previous bool `json:"previous,omitempty"`
SinceSeconds *int64 `json:"sinceSeconds,omitempty"`
SinceTime *metav1.Time `json:"sinceTime,omitempty"`
EmptyTime *metav1.Time `json:"emptyTime"`
}
func (obj *childStructs) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind }
func validateResult(t *testing.T, input interface{}, actual, expected url.Values) {
local := url.Values{}
for k, v := range expected {
local[k] = v
}
for k, v := range actual {
if ev, ok := local[k]; !ok || !reflect.DeepEqual(ev, v) {
if !ok {
t.Errorf("%#v: actual value key %s not found in expected map", input, k)
} else {
t.Errorf("%#v: values don't match: actual: %#v, expected: %#v", input, v, ev)
}
}
delete(local, k)
}
if len(local) > 0 {
t.Errorf("%#v: expected map has keys that were not found in actual map: %#v", input, local)
}
}
func TestConvert(t *testing.T) {
sinceSeconds := int64(123)
sinceTime := metav1.Date(2000, 1, 1, 12, 34, 56, 0, time.UTC)
tests := []struct {
input interface{}
expected url.Values
}{
{
input: &foo{
Str: "hello",
},
expected: url.Values{"str": {"hello"}},
},
{
input: &foo{
Str: "test string",
Slice: []string{"one", "two", "three"},
Integer: 234,
Boolean: true,
},
expected: url.Values{"str": {"test string"}, "slice": {"one", "two", "three"}, "integer": {"234"}, "boolean": {"true"}},
},
{
input: &foo{
Str: "named types",
NamedStr: "value1",
NamedBool: true,
},
expected: url.Values{"str": {"named types"}, "namedStr": {"value1"}, "namedBool": {"true"}},
},
{
input: &foo{
Str: "don't ignore embedded struct",
Foobar: bar{
Float1: 5.0,
},
},
expected: url.Values{"str": {"don't ignore embedded struct"}, "float1": {"5"}, "float2": {"0"}},
},
{
// Ignore untagged fields
input: &bar{
Float1: 23.5,
Float2: 100.7,
Int1: 1,
Int2: 2,
Int3: 3,
Ignored: 1,
Ignored2: "ignored",
},
expected: url.Values{"float1": {"23.5"}, "float2": {"100.7"}, "int1": {"1"}, "int2": {"2"}, "int3": {"3"}},
},
{
// include fields that are not tagged omitempty
input: &foo{
NamedStr: "named str",
},
expected: url.Values{"str": {""}, "namedStr": {"named str"}},
},
{
input: &baz{
Ptr: intp(5),
Bptr: boolp(true),
},
expected: url.Values{"ptr": {"5"}, "bptr": {"true"}},
},
{
input: &baz{
Bptr: boolp(true),
},
expected: url.Values{"ptr": {""}, "bptr": {"true"}},
},
{
input: &baz{
Ptr: intp(5),
},
expected: url.Values{"ptr": {"5"}},
},
{
input: &childStructs{
Container: "mycontainer",
Follow: true,
Previous: true,
SinceSeconds: &sinceSeconds,
SinceTime: &sinceTime, // test a custom marshaller
EmptyTime: nil, // test a nil custom marshaller without omitempty
},
expected: url.Values{"container": {"mycontainer"}, "follow": {"true"}, "previous": {"true"}, "sinceSeconds": {"123"}, "sinceTime": {"2000-01-01T12:34:56Z"}, "emptyTime": {""}},
},
{
input: &childStructs{
Container: "mycontainer",
Follow: true,
Previous: true,
SinceSeconds: &sinceSeconds,
SinceTime: nil, // test a nil custom marshaller with omitempty
},
expected: url.Values{"container": {"mycontainer"}, "follow": {"true"}, "previous": {"true"}, "sinceSeconds": {"123"}, "emptyTime": {""}},
},
}
for _, test := range tests {
result, err := queryparams.Convert(test.input)
if err != nil {
t.Errorf("Unexpected error while converting %#v: %v", test.input, err)
}
validateResult(t, test.input, result, test.expected)
}
}
func intp(n int) *int { return &n }
func boolp(b bool) *bool { return &b }

View file

@ -1,57 +0,0 @@
/*
Copyright 2015 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package fields
import (
"testing"
)
func matches(t *testing.T, ls Set, want string) {
if ls.String() != want {
t.Errorf("Expected '%s', but got '%s'", want, ls.String())
}
}
func TestSetString(t *testing.T) {
matches(t, Set{"x": "y"}, "x=y")
matches(t, Set{"foo": "bar"}, "foo=bar")
matches(t, Set{"foo": "bar", "baz": "qup"}, "baz=qup,foo=bar")
}
func TestFieldHas(t *testing.T) {
fieldHasTests := []struct {
Ls Fields
Key string
Has bool
}{
{Set{"x": "y"}, "x", true},
{Set{"x": ""}, "x", true},
{Set{"x": "y"}, "foo", false},
}
for _, lh := range fieldHasTests {
if has := lh.Ls.Has(lh.Key); has != lh.Has {
t.Errorf("%#v.Has(%#v) => %v, expected %v", lh.Ls, lh.Key, has, lh.Has)
}
}
}
func TestFieldGet(t *testing.T) {
ls := Set{"x": "y"}
if ls.Get("x") != "y" {
t.Errorf("Set.Get is broken")
}
}

View file

@ -406,3 +406,8 @@ func parseSelector(selector string, fn TransformFunc) (Selector, error) {
func OneTermEqualSelector(k, v string) Selector {
return &hasTerm{field: k, value: v}
}
// AndSelectors creates a selector that is the logical AND of all the given selectors
func AndSelectors(selectors ...Selector) Selector {
return andTerm(selectors)
}

View file

@ -1,327 +0,0 @@
/*
Copyright 2015 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package fields
import (
"reflect"
"testing"
)
func TestSplitTerms(t *testing.T) {
testcases := map[string][]string{
// Simple selectors
`a`: {`a`},
`a=avalue`: {`a=avalue`},
`a=avalue,b=bvalue`: {`a=avalue`, `b=bvalue`},
`a=avalue,b==bvalue,c!=cvalue`: {`a=avalue`, `b==bvalue`, `c!=cvalue`},
// Empty terms
``: nil,
`a=a,`: {`a=a`, ``},
`,a=a`: {``, `a=a`},
// Escaped values
`k=\,,k2=v2`: {`k=\,`, `k2=v2`}, // escaped comma in value
`k=\\,k2=v2`: {`k=\\`, `k2=v2`}, // escaped backslash, unescaped comma
`k=\\\,,k2=v2`: {`k=\\\,`, `k2=v2`}, // escaped backslash and comma
`k=\a\b\`: {`k=\a\b\`}, // non-escape sequences
`k=\`: {`k=\`}, // orphan backslash
// Multi-byte
`함=수,목=록`: {`함=수`, `목=록`},
}
for selector, expectedTerms := range testcases {
if terms := splitTerms(selector); !reflect.DeepEqual(terms, expectedTerms) {
t.Errorf("splitSelectors(`%s`): Expected\n%#v\ngot\n%#v", selector, expectedTerms, terms)
}
}
}
func TestSplitTerm(t *testing.T) {
testcases := map[string]struct {
lhs string
op string
rhs string
ok bool
}{
// Simple terms
`a=value`: {lhs: `a`, op: `=`, rhs: `value`, ok: true},
`b==value`: {lhs: `b`, op: `==`, rhs: `value`, ok: true},
`c!=value`: {lhs: `c`, op: `!=`, rhs: `value`, ok: true},
// Empty or invalid terms
``: {lhs: ``, op: ``, rhs: ``, ok: false},
`a`: {lhs: ``, op: ``, rhs: ``, ok: false},
// Escaped values
`k=\,`: {lhs: `k`, op: `=`, rhs: `\,`, ok: true},
`k=\=`: {lhs: `k`, op: `=`, rhs: `\=`, ok: true},
`k=\\\a\b\=\,\`: {lhs: `k`, op: `=`, rhs: `\\\a\b\=\,\`, ok: true},
// Multi-byte
`함=수`: {lhs: ``, op: `=`, rhs: ``, ok: true},
}
for term, expected := range testcases {
lhs, op, rhs, ok := splitTerm(term)
if lhs != expected.lhs || op != expected.op || rhs != expected.rhs || ok != expected.ok {
t.Errorf(
"splitTerm(`%s`): Expected\n%s,%s,%s,%v\nGot\n%s,%s,%s,%v",
term,
expected.lhs, expected.op, expected.rhs, expected.ok,
lhs, op, rhs, ok,
)
}
}
}
func TestEscapeValue(t *testing.T) {
// map values to their normalized escaped values
testcases := map[string]string{
``: ``,
`a`: `a`,
`=`: `\=`,
`,`: `\,`,
`\`: `\\`,
`\=\,\`: `\\\=\\\,\\`,
}
for unescapedValue, escapedValue := range testcases {
actualEscaped := EscapeValue(unescapedValue)
if actualEscaped != escapedValue {
t.Errorf("EscapeValue(%s): expected %s, got %s", unescapedValue, escapedValue, actualEscaped)
}
actualUnescaped, err := UnescapeValue(escapedValue)
if err != nil {
t.Errorf("UnescapeValue(%s): unexpected error %v", escapedValue, err)
}
if actualUnescaped != unescapedValue {
t.Errorf("UnescapeValue(%s): expected %s, got %s", escapedValue, unescapedValue, actualUnescaped)
}
}
// test invalid escape sequences
invalidTestcases := []string{
`\`, // orphan slash is invalid
`\\\`, // orphan slash is invalid
`\a`, // unrecognized escape sequence is invalid
}
for _, invalidValue := range invalidTestcases {
_, err := UnescapeValue(invalidValue)
if _, ok := err.(InvalidEscapeSequence); !ok || err == nil {
t.Errorf("UnescapeValue(%s): expected invalid escape sequence error, got %#v", invalidValue, err)
}
}
}
func TestSelectorParse(t *testing.T) {
testGoodStrings := []string{
"x=a,y=b,z=c",
"",
"x!=a,y=b",
`x=a||y\=b`,
`x=a\=\=b`,
}
testBadStrings := []string{
"x=a||y=b",
"x==a==b",
"x=a,b",
"x in (a)",
"x in (a,b,c)",
"x",
}
for _, test := range testGoodStrings {
lq, err := ParseSelector(test)
if err != nil {
t.Errorf("%v: error %v (%#v)\n", test, err, err)
}
if test != lq.String() {
t.Errorf("%v restring gave: %v\n", test, lq.String())
}
}
for _, test := range testBadStrings {
_, err := ParseSelector(test)
if err == nil {
t.Errorf("%v: did not get expected error\n", test)
}
}
}
func TestDeterministicParse(t *testing.T) {
s1, err := ParseSelector("x=a,a=x")
s2, err2 := ParseSelector("a=x,x=a")
if err != nil || err2 != nil {
t.Errorf("Unexpected parse error")
}
if s1.String() != s2.String() {
t.Errorf("Non-deterministic parse")
}
}
func expectMatch(t *testing.T, selector string, ls Set) {
lq, err := ParseSelector(selector)
if err != nil {
t.Errorf("Unable to parse %v as a selector\n", selector)
return
}
if !lq.Matches(ls) {
t.Errorf("Wanted %s to match '%s', but it did not.\n", selector, ls)
}
}
func expectNoMatch(t *testing.T, selector string, ls Set) {
lq, err := ParseSelector(selector)
if err != nil {
t.Errorf("Unable to parse %v as a selector\n", selector)
return
}
if lq.Matches(ls) {
t.Errorf("Wanted '%s' to not match '%s', but it did.", selector, ls)
}
}
func TestEverything(t *testing.T) {
if !Everything().Matches(Set{"x": "y"}) {
t.Errorf("Nil selector didn't match")
}
if !Everything().Empty() {
t.Errorf("Everything was not empty")
}
}
func TestSelectorMatches(t *testing.T) {
expectMatch(t, "", Set{"x": "y"})
expectMatch(t, "x=y", Set{"x": "y"})
expectMatch(t, "x=y,z=w", Set{"x": "y", "z": "w"})
expectMatch(t, "x!=y,z!=w", Set{"x": "z", "z": "a"})
expectMatch(t, "notin=in", Set{"notin": "in"}) // in and notin in exactMatch
expectNoMatch(t, "x=y", Set{"x": "z"})
expectNoMatch(t, "x=y,z=w", Set{"x": "w", "z": "w"})
expectNoMatch(t, "x!=y,z!=w", Set{"x": "z", "z": "w"})
fieldset := Set{
"foo": "bar",
"baz": "blah",
"complex": `=value\,\`,
}
expectMatch(t, "foo=bar", fieldset)
expectMatch(t, "baz=blah", fieldset)
expectMatch(t, "foo=bar,baz=blah", fieldset)
expectMatch(t, `foo=bar,baz=blah,complex=\=value\\\,\\`, fieldset)
expectNoMatch(t, "foo=blah", fieldset)
expectNoMatch(t, "baz=bar", fieldset)
expectNoMatch(t, "foo=bar,foobar=bar,baz=blah", fieldset)
}
func TestOneTermEqualSelector(t *testing.T) {
if !OneTermEqualSelector("x", "y").Matches(Set{"x": "y"}) {
t.Errorf("No match when match expected.")
}
if OneTermEqualSelector("x", "y").Matches(Set{"x": "z"}) {
t.Errorf("Match when none expected.")
}
}
func expectMatchDirect(t *testing.T, selector, ls Set) {
if !SelectorFromSet(selector).Matches(ls) {
t.Errorf("Wanted %s to match '%s', but it did not.\n", selector, ls)
}
}
func expectNoMatchDirect(t *testing.T, selector, ls Set) {
if SelectorFromSet(selector).Matches(ls) {
t.Errorf("Wanted '%s' to not match '%s', but it did.", selector, ls)
}
}
func TestSetMatches(t *testing.T) {
labelset := Set{
"foo": "bar",
"baz": "blah",
}
expectMatchDirect(t, Set{}, labelset)
expectMatchDirect(t, Set{"foo": "bar"}, labelset)
expectMatchDirect(t, Set{"baz": "blah"}, labelset)
expectMatchDirect(t, Set{"foo": "bar", "baz": "blah"}, labelset)
expectNoMatchDirect(t, Set{"foo": "=blah"}, labelset)
expectNoMatchDirect(t, Set{"baz": "=bar"}, labelset)
expectNoMatchDirect(t, Set{"foo": "=bar", "foobar": "bar", "baz": "blah"}, labelset)
}
func TestNilMapIsValid(t *testing.T) {
selector := Set(nil).AsSelector()
if selector == nil {
t.Errorf("Selector for nil set should be Everything")
}
if !selector.Empty() {
t.Errorf("Selector for nil set should be Empty")
}
}
func TestSetIsEmpty(t *testing.T) {
if !(Set{}).AsSelector().Empty() {
t.Errorf("Empty set should be empty")
}
if !(andTerm(nil)).Empty() {
t.Errorf("Nil andTerm should be empty")
}
if (&hasTerm{}).Empty() {
t.Errorf("hasTerm should not be empty")
}
if (&notHasTerm{}).Empty() {
t.Errorf("notHasTerm should not be empty")
}
if !(andTerm{andTerm{}}).Empty() {
t.Errorf("Nested andTerm should be empty")
}
if (andTerm{&hasTerm{"a", "b"}}).Empty() {
t.Errorf("Nested andTerm should not be empty")
}
}
func TestRequiresExactMatch(t *testing.T) {
testCases := map[string]struct {
S Selector
Label string
Value string
Found bool
}{
"empty set": {Set{}.AsSelector(), "test", "", false},
"empty hasTerm": {&hasTerm{}, "test", "", false},
"skipped hasTerm": {&hasTerm{"a", "b"}, "test", "", false},
"valid hasTerm": {&hasTerm{"test", "b"}, "test", "b", true},
"valid hasTerm no value": {&hasTerm{"test", ""}, "test", "", true},
"valid notHasTerm": {&notHasTerm{"test", "b"}, "test", "", false},
"valid notHasTerm no value": {&notHasTerm{"test", ""}, "test", "", false},
"nil andTerm": {andTerm(nil), "test", "", false},
"empty andTerm": {andTerm{}, "test", "", false},
"nested andTerm": {andTerm{andTerm{}}, "test", "", false},
"nested andTerm matches": {andTerm{&hasTerm{"test", "b"}}, "test", "b", true},
"andTerm with non-match": {andTerm{&hasTerm{}, &hasTerm{"test", "b"}}, "test", "b", true},
}
for k, v := range testCases {
value, found := v.S.RequiresExactMatch(v.Label)
if value != v.Value {
t.Errorf("%s: expected value %s, got %s", k, v.Value, value)
}
if found != v.Found {
t.Errorf("%s: expected found %t, got %t", k, v.Found, found)
}
}
}

View file

@ -1,231 +0,0 @@
/*
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 labels
import (
"testing"
)
func matches(t *testing.T, ls Set, want string) {
if ls.String() != want {
t.Errorf("Expected '%s', but got '%s'", want, ls.String())
}
}
func TestSetString(t *testing.T) {
matches(t, Set{"x": "y"}, "x=y")
matches(t, Set{"foo": "bar"}, "foo=bar")
matches(t, Set{"foo": "bar", "baz": "qup"}, "baz=qup,foo=bar")
// TODO: Make our label representation robust enough to handle labels
// with ",=!" characters in their names.
}
func TestLabelHas(t *testing.T) {
labelHasTests := []struct {
Ls Labels
Key string
Has bool
}{
{Set{"x": "y"}, "x", true},
{Set{"x": ""}, "x", true},
{Set{"x": "y"}, "foo", false},
}
for _, lh := range labelHasTests {
if has := lh.Ls.Has(lh.Key); has != lh.Has {
t.Errorf("%#v.Has(%#v) => %v, expected %v", lh.Ls, lh.Key, has, lh.Has)
}
}
}
func TestLabelGet(t *testing.T) {
ls := Set{"x": "y"}
if ls.Get("x") != "y" {
t.Errorf("Set.Get is broken")
}
}
func TestLabelConflict(t *testing.T) {
tests := []struct {
labels1 map[string]string
labels2 map[string]string
conflict bool
}{
{
labels1: map[string]string{},
labels2: map[string]string{},
conflict: false,
},
{
labels1: map[string]string{"env": "test"},
labels2: map[string]string{"infra": "true"},
conflict: false,
},
{
labels1: map[string]string{"env": "test"},
labels2: map[string]string{"infra": "true", "env": "test"},
conflict: false,
},
{
labels1: map[string]string{"env": "test"},
labels2: map[string]string{"env": "dev"},
conflict: true,
},
{
labels1: map[string]string{"env": "test", "infra": "false"},
labels2: map[string]string{"infra": "true", "color": "blue"},
conflict: true,
},
}
for _, test := range tests {
conflict := Conflicts(Set(test.labels1), Set(test.labels2))
if conflict != test.conflict {
t.Errorf("expected: %v but got: %v", test.conflict, conflict)
}
}
}
func TestLabelMerge(t *testing.T) {
tests := []struct {
labels1 map[string]string
labels2 map[string]string
mergedLabels map[string]string
}{
{
labels1: map[string]string{},
labels2: map[string]string{},
mergedLabels: map[string]string{},
},
{
labels1: map[string]string{"infra": "true"},
labels2: map[string]string{},
mergedLabels: map[string]string{"infra": "true"},
},
{
labels1: map[string]string{"infra": "true"},
labels2: map[string]string{"env": "test", "color": "blue"},
mergedLabels: map[string]string{"infra": "true", "env": "test", "color": "blue"},
},
}
for _, test := range tests {
mergedLabels := Merge(Set(test.labels1), Set(test.labels2))
if !Equals(mergedLabels, test.mergedLabels) {
t.Errorf("expected: %v but got: %v", test.mergedLabels, mergedLabels)
}
}
}
func TestLabelSelectorParse(t *testing.T) {
tests := []struct {
selector string
labels map[string]string
valid bool
}{
{
selector: "",
labels: map[string]string{},
valid: true,
},
{
selector: "x=a",
labels: map[string]string{"x": "a"},
valid: true,
},
{
selector: "x=a,y=b,z=c",
labels: map[string]string{"x": "a", "y": "b", "z": "c"},
valid: true,
},
{
selector: " x = a , y = b , z = c ",
labels: map[string]string{"x": "a", "y": "b", "z": "c"},
valid: true,
},
{
selector: "color=green,env=test,service=front",
labels: map[string]string{"color": "green", "env": "test", "service": "front"},
valid: true,
},
{
selector: "color=green, env=test, service=front",
labels: map[string]string{"color": "green", "env": "test", "service": "front"},
valid: true,
},
{
selector: ",",
labels: map[string]string{},
valid: false,
},
{
selector: "x",
labels: map[string]string{},
valid: false,
},
{
selector: "x,y",
labels: map[string]string{},
valid: false,
},
{
selector: "x=$y",
labels: map[string]string{},
valid: false,
},
{
selector: "x!=y",
labels: map[string]string{},
valid: false,
},
{
selector: "x==y",
labels: map[string]string{},
valid: false,
},
{
selector: "x=a||y=b",
labels: map[string]string{},
valid: false,
},
{
selector: "x in (y)",
labels: map[string]string{},
valid: false,
},
{
selector: "x notin (y)",
labels: map[string]string{},
valid: false,
},
{
selector: "x y",
labels: map[string]string{},
valid: false,
},
}
for _, test := range tests {
labels, err := ConvertSelectorToLabelsMap(test.selector)
if test.valid && err != nil {
t.Errorf("selector: %s, expected no error but got: %s", test.selector, err)
} else if !test.valid && err == nil {
t.Errorf("selector: %s, expected an error", test.selector)
}
if !Equals(Set(labels), test.labels) {
t.Errorf("expected: %s but got: %s", test.labels, labels)
}
}
}

View file

@ -1,575 +0,0 @@
/*
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 labels
import (
"reflect"
"strings"
"testing"
"k8s.io/apimachinery/pkg/selection"
"k8s.io/apimachinery/pkg/util/sets"
)
func TestSelectorParse(t *testing.T) {
testGoodStrings := []string{
"x=a,y=b,z=c",
"",
"x!=a,y=b",
"x=",
"x= ",
"x=,z= ",
"x= ,z= ",
"!x",
"x>1",
"x>1,z<5",
}
testBadStrings := []string{
"x=a||y=b",
"x==a==b",
"!x=a",
"x<a",
}
for _, test := range testGoodStrings {
lq, err := Parse(test)
if err != nil {
t.Errorf("%v: error %v (%#v)\n", test, err, err)
}
if strings.Replace(test, " ", "", -1) != lq.String() {
t.Errorf("%v restring gave: %v\n", test, lq.String())
}
}
for _, test := range testBadStrings {
_, err := Parse(test)
if err == nil {
t.Errorf("%v: did not get expected error\n", test)
}
}
}
func TestDeterministicParse(t *testing.T) {
s1, err := Parse("x=a,a=x")
s2, err2 := Parse("a=x,x=a")
if err != nil || err2 != nil {
t.Errorf("Unexpected parse error")
}
if s1.String() != s2.String() {
t.Errorf("Non-deterministic parse")
}
}
func expectMatch(t *testing.T, selector string, ls Set) {
lq, err := Parse(selector)
if err != nil {
t.Errorf("Unable to parse %v as a selector\n", selector)
return
}
if !lq.Matches(ls) {
t.Errorf("Wanted %s to match '%s', but it did not.\n", selector, ls)
}
}
func expectNoMatch(t *testing.T, selector string, ls Set) {
lq, err := Parse(selector)
if err != nil {
t.Errorf("Unable to parse %v as a selector\n", selector)
return
}
if lq.Matches(ls) {
t.Errorf("Wanted '%s' to not match '%s', but it did.", selector, ls)
}
}
func TestEverything(t *testing.T) {
if !Everything().Matches(Set{"x": "y"}) {
t.Errorf("Nil selector didn't match")
}
if !Everything().Empty() {
t.Errorf("Everything was not empty")
}
}
func TestSelectorMatches(t *testing.T) {
expectMatch(t, "", Set{"x": "y"})
expectMatch(t, "x=y", Set{"x": "y"})
expectMatch(t, "x=y,z=w", Set{"x": "y", "z": "w"})
expectMatch(t, "x!=y,z!=w", Set{"x": "z", "z": "a"})
expectMatch(t, "notin=in", Set{"notin": "in"}) // in and notin in exactMatch
expectMatch(t, "x", Set{"x": "z"})
expectMatch(t, "!x", Set{"y": "z"})
expectMatch(t, "x>1", Set{"x": "2"})
expectMatch(t, "x<1", Set{"x": "0"})
expectNoMatch(t, "x=z", Set{})
expectNoMatch(t, "x=y", Set{"x": "z"})
expectNoMatch(t, "x=y,z=w", Set{"x": "w", "z": "w"})
expectNoMatch(t, "x!=y,z!=w", Set{"x": "z", "z": "w"})
expectNoMatch(t, "x", Set{"y": "z"})
expectNoMatch(t, "!x", Set{"x": "z"})
expectNoMatch(t, "x>1", Set{"x": "0"})
expectNoMatch(t, "x<1", Set{"x": "2"})
labelset := Set{
"foo": "bar",
"baz": "blah",
}
expectMatch(t, "foo=bar", labelset)
expectMatch(t, "baz=blah", labelset)
expectMatch(t, "foo=bar,baz=blah", labelset)
expectNoMatch(t, "foo=blah", labelset)
expectNoMatch(t, "baz=bar", labelset)
expectNoMatch(t, "foo=bar,foobar=bar,baz=blah", labelset)
}
func expectMatchDirect(t *testing.T, selector, ls Set) {
if !SelectorFromSet(selector).Matches(ls) {
t.Errorf("Wanted %s to match '%s', but it did not.\n", selector, ls)
}
}
func expectNoMatchDirect(t *testing.T, selector, ls Set) {
if SelectorFromSet(selector).Matches(ls) {
t.Errorf("Wanted '%s' to not match '%s', but it did.", selector, ls)
}
}
func TestSetMatches(t *testing.T) {
labelset := Set{
"foo": "bar",
"baz": "blah",
}
expectMatchDirect(t, Set{}, labelset)
expectMatchDirect(t, Set{"foo": "bar"}, labelset)
expectMatchDirect(t, Set{"baz": "blah"}, labelset)
expectMatchDirect(t, Set{"foo": "bar", "baz": "blah"}, labelset)
//TODO: bad values not handled for the moment in SelectorFromSet
//expectNoMatchDirect(t, Set{"foo": "=blah"}, labelset)
//expectNoMatchDirect(t, Set{"baz": "=bar"}, labelset)
//expectNoMatchDirect(t, Set{"foo": "=bar", "foobar": "bar", "baz": "blah"}, labelset)
}
func TestNilMapIsValid(t *testing.T) {
selector := Set(nil).AsSelector()
if selector == nil {
t.Errorf("Selector for nil set should be Everything")
}
if !selector.Empty() {
t.Errorf("Selector for nil set should be Empty")
}
}
func TestSetIsEmpty(t *testing.T) {
if !(Set{}).AsSelector().Empty() {
t.Errorf("Empty set should be empty")
}
if !(NewSelector()).Empty() {
t.Errorf("Nil Selector should be empty")
}
}
func TestLexer(t *testing.T) {
testcases := []struct {
s string
t Token
}{
{"", EndOfStringToken},
{",", CommaToken},
{"notin", NotInToken},
{"in", InToken},
{"=", EqualsToken},
{"==", DoubleEqualsToken},
{">", GreaterThanToken},
{"<", LessThanToken},
//Note that Lex returns the longest valid token found
{"!", DoesNotExistToken},
{"!=", NotEqualsToken},
{"(", OpenParToken},
{")", ClosedParToken},
//Non-"special" characters are considered part of an identifier
{"~", IdentifierToken},
{"||", IdentifierToken},
}
for _, v := range testcases {
l := &Lexer{s: v.s, pos: 0}
token, lit := l.Lex()
if token != v.t {
t.Errorf("Got %d it should be %d for '%s'", token, v.t, v.s)
}
if v.t != ErrorToken && lit != v.s {
t.Errorf("Got '%s' it should be '%s'", lit, v.s)
}
}
}
func min(l, r int) (m int) {
m = r
if l < r {
m = l
}
return m
}
func TestLexerSequence(t *testing.T) {
testcases := []struct {
s string
t []Token
}{
{"key in ( value )", []Token{IdentifierToken, InToken, OpenParToken, IdentifierToken, ClosedParToken}},
{"key notin ( value )", []Token{IdentifierToken, NotInToken, OpenParToken, IdentifierToken, ClosedParToken}},
{"key in ( value1, value2 )", []Token{IdentifierToken, InToken, OpenParToken, IdentifierToken, CommaToken, IdentifierToken, ClosedParToken}},
{"key", []Token{IdentifierToken}},
{"!key", []Token{DoesNotExistToken, IdentifierToken}},
{"()", []Token{OpenParToken, ClosedParToken}},
{"x in (),y", []Token{IdentifierToken, InToken, OpenParToken, ClosedParToken, CommaToken, IdentifierToken}},
{"== != (), = notin", []Token{DoubleEqualsToken, NotEqualsToken, OpenParToken, ClosedParToken, CommaToken, EqualsToken, NotInToken}},
{"key>2", []Token{IdentifierToken, GreaterThanToken, IdentifierToken}},
{"key<1", []Token{IdentifierToken, LessThanToken, IdentifierToken}},
}
for _, v := range testcases {
var literals []string
var tokens []Token
l := &Lexer{s: v.s, pos: 0}
for {
token, lit := l.Lex()
if token == EndOfStringToken {
break
}
tokens = append(tokens, token)
literals = append(literals, lit)
}
if len(tokens) != len(v.t) {
t.Errorf("Bad number of tokens for '%s %d, %d", v.s, len(tokens), len(v.t))
}
for i := 0; i < min(len(tokens), len(v.t)); i++ {
if tokens[i] != v.t[i] {
t.Errorf("Test '%s': Mismatching in token type found '%v' it should be '%v'", v.s, tokens[i], v.t[i])
}
}
}
}
func TestParserLookahead(t *testing.T) {
testcases := []struct {
s string
t []Token
}{
{"key in ( value )", []Token{IdentifierToken, InToken, OpenParToken, IdentifierToken, ClosedParToken, EndOfStringToken}},
{"key notin ( value )", []Token{IdentifierToken, NotInToken, OpenParToken, IdentifierToken, ClosedParToken, EndOfStringToken}},
{"key in ( value1, value2 )", []Token{IdentifierToken, InToken, OpenParToken, IdentifierToken, CommaToken, IdentifierToken, ClosedParToken, EndOfStringToken}},
{"key", []Token{IdentifierToken, EndOfStringToken}},
{"!key", []Token{DoesNotExistToken, IdentifierToken, EndOfStringToken}},
{"()", []Token{OpenParToken, ClosedParToken, EndOfStringToken}},
{"", []Token{EndOfStringToken}},
{"x in (),y", []Token{IdentifierToken, InToken, OpenParToken, ClosedParToken, CommaToken, IdentifierToken, EndOfStringToken}},
{"== != (), = notin", []Token{DoubleEqualsToken, NotEqualsToken, OpenParToken, ClosedParToken, CommaToken, EqualsToken, NotInToken, EndOfStringToken}},
{"key>2", []Token{IdentifierToken, GreaterThanToken, IdentifierToken, EndOfStringToken}},
{"key<1", []Token{IdentifierToken, LessThanToken, IdentifierToken, EndOfStringToken}},
}
for _, v := range testcases {
p := &Parser{l: &Lexer{s: v.s, pos: 0}, position: 0}
p.scan()
if len(p.scannedItems) != len(v.t) {
t.Errorf("Expected %d items found %d", len(v.t), len(p.scannedItems))
}
for {
token, lit := p.lookahead(KeyAndOperator)
token2, lit2 := p.consume(KeyAndOperator)
if token == EndOfStringToken {
break
}
if token != token2 || lit != lit2 {
t.Errorf("Bad values")
}
}
}
}
func TestRequirementConstructor(t *testing.T) {
requirementConstructorTests := []struct {
Key string
Op selection.Operator
Vals sets.String
Success bool
}{
{"x", selection.In, nil, false},
{"x", selection.NotIn, sets.NewString(), false},
{"x", selection.In, sets.NewString("foo"), true},
{"x", selection.NotIn, sets.NewString("foo"), true},
{"x", selection.Exists, nil, true},
{"x", selection.DoesNotExist, nil, true},
{"1foo", selection.In, sets.NewString("bar"), true},
{"1234", selection.In, sets.NewString("bar"), true},
{"y", selection.GreaterThan, sets.NewString("1"), true},
{"z", selection.LessThan, sets.NewString("6"), true},
{"foo", selection.GreaterThan, sets.NewString("bar"), false},
{"barz", selection.LessThan, sets.NewString("blah"), false},
{strings.Repeat("a", 254), selection.Exists, nil, false}, //breaks DNS rule that len(key) <= 253
}
for _, rc := range requirementConstructorTests {
if _, err := NewRequirement(rc.Key, rc.Op, rc.Vals.List()); err == nil && !rc.Success {
t.Errorf("expected error with key:%#v op:%v vals:%v, got no error", rc.Key, rc.Op, rc.Vals)
} else if err != nil && rc.Success {
t.Errorf("expected no error with key:%#v op:%v vals:%v, got:%v", rc.Key, rc.Op, rc.Vals, err)
}
}
}
func TestToString(t *testing.T) {
var req Requirement
toStringTests := []struct {
In *internalSelector
Out string
Valid bool
}{
{&internalSelector{
getRequirement("x", selection.In, sets.NewString("abc", "def"), t),
getRequirement("y", selection.NotIn, sets.NewString("jkl"), t),
getRequirement("z", selection.Exists, nil, t)},
"x in (abc,def),y notin (jkl),z", true},
{&internalSelector{
getRequirement("x", selection.NotIn, sets.NewString("abc", "def"), t),
getRequirement("y", selection.NotEquals, sets.NewString("jkl"), t),
getRequirement("z", selection.DoesNotExist, nil, t)},
"x notin (abc,def),y!=jkl,!z", true},
{&internalSelector{
getRequirement("x", selection.In, sets.NewString("abc", "def"), t),
req}, // adding empty req for the trailing ','
"x in (abc,def),", false},
{&internalSelector{
getRequirement("x", selection.NotIn, sets.NewString("abc"), t),
getRequirement("y", selection.In, sets.NewString("jkl", "mno"), t),
getRequirement("z", selection.NotIn, sets.NewString(""), t)},
"x notin (abc),y in (jkl,mno),z notin ()", true},
{&internalSelector{
getRequirement("x", selection.Equals, sets.NewString("abc"), t),
getRequirement("y", selection.DoubleEquals, sets.NewString("jkl"), t),
getRequirement("z", selection.NotEquals, sets.NewString("a"), t),
getRequirement("z", selection.Exists, nil, t)},
"x=abc,y==jkl,z!=a,z", true},
{&internalSelector{
getRequirement("x", selection.GreaterThan, sets.NewString("2"), t),
getRequirement("y", selection.LessThan, sets.NewString("8"), t),
getRequirement("z", selection.Exists, nil, t)},
"x>2,y<8,z", true},
}
for _, ts := range toStringTests {
if out := ts.In.String(); out == "" && ts.Valid {
t.Errorf("%#v.String() => '%v' expected no error", ts.In, out)
} else if out != ts.Out {
t.Errorf("%#v.String() => '%v' want '%v'", ts.In, out, ts.Out)
}
}
}
func TestRequirementSelectorMatching(t *testing.T) {
var req Requirement
labelSelectorMatchingTests := []struct {
Set Set
Sel Selector
Match bool
}{
{Set{"x": "foo", "y": "baz"}, &internalSelector{
req,
}, false},
{Set{"x": "foo", "y": "baz"}, &internalSelector{
getRequirement("x", selection.In, sets.NewString("foo"), t),
getRequirement("y", selection.NotIn, sets.NewString("alpha"), t),
}, true},
{Set{"x": "foo", "y": "baz"}, &internalSelector{
getRequirement("x", selection.In, sets.NewString("foo"), t),
getRequirement("y", selection.In, sets.NewString("alpha"), t),
}, false},
{Set{"y": ""}, &internalSelector{
getRequirement("x", selection.NotIn, sets.NewString(""), t),
getRequirement("y", selection.Exists, nil, t),
}, true},
{Set{"y": ""}, &internalSelector{
getRequirement("x", selection.DoesNotExist, nil, t),
getRequirement("y", selection.Exists, nil, t),
}, true},
{Set{"y": ""}, &internalSelector{
getRequirement("x", selection.NotIn, sets.NewString(""), t),
getRequirement("y", selection.DoesNotExist, nil, t),
}, false},
{Set{"y": "baz"}, &internalSelector{
getRequirement("x", selection.In, sets.NewString(""), t),
}, false},
{Set{"z": "2"}, &internalSelector{
getRequirement("z", selection.GreaterThan, sets.NewString("1"), t),
}, true},
{Set{"z": "v2"}, &internalSelector{
getRequirement("z", selection.GreaterThan, sets.NewString("1"), t),
}, false},
}
for _, lsm := range labelSelectorMatchingTests {
if match := lsm.Sel.Matches(lsm.Set); match != lsm.Match {
t.Errorf("%+v.Matches(%#v) => %v, want %v", lsm.Sel, lsm.Set, match, lsm.Match)
}
}
}
func TestSetSelectorParser(t *testing.T) {
setSelectorParserTests := []struct {
In string
Out Selector
Match bool
Valid bool
}{
{"", NewSelector(), true, true},
{"\rx", internalSelector{
getRequirement("x", selection.Exists, nil, t),
}, true, true},
{"this-is-a-dns.domain.com/key-with-dash", internalSelector{
getRequirement("this-is-a-dns.domain.com/key-with-dash", selection.Exists, nil, t),
}, true, true},
{"this-is-another-dns.domain.com/key-with-dash in (so,what)", internalSelector{
getRequirement("this-is-another-dns.domain.com/key-with-dash", selection.In, sets.NewString("so", "what"), t),
}, true, true},
{"0.1.2.domain/99 notin (10.10.100.1, tick.tack.clock)", internalSelector{
getRequirement("0.1.2.domain/99", selection.NotIn, sets.NewString("10.10.100.1", "tick.tack.clock"), t),
}, true, true},
{"foo in (abc)", internalSelector{
getRequirement("foo", selection.In, sets.NewString("abc"), t),
}, true, true},
{"x notin\n (abc)", internalSelector{
getRequirement("x", selection.NotIn, sets.NewString("abc"), t),
}, true, true},
{"x notin \t (abc,def)", internalSelector{
getRequirement("x", selection.NotIn, sets.NewString("abc", "def"), t),
}, true, true},
{"x in (abc,def)", internalSelector{
getRequirement("x", selection.In, sets.NewString("abc", "def"), t),
}, true, true},
{"x in (abc,)", internalSelector{
getRequirement("x", selection.In, sets.NewString("abc", ""), t),
}, true, true},
{"x in ()", internalSelector{
getRequirement("x", selection.In, sets.NewString(""), t),
}, true, true},
{"x notin (abc,,def),bar,z in (),w", internalSelector{
getRequirement("bar", selection.Exists, nil, t),
getRequirement("w", selection.Exists, nil, t),
getRequirement("x", selection.NotIn, sets.NewString("abc", "", "def"), t),
getRequirement("z", selection.In, sets.NewString(""), t),
}, true, true},
{"x,y in (a)", internalSelector{
getRequirement("y", selection.In, sets.NewString("a"), t),
getRequirement("x", selection.Exists, nil, t),
}, false, true},
{"x=a", internalSelector{
getRequirement("x", selection.Equals, sets.NewString("a"), t),
}, true, true},
{"x>1", internalSelector{
getRequirement("x", selection.GreaterThan, sets.NewString("1"), t),
}, true, true},
{"x<7", internalSelector{
getRequirement("x", selection.LessThan, sets.NewString("7"), t),
}, true, true},
{"x=a,y!=b", internalSelector{
getRequirement("x", selection.Equals, sets.NewString("a"), t),
getRequirement("y", selection.NotEquals, sets.NewString("b"), t),
}, true, true},
{"x=a,y!=b,z in (h,i,j)", internalSelector{
getRequirement("x", selection.Equals, sets.NewString("a"), t),
getRequirement("y", selection.NotEquals, sets.NewString("b"), t),
getRequirement("z", selection.In, sets.NewString("h", "i", "j"), t),
}, true, true},
{"x=a||y=b", internalSelector{}, false, false},
{"x,,y", nil, true, false},
{",x,y", nil, true, false},
{"x nott in (y)", nil, true, false},
{"x notin ( )", internalSelector{
getRequirement("x", selection.NotIn, sets.NewString(""), t),
}, true, true},
{"x notin (, a)", internalSelector{
getRequirement("x", selection.NotIn, sets.NewString("", "a"), t),
}, true, true},
{"a in (xyz),", nil, true, false},
{"a in (xyz)b notin ()", nil, true, false},
{"a ", internalSelector{
getRequirement("a", selection.Exists, nil, t),
}, true, true},
{"a in (x,y,notin, z,in)", internalSelector{
getRequirement("a", selection.In, sets.NewString("in", "notin", "x", "y", "z"), t),
}, true, true}, // operator 'in' inside list of identifiers
{"a in (xyz abc)", nil, false, false}, // no comma
{"a notin(", nil, true, false}, // bad formed
{"a (", nil, false, false}, // cpar
{"(", nil, false, false}, // opar
}
for _, ssp := range setSelectorParserTests {
if sel, err := Parse(ssp.In); err != nil && ssp.Valid {
t.Errorf("Parse(%s) => %v expected no error", ssp.In, err)
} else if err == nil && !ssp.Valid {
t.Errorf("Parse(%s) => %+v expected error", ssp.In, sel)
} else if ssp.Match && !reflect.DeepEqual(sel, ssp.Out) {
t.Errorf("Parse(%s) => parse output '%#v' doesn't match '%#v' expected match", ssp.In, sel, ssp.Out)
}
}
}
func getRequirement(key string, op selection.Operator, vals sets.String, t *testing.T) Requirement {
req, err := NewRequirement(key, op, vals.List())
if err != nil {
t.Errorf("NewRequirement(%v, %v, %v) resulted in error:%v", key, op, vals, err)
return Requirement{}
}
return *req
}
func TestAdd(t *testing.T) {
testCases := []struct {
name string
sel Selector
key string
operator selection.Operator
values []string
refSelector Selector
}{
{
"keyInOperator",
internalSelector{},
"key",
selection.In,
[]string{"value"},
internalSelector{Requirement{"key", selection.In, []string{"value"}}},
},
{
"keyEqualsOperator",
internalSelector{Requirement{"key", selection.In, []string{"value"}}},
"key2",
selection.Equals,
[]string{"value2"},
internalSelector{
Requirement{"key", selection.In, []string{"value"}},
Requirement{"key2", selection.Equals, []string{"value2"}},
},
},
}
for _, ts := range testCases {
req, err := NewRequirement(ts.key, ts.operator, ts.values)
if err != nil {
t.Errorf("%s - Unable to create labels.Requirement", ts.name)
}
ts.sel = ts.sel.Add(*req)
if !reflect.DeepEqual(ts.sel, ts.refSelector) {
t.Errorf("%s - Expected %v found %v", ts.name, ts.refSelector, ts.sel)
}
}
}

View file

@ -19,6 +19,7 @@ package openapi
import (
"github.com/emicklei/go-restful"
"github.com/go-openapi/spec"
"strings"
)
// OpenAPIDefinition describes single type. Normally these definitions are auto-generated using gen-openapi.
@ -27,8 +28,10 @@ type OpenAPIDefinition struct {
Dependencies []string
}
type ReferenceCallback func(path string) spec.Ref
// OpenAPIDefinitions is collection of all definitions.
type OpenAPIDefinitions map[string]OpenAPIDefinition
type GetOpenAPIDefinitions func(ReferenceCallback) map[string]OpenAPIDefinition
// OpenAPIDefinitionGetter gets openAPI definitions for a given type. If a type implements this interface,
// the definition returned by it will be used, otherwise the auto-generated definitions will be used. See
@ -59,11 +62,18 @@ type Config struct {
// OpenAPIDefinitions should provide definition for all models used by routes. Failure to provide this map
// or any of the models will result in spec generation failure.
Definitions *OpenAPIDefinitions
GetDefinitions GetOpenAPIDefinitions
// GetOperationIDAndTags returns operation id and tags for a restful route. It is an optional function to customize operation IDs.
GetOperationIDAndTags func(servePath string, r *restful.Route) (string, []string, error)
// GetDefinitionName returns a friendly name for a definition base on the serving path. parameter `name` is the full name of the definition.
// It is an optional function to customize model names.
GetDefinitionName func(servePath string, name string) (string, spec.Extensions)
// PostProcessSpec runs after the spec is ready to serve. It allows a final modification to the spec before serving.
PostProcessSpec func(*spec.Swagger) (*spec.Swagger, error)
// SecurityDefinitions is list of all security definitions for OpenAPI service. If this is not nil, the user of config
// is responsible to provide DefaultSecurity and (maybe) add unauthorized response to CommonResponses.
SecurityDefinitions *spec.SecurityDefinitions
@ -141,3 +151,10 @@ func GetOpenAPITypeFormat(typeName string) (string, string) {
}
return mapped[0], mapped[1]
}
func EscapeJsonPointer(p string) string {
// Escaping reference name using rfc6901
p = strings.Replace(p, "~", "~0", -1)
p = strings.Replace(p, "/", "~1", -1)
return p
}

View file

@ -1,19 +0,0 @@
approvers:
- caesarxuchao
- deads2k
- lavalamp
- smarterclayton
reviewers:
- lavalamp
- smarterclayton
- wojtek-t
- deads2k
- derekwaynecarr
- caesarxuchao
- mikedanese
- nikhiljindal
- gmarek
- krousey
- timothysc
- piosz
- mbohlool

View file

@ -161,11 +161,12 @@ func (c *parameterCodec) DecodeParameters(parameters url.Values, from schema.Gro
if err != nil {
return err
}
targetGVK := targetGVKs[0]
if targetGVK.GroupVersion() == from {
return c.convertor.Convert(&parameters, into, nil)
for i := range targetGVKs {
if targetGVKs[i].GroupVersion() == from {
return c.convertor.Convert(&parameters, into, nil)
}
}
input, err := c.creator.New(from.WithKind(targetGVK.Kind))
input, err := c.creator.New(from.WithKind(targetGVKs[0].Kind))
if err != nil {
return err
}
@ -194,16 +195,17 @@ func (c *parameterCodec) EncodeParameters(obj Object, to schema.GroupVersion) (u
}
type base64Serializer struct {
Serializer
Encoder
Decoder
}
func NewBase64Serializer(s Serializer) Serializer {
return &base64Serializer{s}
func NewBase64Serializer(e Encoder, d Decoder) Serializer {
return &base64Serializer{e, d}
}
func (s base64Serializer) Encode(obj Object, stream io.Writer) error {
e := base64.NewEncoder(base64.StdEncoding, stream)
err := s.Serializer.Encode(obj, e)
err := s.Encoder.Encode(obj, e)
e.Close()
return err
}
@ -214,7 +216,7 @@ func (s base64Serializer) Decode(data []byte, defaults *schema.GroupVersionKind,
if err != nil {
return nil, nil, err
}
return s.Serializer.Decode(out[:n], defaults, into)
return s.Decoder.Decode(out[:n], defaults, into)
}
// SerializerInfoForMediaType returns the first info in types that has a matching media type (which cannot

View file

@ -1,135 +0,0 @@
/*
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 runtime_test
import (
"reflect"
"testing"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
)
type InternalComplex struct {
runtime.TypeMeta
String string
Integer int
Integer64 int64
Int64 int64
Bool bool
}
type ExternalComplex struct {
runtime.TypeMeta `json:",inline"`
String string `json:"string" description:"testing"`
Integer int `json:"int"`
Integer64 int64 `json:",omitempty"`
Int64 int64
Bool bool `json:"bool"`
}
func (obj *InternalComplex) GetObjectKind() schema.ObjectKind { return &obj.TypeMeta }
func (obj *ExternalComplex) GetObjectKind() schema.ObjectKind { return &obj.TypeMeta }
func TestStringMapConversion(t *testing.T) {
internalGV := schema.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
externalGV := schema.GroupVersion{Group: "test.group", Version: "external"}
scheme := runtime.NewScheme()
scheme.Log(t)
scheme.AddKnownTypeWithName(internalGV.WithKind("Complex"), &InternalComplex{})
scheme.AddKnownTypeWithName(externalGV.WithKind("Complex"), &ExternalComplex{})
testCases := map[string]struct {
input map[string][]string
errFn func(error) bool
expected runtime.Object
}{
"ignores omitempty": {
input: map[string][]string{
"String": {"not_used"},
"string": {"value"},
"int": {"1"},
"Integer64": {"2"},
},
expected: &ExternalComplex{String: "value", Integer: 1},
},
"returns error on bad int": {
input: map[string][]string{
"int": {"a"},
},
errFn: func(err error) bool { return err != nil },
expected: &ExternalComplex{},
},
"parses int64": {
input: map[string][]string{
"Int64": {"-1"},
},
expected: &ExternalComplex{Int64: -1},
},
"returns error on bad int64": {
input: map[string][]string{
"Int64": {"a"},
},
errFn: func(err error) bool { return err != nil },
expected: &ExternalComplex{},
},
"parses boolean true": {
input: map[string][]string{
"bool": {"true"},
},
expected: &ExternalComplex{Bool: true},
},
"parses boolean any value": {
input: map[string][]string{
"bool": {"foo"},
},
expected: &ExternalComplex{Bool: true},
},
"parses boolean false": {
input: map[string][]string{
"bool": {"false"},
},
expected: &ExternalComplex{Bool: false},
},
"parses boolean empty value": {
input: map[string][]string{
"bool": {""},
},
expected: &ExternalComplex{Bool: true},
},
"parses boolean no value": {
input: map[string][]string{
"bool": {},
},
expected: &ExternalComplex{Bool: false},
},
}
for k, tc := range testCases {
out := &ExternalComplex{}
if err := scheme.Convert(&tc.input, out, nil); (tc.errFn == nil && err != nil) || (tc.errFn != nil && !tc.errFn(err)) {
t.Errorf("%s: unexpected error: %v", k, err)
continue
} else if err != nil {
continue
}
if !reflect.DeepEqual(out, tc.expected) {
t.Errorf("%s: unexpected output: %#v", k, out)
}
}
}

View file

@ -1,290 +0,0 @@
/*
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 runtime_test
import (
"encoding/json"
"reflect"
"strings"
"testing"
"k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/apimachinery/pkg/util/diff"
)
type EmbeddedTest struct {
runtime.TypeMeta
ID string
Object runtime.Object
EmptyObject runtime.Object
}
type EmbeddedTestExternal struct {
runtime.TypeMeta `json:",inline"`
ID string `json:"id,omitempty"`
Object runtime.RawExtension `json:"object,omitempty"`
EmptyObject runtime.RawExtension `json:"emptyObject,omitempty"`
}
type ObjectTest struct {
runtime.TypeMeta
ID string
Items []runtime.Object
}
type ObjectTestExternal struct {
runtime.TypeMeta `yaml:",inline" json:",inline"`
ID string `json:"id,omitempty"`
Items []runtime.RawExtension `json:"items,omitempty"`
}
func (obj *ObjectTest) GetObjectKind() schema.ObjectKind { return &obj.TypeMeta }
func (obj *ObjectTestExternal) GetObjectKind() schema.ObjectKind { return &obj.TypeMeta }
func (obj *EmbeddedTest) GetObjectKind() schema.ObjectKind { return &obj.TypeMeta }
func (obj *EmbeddedTestExternal) GetObjectKind() schema.ObjectKind { return &obj.TypeMeta }
func TestDecodeEmptyRawExtensionAsObject(t *testing.T) {
internalGV := schema.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
externalGV := schema.GroupVersion{Group: "test.group", Version: "v1test"}
externalGVK := externalGV.WithKind("ObjectTest")
s := runtime.NewScheme()
s.AddKnownTypes(internalGV, &ObjectTest{})
s.AddKnownTypeWithName(externalGVK, &ObjectTestExternal{})
codec := serializer.NewCodecFactory(s).LegacyCodec(externalGV)
obj, gvk, err := codec.Decode([]byte(`{"kind":"`+externalGVK.Kind+`","apiVersion":"`+externalGV.String()+`","items":[{}]}`), nil, nil)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
test := obj.(*ObjectTest)
if unk, ok := test.Items[0].(*runtime.Unknown); !ok || unk.Kind != "" || unk.APIVersion != "" || string(unk.Raw) != "{}" || unk.ContentType != runtime.ContentTypeJSON {
t.Fatalf("unexpected object: %#v", test.Items[0])
}
if *gvk != externalGVK {
t.Fatalf("unexpected kind: %#v", gvk)
}
obj, gvk, err = codec.Decode([]byte(`{"kind":"`+externalGVK.Kind+`","apiVersion":"`+externalGV.String()+`","items":[{"kind":"Other","apiVersion":"v1"}]}`), nil, nil)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
test = obj.(*ObjectTest)
if unk, ok := test.Items[0].(*runtime.Unknown); !ok || unk.Kind != "" || unk.APIVersion != "" || string(unk.Raw) != `{"kind":"Other","apiVersion":"v1"}` || unk.ContentType != runtime.ContentTypeJSON {
t.Fatalf("unexpected object: %#v", test.Items[0])
}
if *gvk != externalGVK {
t.Fatalf("unexpected kind: %#v", gvk)
}
}
func TestArrayOfRuntimeObject(t *testing.T) {
internalGV := schema.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
externalGV := schema.GroupVersion{Group: "test.group", Version: "v1test"}
s := runtime.NewScheme()
s.AddKnownTypes(internalGV, &EmbeddedTest{})
s.AddKnownTypeWithName(externalGV.WithKind("EmbeddedTest"), &EmbeddedTestExternal{})
s.AddKnownTypes(internalGV, &ObjectTest{})
s.AddKnownTypeWithName(externalGV.WithKind("ObjectTest"), &ObjectTestExternal{})
codec := serializer.NewCodecFactory(s).LegacyCodec(externalGV)
innerItems := []runtime.Object{
&EmbeddedTest{ID: "baz"},
}
items := []runtime.Object{
&EmbeddedTest{ID: "foo"},
&EmbeddedTest{ID: "bar"},
// TODO: until YAML is removed, this JSON must be in ascending key order to ensure consistent roundtrip serialization
&runtime.Unknown{
Raw: []byte(`{"apiVersion":"unknown.group/unknown","foo":"bar","kind":"OtherTest"}`),
ContentType: runtime.ContentTypeJSON,
},
&ObjectTest{
Items: runtime.NewEncodableList(codec, innerItems),
},
}
internal := &ObjectTest{
Items: runtime.NewEncodableList(codec, items),
}
wire, err := runtime.Encode(codec, internal)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
t.Logf("Wire format is:\n%s\n", string(wire))
obj := &ObjectTestExternal{}
if err := json.Unmarshal(wire, obj); err != nil {
t.Fatalf("unexpected error: %v", err)
}
t.Logf("exact wire is: %s", string(obj.Items[0].Raw))
items[3] = &ObjectTest{Items: innerItems}
internal.Items = items
decoded, err := runtime.Decode(codec, wire)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
list, err := meta.ExtractList(decoded)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if errs := runtime.DecodeList(list, codec); len(errs) > 0 {
t.Fatalf("unexpected error: %v", errs)
}
list2, err := meta.ExtractList(list[3])
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if errs := runtime.DecodeList(list2, codec); len(errs) > 0 {
t.Fatalf("unexpected error: %v", errs)
}
if err := meta.SetList(list[3], list2); err != nil {
t.Fatalf("unexpected error: %v", err)
}
// we want DecodeList to set type meta if possible, even on runtime.Unknown objects
internal.Items[2].(*runtime.Unknown).TypeMeta = runtime.TypeMeta{Kind: "OtherTest", APIVersion: "unknown.group/unknown"}
if e, a := internal.Items, list; !reflect.DeepEqual(e, a) {
t.Errorf("mismatched decoded: %s", diff.ObjectGoPrintSideBySide(e, a))
}
}
func TestNestedObject(t *testing.T) {
internalGV := schema.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
externalGV := schema.GroupVersion{Group: "test.group", Version: "v1test"}
embeddedTestExternalGVK := externalGV.WithKind("EmbeddedTest")
s := runtime.NewScheme()
s.AddKnownTypes(internalGV, &EmbeddedTest{})
s.AddKnownTypeWithName(embeddedTestExternalGVK, &EmbeddedTestExternal{})
codec := serializer.NewCodecFactory(s).LegacyCodec(externalGV)
inner := &EmbeddedTest{
ID: "inner",
}
outer := &EmbeddedTest{
ID: "outer",
Object: runtime.NewEncodable(codec, inner),
}
wire, err := runtime.Encode(codec, outer)
if err != nil {
t.Fatalf("Unexpected encode error '%v'", err)
}
t.Logf("Wire format is:\n%v\n", string(wire))
decoded, err := runtime.Decode(codec, wire)
if err != nil {
t.Fatalf("Unexpected decode error %v", err)
}
// for later tests
outer.Object = inner
if e, a := outer, decoded; reflect.DeepEqual(e, a) {
t.Errorf("Expected unequal %#v %#v", e, a)
}
obj, err := runtime.Decode(codec, decoded.(*EmbeddedTest).Object.(*runtime.Unknown).Raw)
if err != nil {
t.Fatal(err)
}
decoded.(*EmbeddedTest).Object = obj
if e, a := outer, decoded; !reflect.DeepEqual(e, a) {
t.Errorf("Expected equal %#v %#v", e, a)
}
// test JSON decoding of the external object, which should preserve
// raw bytes
var externalViaJSON EmbeddedTestExternal
err = json.Unmarshal(wire, &externalViaJSON)
if err != nil {
t.Fatalf("Unexpected decode error %v", err)
}
if externalViaJSON.Kind == "" || externalViaJSON.APIVersion == "" || externalViaJSON.ID != "outer" {
t.Errorf("Expected objects to have type info set, got %#v", externalViaJSON)
}
if !reflect.DeepEqual(externalViaJSON.EmptyObject.Raw, []byte("null")) || len(externalViaJSON.Object.Raw) == 0 {
t.Errorf("Expected deserialization of nested objects into bytes, got %#v", externalViaJSON)
}
// test JSON decoding, too, since Decode uses yaml unmarshalling.
// Generic Unmarshalling of JSON cannot load the nested objects because there is
// no default schema set. Consumers wishing to get direct JSON decoding must use
// the external representation
var decodedViaJSON EmbeddedTest
err = json.Unmarshal(wire, &decodedViaJSON)
if err == nil || !strings.Contains(err.Error(), "unmarshal object into Go value of type runtime.Object") {
t.Fatalf("Unexpected decode error %v", err)
}
if a := decodedViaJSON; a.Object != nil || a.EmptyObject != nil {
t.Errorf("Expected embedded objects to be nil: %#v", a)
}
}
// TestDeepCopyOfRuntimeObject checks to make sure that runtime.Objects's can be passed through DeepCopy with fidelity
func TestDeepCopyOfRuntimeObject(t *testing.T) {
internalGV := schema.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
externalGV := schema.GroupVersion{Group: "test.group", Version: "v1test"}
embeddedTestExternalGVK := externalGV.WithKind("EmbeddedTest")
s := runtime.NewScheme()
s.AddKnownTypes(internalGV, &EmbeddedTest{})
s.AddKnownTypeWithName(embeddedTestExternalGVK, &EmbeddedTestExternal{})
original := &EmbeddedTest{
ID: "outer",
Object: &EmbeddedTest{
ID: "inner",
},
}
codec := serializer.NewCodecFactory(s).LegacyCodec(externalGV)
originalData, err := runtime.Encode(codec, original)
if err != nil {
t.Errorf("unexpected error: %v", err)
}
t.Logf("originalRole = %v\n", string(originalData))
copyOfOriginal, err := s.DeepCopy(original)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
copiedData, err := runtime.Encode(codec, copyOfOriginal.(runtime.Object))
if err != nil {
t.Errorf("unexpected error: %v", err)
}
t.Logf("copyOfRole = %v\n", string(copiedData))
if !reflect.DeepEqual(original, copyOfOriginal) {
t.Errorf("expected \n%v\n, got \n%v", string(originalData), string(copiedData))
}
}

View file

@ -1,39 +0,0 @@
/*
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 runtime_test
import (
"encoding/json"
"testing"
"k8s.io/apimachinery/pkg/runtime"
)
func TestEmbeddedRawExtensionMarshal(t *testing.T) {
type test struct {
Ext runtime.RawExtension
}
extension := test{Ext: runtime.RawExtension{Raw: []byte(`{"foo":"bar"}`)}}
data, err := json.Marshal(extension)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if string(data) != `{"Ext":{"foo":"bar"}}` {
t.Errorf("unexpected data: %s", string(data))
}
}

View file

@ -1,136 +0,0 @@
/*
Copyright 2015 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package schema
import (
"testing"
)
func TestGroupVersionParse(t *testing.T) {
tests := []struct {
input string
out GroupVersion
err func(error) bool
}{
{input: "v1", out: GroupVersion{Version: "v1"}},
{input: "v2", out: GroupVersion{Version: "v2"}},
{input: "/v1", out: GroupVersion{Version: "v1"}},
{input: "v1/", out: GroupVersion{Group: "v1"}},
{input: "/v1/", err: func(err error) bool { return err.Error() == "unexpected GroupVersion string: /v1/" }},
{input: "v1/a", out: GroupVersion{Group: "v1", Version: "a"}},
}
for i, test := range tests {
out, err := ParseGroupVersion(test.input)
if test.err == nil && err != nil || err == nil && test.err != nil {
t.Errorf("%d: unexpected error: %v", i, err)
continue
}
if test.err != nil && !test.err(err) {
t.Errorf("%d: unexpected error: %v", i, err)
continue
}
if out != test.out {
t.Errorf("%d: unexpected output: %#v", i, out)
}
}
}
func TestGroupResourceParse(t *testing.T) {
tests := []struct {
input string
out GroupResource
}{
{input: "v1", out: GroupResource{Resource: "v1"}},
{input: ".v1", out: GroupResource{Group: "v1"}},
{input: "v1.", out: GroupResource{Resource: "v1"}},
{input: "v1.a", out: GroupResource{Group: "a", Resource: "v1"}},
{input: "b.v1.a", out: GroupResource{Group: "v1.a", Resource: "b"}},
}
for i, test := range tests {
out := ParseGroupResource(test.input)
if out != test.out {
t.Errorf("%d: unexpected output: %#v", i, out)
}
}
}
func TestParseResourceArg(t *testing.T) {
tests := []struct {
input string
gvr *GroupVersionResource
gr GroupResource
}{
{input: "v1", gr: GroupResource{Resource: "v1"}},
{input: ".v1", gr: GroupResource{Group: "v1"}},
{input: "v1.", gr: GroupResource{Resource: "v1"}},
{input: "v1.a", gr: GroupResource{Group: "a", Resource: "v1"}},
{input: "b.v1.a", gvr: &GroupVersionResource{Group: "a", Version: "v1", Resource: "b"}, gr: GroupResource{Group: "v1.a", Resource: "b"}},
}
for i, test := range tests {
gvr, gr := ParseResourceArg(test.input)
if (gvr != nil && test.gvr == nil) || (gvr == nil && test.gvr != nil) || (test.gvr != nil && *gvr != *test.gvr) {
t.Errorf("%d: unexpected output: %#v", i, gvr)
}
if gr != test.gr {
t.Errorf("%d: unexpected output: %#v", i, gr)
}
}
}
func TestKindForGroupVersionKinds(t *testing.T) {
gvks := GroupVersions{
GroupVersion{Group: "batch", Version: "v1"},
GroupVersion{Group: "batch", Version: "v2alpha1"},
GroupVersion{Group: "policy", Version: "v1beta1"},
}
cases := []struct {
input []GroupVersionKind
target GroupVersionKind
ok bool
}{
{
input: []GroupVersionKind{{Group: "batch", Version: "v2alpha1", Kind: "ScheduledJob"}},
target: GroupVersionKind{Group: "batch", Version: "v2alpha1", Kind: "ScheduledJob"},
ok: true,
},
{
input: []GroupVersionKind{{Group: "batch", Version: "v3alpha1", Kind: "CronJob"}},
target: GroupVersionKind{Group: "batch", Version: "v1", Kind: "CronJob"},
ok: true,
},
{
input: []GroupVersionKind{{Group: "policy", Version: "v1beta1", Kind: "PodDisruptionBudget"}},
target: GroupVersionKind{Group: "policy", Version: "v1beta1", Kind: "PodDisruptionBudget"},
ok: true,
},
{
input: []GroupVersionKind{{Group: "apps", Version: "v1alpha1", Kind: "StatefulSet"}},
target: GroupVersionKind{},
ok: false,
},
}
for i, c := range cases {
target, ok := gvks.KindForGroupVersionKinds(c.input)
if c.target != target {
t.Errorf("%d: unexpected target: %v, expected %v", i, target, c.target)
}
if c.ok != ok {
t.Errorf("%d: unexpected ok: %v, expected %v", i, ok, c.ok)
}
}
}

View file

@ -163,22 +163,13 @@ func (s *Scheme) AddUnversionedTypes(version schema.GroupVersion, types ...Objec
// the struct becomes the "kind" field when encoding. Version may not be empty - use the
// APIVersionInternal constant if you have a type that does not have a formal version.
func (s *Scheme) AddKnownTypes(gv schema.GroupVersion, types ...Object) {
if len(gv.Version) == 0 {
panic(fmt.Sprintf("version is required on all types: %s %v", gv, types[0]))
}
for _, obj := range types {
t := reflect.TypeOf(obj)
if t.Kind() != reflect.Ptr {
panic("All types must be pointers to structs.")
}
t = t.Elem()
if t.Kind() != reflect.Struct {
panic("All types must be pointers to structs.")
}
gvk := gv.WithKind(t.Name())
s.gvkToType[gvk] = t
s.typeToGVK[t] = append(s.typeToGVK[t], gvk)
s.AddKnownTypeWithName(gv.WithKind(t.Name()), obj)
}
}
@ -199,7 +190,17 @@ func (s *Scheme) AddKnownTypeWithName(gvk schema.GroupVersionKind, obj Object) {
panic("All types must be pointers to structs.")
}
if oldT, found := s.gvkToType[gvk]; found && oldT != t {
panic(fmt.Sprintf("Double registration of different types for %v: old=%v.%v, new=%v.%v", gvk, oldT.PkgPath(), oldT.Name(), t.PkgPath(), t.Name()))
}
s.gvkToType[gvk] = t
for _, existingGvk := range s.typeToGVK[t] {
if existingGvk == gvk {
return
}
}
s.typeToGVK[t] = append(s.typeToGVK[t], gvk)
}

View file

@ -1,925 +0,0 @@
/*
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 runtime_test
import (
"reflect"
"strings"
"testing"
"github.com/google/gofuzz"
flag "github.com/spf13/pflag"
"k8s.io/apimachinery/pkg/conversion"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer"
"k8s.io/apimachinery/pkg/util/diff"
)
var fuzzIters = flag.Int("fuzz-iters", 50, "How many fuzzing iterations to do.")
type InternalSimple struct {
runtime.TypeMeta `json:",inline"`
TestString string `json:"testString"`
}
type ExternalSimple struct {
runtime.TypeMeta `json:",inline"`
TestString string `json:"testString"`
}
func (obj *InternalSimple) GetObjectKind() schema.ObjectKind { return &obj.TypeMeta }
func (obj *ExternalSimple) GetObjectKind() schema.ObjectKind { return &obj.TypeMeta }
func TestScheme(t *testing.T) {
internalGV := schema.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
externalGV := schema.GroupVersion{Group: "test.group", Version: "testExternal"}
scheme := runtime.NewScheme()
scheme.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{})
scheme.AddKnownTypeWithName(externalGV.WithKind("Simple"), &ExternalSimple{})
// If set, would clear TypeMeta during conversion.
//scheme.AddIgnoredConversionType(&TypeMeta{}, &TypeMeta{})
// test that scheme is an ObjectTyper
var _ runtime.ObjectTyper = scheme
internalToExternalCalls := 0
externalToInternalCalls := 0
// Register functions to verify that scope.Meta() gets set correctly.
err := scheme.AddConversionFuncs(
func(in *InternalSimple, out *ExternalSimple, scope conversion.Scope) error {
scope.Convert(&in.TypeMeta, &out.TypeMeta, 0)
scope.Convert(&in.TestString, &out.TestString, 0)
internalToExternalCalls++
return nil
},
func(in *ExternalSimple, out *InternalSimple, scope conversion.Scope) error {
scope.Convert(&in.TypeMeta, &out.TypeMeta, 0)
scope.Convert(&in.TestString, &out.TestString, 0)
externalToInternalCalls++
return nil
},
)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
codecs := serializer.NewCodecFactory(scheme)
codec := codecs.LegacyCodec(externalGV)
info, _ := runtime.SerializerInfoForMediaType(codecs.SupportedMediaTypes(), runtime.ContentTypeJSON)
jsonserializer := info.Serializer
simple := &InternalSimple{
TestString: "foo",
}
// Test Encode, Decode, DecodeInto, and DecodeToVersion
obj := runtime.Object(simple)
data, err := runtime.Encode(codec, obj)
if err != nil {
t.Fatal(err)
}
obj2, err := runtime.Decode(codec, data)
if err != nil {
t.Fatal(err)
}
if _, ok := obj2.(*InternalSimple); !ok {
t.Fatalf("Got wrong type")
}
if e, a := simple, obj2; !reflect.DeepEqual(e, a) {
t.Errorf("Expected:\n %#v,\n Got:\n %#v", e, a)
}
obj3 := &InternalSimple{}
if err := runtime.DecodeInto(codec, data, obj3); err != nil {
t.Fatal(err)
}
// clearing TypeMeta is a function of the scheme, which we do not test here (ConvertToVersion
// does not automatically clear TypeMeta anymore).
simple.TypeMeta = runtime.TypeMeta{Kind: "Simple", APIVersion: externalGV.String()}
if e, a := simple, obj3; !reflect.DeepEqual(e, a) {
t.Errorf("Expected:\n %#v,\n Got:\n %#v", e, a)
}
obj4, err := runtime.Decode(jsonserializer, data)
if err != nil {
t.Fatal(err)
}
if _, ok := obj4.(*ExternalSimple); !ok {
t.Fatalf("Got wrong type")
}
// Test Convert
external := &ExternalSimple{}
err = scheme.Convert(simple, external, nil)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
if e, a := simple.TestString, external.TestString; e != a {
t.Errorf("Expected %v, got %v", e, a)
}
// Encode and Convert should each have caused an increment.
if e, a := 2, internalToExternalCalls; e != a {
t.Errorf("Expected %v, got %v", e, a)
}
// DecodeInto and Decode should each have caused an increment because of a conversion
if e, a := 2, externalToInternalCalls; e != a {
t.Errorf("Expected %v, got %v", e, a)
}
}
func TestBadJSONRejection(t *testing.T) {
scheme := runtime.NewScheme()
codecs := serializer.NewCodecFactory(scheme)
info, _ := runtime.SerializerInfoForMediaType(codecs.SupportedMediaTypes(), runtime.ContentTypeJSON)
jsonserializer := info.Serializer
badJSONMissingKind := []byte(`{ }`)
if _, err := runtime.Decode(jsonserializer, badJSONMissingKind); err == nil {
t.Errorf("Did not reject despite lack of kind field: %s", badJSONMissingKind)
}
badJSONUnknownType := []byte(`{"kind": "bar"}`)
if _, err1 := runtime.Decode(jsonserializer, badJSONUnknownType); err1 == nil {
t.Errorf("Did not reject despite use of unknown type: %s", badJSONUnknownType)
}
/*badJSONKindMismatch := []byte(`{"kind": "Pod"}`)
if err2 := DecodeInto(badJSONKindMismatch, &Node{}); err2 == nil {
t.Errorf("Kind is set but doesn't match the object type: %s", badJSONKindMismatch)
}*/
}
type ExtensionA struct {
runtime.TypeMeta `json:",inline"`
TestString string `json:"testString"`
}
type ExtensionB struct {
runtime.TypeMeta `json:",inline"`
TestString string `json:"testString"`
}
type ExternalExtensionType struct {
runtime.TypeMeta `json:",inline"`
Extension runtime.RawExtension `json:"extension"`
}
type InternalExtensionType struct {
runtime.TypeMeta `json:",inline"`
Extension runtime.Object `json:"extension"`
}
type ExternalOptionalExtensionType struct {
runtime.TypeMeta `json:",inline"`
Extension runtime.RawExtension `json:"extension,omitempty"`
}
type InternalOptionalExtensionType struct {
runtime.TypeMeta `json:",inline"`
Extension runtime.Object `json:"extension,omitempty"`
}
func (obj *ExtensionA) GetObjectKind() schema.ObjectKind { return &obj.TypeMeta }
func (obj *ExtensionB) GetObjectKind() schema.ObjectKind { return &obj.TypeMeta }
func (obj *ExternalExtensionType) GetObjectKind() schema.ObjectKind { return &obj.TypeMeta }
func (obj *InternalExtensionType) GetObjectKind() schema.ObjectKind { return &obj.TypeMeta }
func (obj *ExternalOptionalExtensionType) GetObjectKind() schema.ObjectKind { return &obj.TypeMeta }
func (obj *InternalOptionalExtensionType) GetObjectKind() schema.ObjectKind { return &obj.TypeMeta }
func TestExternalToInternalMapping(t *testing.T) {
internalGV := schema.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
externalGV := schema.GroupVersion{Group: "test.group", Version: "testExternal"}
scheme := runtime.NewScheme()
scheme.AddKnownTypeWithName(internalGV.WithKind("OptionalExtensionType"), &InternalOptionalExtensionType{})
scheme.AddKnownTypeWithName(externalGV.WithKind("OptionalExtensionType"), &ExternalOptionalExtensionType{})
codec := serializer.NewCodecFactory(scheme).LegacyCodec(externalGV)
table := []struct {
obj runtime.Object
encoded string
}{
{
&InternalOptionalExtensionType{Extension: nil},
`{"kind":"OptionalExtensionType","apiVersion":"` + externalGV.String() + `"}`,
},
}
for i, item := range table {
gotDecoded, err := runtime.Decode(codec, []byte(item.encoded))
if err != nil {
t.Errorf("unexpected error '%v' (%v)", err, item.encoded)
} else if e, a := item.obj, gotDecoded; !reflect.DeepEqual(e, a) {
t.Errorf("%d: unexpected objects:\n%s", i, diff.ObjectGoPrintSideBySide(e, a))
}
}
}
func TestExtensionMapping(t *testing.T) {
internalGV := schema.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
externalGV := schema.GroupVersion{Group: "test.group", Version: "testExternal"}
scheme := runtime.NewScheme()
scheme.AddKnownTypeWithName(internalGV.WithKind("ExtensionType"), &InternalExtensionType{})
scheme.AddKnownTypeWithName(internalGV.WithKind("OptionalExtensionType"), &InternalOptionalExtensionType{})
scheme.AddKnownTypeWithName(externalGV.WithKind("ExtensionType"), &ExternalExtensionType{})
scheme.AddKnownTypeWithName(externalGV.WithKind("OptionalExtensionType"), &ExternalOptionalExtensionType{})
// register external first when the object is the same in both schemes, so ObjectVersionAndKind reports the
// external version.
scheme.AddKnownTypeWithName(externalGV.WithKind("A"), &ExtensionA{})
scheme.AddKnownTypeWithName(externalGV.WithKind("B"), &ExtensionB{})
scheme.AddKnownTypeWithName(internalGV.WithKind("A"), &ExtensionA{})
scheme.AddKnownTypeWithName(internalGV.WithKind("B"), &ExtensionB{})
codec := serializer.NewCodecFactory(scheme).LegacyCodec(externalGV)
table := []struct {
obj runtime.Object
expected runtime.Object
encoded string
}{
{
&InternalExtensionType{
Extension: runtime.NewEncodable(codec, &ExtensionA{TestString: "foo"}),
},
&InternalExtensionType{
Extension: &runtime.Unknown{
Raw: []byte(`{"apiVersion":"test.group/testExternal","kind":"A","testString":"foo"}`),
ContentType: runtime.ContentTypeJSON,
},
},
// apiVersion is set in the serialized object for easier consumption by clients
`{"apiVersion":"` + externalGV.String() + `","kind":"ExtensionType","extension":{"apiVersion":"test.group/testExternal","kind":"A","testString":"foo"}}
`,
}, {
&InternalExtensionType{Extension: runtime.NewEncodable(codec, &ExtensionB{TestString: "bar"})},
&InternalExtensionType{
Extension: &runtime.Unknown{
Raw: []byte(`{"apiVersion":"test.group/testExternal","kind":"B","testString":"bar"}`),
ContentType: runtime.ContentTypeJSON,
},
},
// apiVersion is set in the serialized object for easier consumption by clients
`{"apiVersion":"` + externalGV.String() + `","kind":"ExtensionType","extension":{"apiVersion":"test.group/testExternal","kind":"B","testString":"bar"}}
`,
}, {
&InternalExtensionType{Extension: nil},
&InternalExtensionType{
Extension: nil,
},
`{"apiVersion":"` + externalGV.String() + `","kind":"ExtensionType","extension":null}
`,
},
}
for i, item := range table {
gotEncoded, err := runtime.Encode(codec, item.obj)
if err != nil {
t.Errorf("unexpected error '%v' (%#v)", err, item.obj)
} else if e, a := item.encoded, string(gotEncoded); e != a {
t.Errorf("expected\n%#v\ngot\n%#v\n", e, a)
}
gotDecoded, err := runtime.Decode(codec, []byte(item.encoded))
if err != nil {
t.Errorf("unexpected error '%v' (%v)", err, item.encoded)
} else if e, a := item.expected, gotDecoded; !reflect.DeepEqual(e, a) {
t.Errorf("%d: unexpected objects:\n%s", i, diff.ObjectGoPrintSideBySide(e, a))
}
}
}
func TestEncode(t *testing.T) {
internalGV := schema.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
externalGV := schema.GroupVersion{Group: "test.group", Version: "testExternal"}
scheme := runtime.NewScheme()
scheme.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{})
scheme.AddKnownTypeWithName(externalGV.WithKind("Simple"), &ExternalSimple{})
codec := serializer.NewCodecFactory(scheme).LegacyCodec(externalGV)
test := &InternalSimple{
TestString: "I'm the same",
}
obj := runtime.Object(test)
data, err := runtime.Encode(codec, obj)
obj2, gvk, err2 := codec.Decode(data, nil, nil)
if err != nil || err2 != nil {
t.Fatalf("Failure: '%v' '%v'", err, err2)
}
if _, ok := obj2.(*InternalSimple); !ok {
t.Fatalf("Got wrong type")
}
if !reflect.DeepEqual(obj2, test) {
t.Errorf("Expected:\n %#v,\n Got:\n %#v", test, obj2)
}
if !reflect.DeepEqual(gvk, &schema.GroupVersionKind{Group: "test.group", Version: "testExternal", Kind: "Simple"}) {
t.Errorf("unexpected gvk returned by decode: %#v", gvk)
}
}
func TestUnversionedTypes(t *testing.T) {
internalGV := schema.GroupVersion{Group: "test.group", Version: runtime.APIVersionInternal}
externalGV := schema.GroupVersion{Group: "test.group", Version: "testExternal"}
otherGV := schema.GroupVersion{Group: "group", Version: "other"}
scheme := runtime.NewScheme()
scheme.AddUnversionedTypes(externalGV, &InternalSimple{})
scheme.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{})
scheme.AddKnownTypeWithName(externalGV.WithKind("Simple"), &ExternalSimple{})
scheme.AddKnownTypeWithName(otherGV.WithKind("Simple"), &ExternalSimple{})
codec := serializer.NewCodecFactory(scheme).LegacyCodec(externalGV)
if unv, ok := scheme.IsUnversioned(&InternalSimple{}); !unv || !ok {
t.Fatalf("type not unversioned and in scheme: %t %t", unv, ok)
}
kinds, _, err := scheme.ObjectKinds(&InternalSimple{})
if err != nil {
t.Fatal(err)
}
kind := kinds[0]
if kind != externalGV.WithKind("InternalSimple") {
t.Fatalf("unexpected: %#v", kind)
}
test := &InternalSimple{
TestString: "I'm the same",
}
obj := runtime.Object(test)
data, err := runtime.Encode(codec, obj)
if err != nil {
t.Fatal(err)
}
obj2, gvk, err := codec.Decode(data, nil, nil)
if err != nil {
t.Fatal(err)
}
if _, ok := obj2.(*InternalSimple); !ok {
t.Fatalf("Got wrong type")
}
if !reflect.DeepEqual(obj2, test) {
t.Errorf("Expected:\n %#v,\n Got:\n %#v", test, obj2)
}
// object is serialized as an unversioned object (in the group and version it was defined in)
if !reflect.DeepEqual(gvk, &schema.GroupVersionKind{Group: "test.group", Version: "testExternal", Kind: "InternalSimple"}) {
t.Errorf("unexpected gvk returned by decode: %#v", gvk)
}
// when serialized to a different group, the object is kept in its preferred name
codec = serializer.NewCodecFactory(scheme).LegacyCodec(otherGV)
data, err = runtime.Encode(codec, obj)
if err != nil {
t.Fatal(err)
}
if string(data) != `{"apiVersion":"test.group/testExternal","kind":"InternalSimple","testString":"I'm the same"}`+"\n" {
t.Errorf("unexpected data: %s", data)
}
}
// Test a weird version/kind embedding format.
type MyWeirdCustomEmbeddedVersionKindField struct {
ID string `json:"ID,omitempty"`
APIVersion string `json:"myVersionKey,omitempty"`
ObjectKind string `json:"myKindKey,omitempty"`
Z string `json:"Z,omitempty"`
Y uint64 `json:"Y,omitempty"`
}
type TestType1 struct {
MyWeirdCustomEmbeddedVersionKindField `json:",inline"`
A string `json:"A,omitempty"`
B int `json:"B,omitempty"`
C int8 `json:"C,omitempty"`
D int16 `json:"D,omitempty"`
E int32 `json:"E,omitempty"`
F int64 `json:"F,omitempty"`
G uint `json:"G,omitempty"`
H uint8 `json:"H,omitempty"`
I uint16 `json:"I,omitempty"`
J uint32 `json:"J,omitempty"`
K uint64 `json:"K,omitempty"`
L bool `json:"L,omitempty"`
M map[string]int `json:"M,omitempty"`
N map[string]TestType2 `json:"N,omitempty"`
O *TestType2 `json:"O,omitempty"`
P []TestType2 `json:"Q,omitempty"`
}
type TestType2 struct {
A string `json:"A,omitempty"`
B int `json:"B,omitempty"`
}
type ExternalTestType2 struct {
A string `json:"A,omitempty"`
B int `json:"B,omitempty"`
}
type ExternalTestType1 struct {
MyWeirdCustomEmbeddedVersionKindField `json:",inline"`
A string `json:"A,omitempty"`
B int `json:"B,omitempty"`
C int8 `json:"C,omitempty"`
D int16 `json:"D,omitempty"`
E int32 `json:"E,omitempty"`
F int64 `json:"F,omitempty"`
G uint `json:"G,omitempty"`
H uint8 `json:"H,omitempty"`
I uint16 `json:"I,omitempty"`
J uint32 `json:"J,omitempty"`
K uint64 `json:"K,omitempty"`
L bool `json:"L,omitempty"`
M map[string]int `json:"M,omitempty"`
N map[string]ExternalTestType2 `json:"N,omitempty"`
O *ExternalTestType2 `json:"O,omitempty"`
P []ExternalTestType2 `json:"Q,omitempty"`
}
type ExternalInternalSame struct {
MyWeirdCustomEmbeddedVersionKindField `json:",inline"`
A TestType2 `json:"A,omitempty"`
}
type UnversionedType struct {
MyWeirdCustomEmbeddedVersionKindField `json:",inline"`
A string `json:"A,omitempty"`
}
type UnknownType struct {
MyWeirdCustomEmbeddedVersionKindField `json:",inline"`
A string `json:"A,omitempty"`
}
func (obj *MyWeirdCustomEmbeddedVersionKindField) GetObjectKind() schema.ObjectKind { return obj }
func (obj *MyWeirdCustomEmbeddedVersionKindField) SetGroupVersionKind(gvk schema.GroupVersionKind) {
obj.APIVersion, obj.ObjectKind = gvk.ToAPIVersionAndKind()
}
func (obj *MyWeirdCustomEmbeddedVersionKindField) GroupVersionKind() schema.GroupVersionKind {
return schema.FromAPIVersionAndKind(obj.APIVersion, obj.ObjectKind)
}
func (obj *ExternalInternalSame) GetObjectKind() schema.ObjectKind {
return &obj.MyWeirdCustomEmbeddedVersionKindField
}
func (obj *TestType1) GetObjectKind() schema.ObjectKind {
return &obj.MyWeirdCustomEmbeddedVersionKindField
}
func (obj *ExternalTestType1) GetObjectKind() schema.ObjectKind {
return &obj.MyWeirdCustomEmbeddedVersionKindField
}
func (obj *TestType2) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind }
func (obj *ExternalTestType2) GetObjectKind() schema.ObjectKind {
return schema.EmptyObjectKind
}
// TestObjectFuzzer can randomly populate all the above objects.
var TestObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 100).Funcs(
func(j *MyWeirdCustomEmbeddedVersionKindField, c fuzz.Continue) {
// We have to customize the randomization of MyWeirdCustomEmbeddedVersionKindFields because their
// APIVersion and Kind must remain blank in memory.
j.APIVersion = ""
j.ObjectKind = ""
j.ID = c.RandString()
},
)
// Returns a new Scheme set up with the test objects.
func GetTestScheme() *runtime.Scheme {
internalGV := schema.GroupVersion{Version: "__internal"}
externalGV := schema.GroupVersion{Version: "v1"}
alternateExternalGV := schema.GroupVersion{Group: "custom", Version: "v1"}
differentExternalGV := schema.GroupVersion{Group: "other", Version: "v2"}
s := runtime.NewScheme()
// Ordinarily, we wouldn't add TestType2, but because this is a test and
// both types are from the same package, we need to get it into the system
// so that converter will match it with ExternalType2.
s.AddKnownTypes(internalGV, &TestType1{}, &TestType2{}, &ExternalInternalSame{})
s.AddKnownTypes(externalGV, &ExternalInternalSame{})
s.AddKnownTypeWithName(externalGV.WithKind("TestType1"), &ExternalTestType1{})
s.AddKnownTypeWithName(externalGV.WithKind("TestType2"), &ExternalTestType2{})
s.AddKnownTypeWithName(internalGV.WithKind("TestType3"), &TestType1{})
s.AddKnownTypeWithName(externalGV.WithKind("TestType3"), &ExternalTestType1{})
s.AddKnownTypeWithName(externalGV.WithKind("TestType4"), &ExternalTestType1{})
s.AddKnownTypeWithName(alternateExternalGV.WithKind("TestType3"), &ExternalTestType1{})
s.AddKnownTypeWithName(alternateExternalGV.WithKind("TestType5"), &ExternalTestType1{})
s.AddKnownTypeWithName(differentExternalGV.WithKind("TestType1"), &ExternalTestType1{})
s.AddUnversionedTypes(externalGV, &UnversionedType{})
return s
}
func TestKnownTypes(t *testing.T) {
s := GetTestScheme()
if len(s.KnownTypes(schema.GroupVersion{Group: "group", Version: "v2"})) != 0 {
t.Errorf("should have no known types for v2")
}
types := s.KnownTypes(schema.GroupVersion{Version: "v1"})
for _, s := range []string{"TestType1", "TestType2", "TestType3", "ExternalInternalSame"} {
if _, ok := types[s]; !ok {
t.Errorf("missing type %q", s)
}
}
}
func TestConvertToVersionBasic(t *testing.T) {
s := GetTestScheme()
tt := &TestType1{A: "I'm not a pointer object"}
other, err := s.ConvertToVersion(tt, schema.GroupVersion{Version: "v1"})
if err != nil {
t.Fatalf("Failure: %v", err)
}
converted, ok := other.(*ExternalTestType1)
if !ok {
t.Fatalf("Got wrong type: %T", other)
}
if tt.A != converted.A {
t.Fatalf("Failed to convert object correctly: %#v", converted)
}
}
type testGroupVersioner struct {
target schema.GroupVersionKind
ok bool
}
func (m testGroupVersioner) KindForGroupVersionKinds(kinds []schema.GroupVersionKind) (schema.GroupVersionKind, bool) {
return m.target, m.ok
}
func TestConvertToVersion(t *testing.T) {
testCases := []struct {
scheme *runtime.Scheme
in runtime.Object
gv runtime.GroupVersioner
same bool
out runtime.Object
errFn func(error) bool
}{
// errors if the type is not registered in the scheme
{
scheme: GetTestScheme(),
in: &UnknownType{},
errFn: func(err error) bool { return err != nil && runtime.IsNotRegisteredError(err) },
},
// errors if the group versioner returns no target
{
scheme: GetTestScheme(),
in: &ExternalTestType1{A: "test"},
gv: testGroupVersioner{},
errFn: func(err error) bool {
return err != nil && strings.Contains(err.Error(), "is not suitable for converting")
},
},
// converts to internal
{
scheme: GetTestScheme(),
in: &ExternalTestType1{A: "test"},
gv: schema.GroupVersion{Version: "__internal"},
out: &TestType1{A: "test"},
},
// prefers the best match
{
scheme: GetTestScheme(),
in: &ExternalTestType1{A: "test"},
gv: schema.GroupVersions{{Version: "__internal"}, {Version: "v1"}},
out: &ExternalTestType1{
MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType1"},
A: "test",
},
},
// unversioned type returned as-is
{
scheme: GetTestScheme(),
in: &UnversionedType{A: "test"},
gv: schema.GroupVersions{{Version: "v1"}},
same: true,
out: &UnversionedType{
MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "UnversionedType"},
A: "test",
},
},
// unversioned type returned when not included in the target types
{
scheme: GetTestScheme(),
in: &UnversionedType{A: "test"},
gv: schema.GroupVersions{{Group: "other", Version: "v2"}},
same: true,
out: &UnversionedType{
MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "UnversionedType"},
A: "test",
},
},
// detected as already being in the target version
{
scheme: GetTestScheme(),
in: &ExternalTestType1{A: "test"},
gv: schema.GroupVersions{{Version: "v1"}},
same: true,
out: &ExternalTestType1{
MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType1"},
A: "test",
},
},
// detected as already being in the first target version
{
scheme: GetTestScheme(),
in: &ExternalTestType1{A: "test"},
gv: schema.GroupVersions{{Version: "v1"}, {Version: "__internal"}},
same: true,
out: &ExternalTestType1{
MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType1"},
A: "test",
},
},
// detected as already being in the first target version
{
scheme: GetTestScheme(),
in: &ExternalTestType1{A: "test"},
gv: schema.GroupVersions{{Version: "v1"}, {Version: "__internal"}},
same: true,
out: &ExternalTestType1{
MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType1"},
A: "test",
},
},
// the external type is registered in multiple groups, versions, and kinds, and can be targeted to all of them (1/3): different kind
{
scheme: GetTestScheme(),
in: &ExternalTestType1{A: "test"},
gv: testGroupVersioner{ok: true, target: schema.GroupVersionKind{Kind: "TestType3", Version: "v1"}},
same: true,
out: &ExternalTestType1{
MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType3"},
A: "test",
},
},
// the external type is registered in multiple groups, versions, and kinds, and can be targeted to all of them (2/3): different gv
{
scheme: GetTestScheme(),
in: &ExternalTestType1{A: "test"},
gv: testGroupVersioner{ok: true, target: schema.GroupVersionKind{Kind: "TestType3", Group: "custom", Version: "v1"}},
same: true,
out: &ExternalTestType1{
MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "custom/v1", ObjectKind: "TestType3"},
A: "test",
},
},
// the external type is registered in multiple groups, versions, and kinds, and can be targeted to all of them (3/3): different gvk
{
scheme: GetTestScheme(),
in: &ExternalTestType1{A: "test"},
gv: testGroupVersioner{ok: true, target: schema.GroupVersionKind{Group: "custom", Version: "v1", Kind: "TestType5"}},
same: true,
out: &ExternalTestType1{
MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "custom/v1", ObjectKind: "TestType5"},
A: "test",
},
},
// multi group versioner recognizes multiple groups and forces the output to a particular version, copies because version differs
{
scheme: GetTestScheme(),
in: &ExternalTestType1{A: "test"},
gv: runtime.NewMultiGroupVersioner(schema.GroupVersion{Group: "other", Version: "v2"}, schema.GroupKind{Group: "custom", Kind: "TestType3"}, schema.GroupKind{Kind: "TestType1"}),
out: &ExternalTestType1{
MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "other/v2", ObjectKind: "TestType1"},
A: "test",
},
},
// multi group versioner recognizes multiple groups and forces the output to a particular version, copies because version differs
{
scheme: GetTestScheme(),
in: &ExternalTestType1{A: "test"},
gv: runtime.NewMultiGroupVersioner(schema.GroupVersion{Group: "other", Version: "v2"}, schema.GroupKind{Kind: "TestType1"}, schema.GroupKind{Group: "custom", Kind: "TestType3"}),
out: &ExternalTestType1{
MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "other/v2", ObjectKind: "TestType1"},
A: "test",
},
},
// multi group versioner is unable to find a match when kind AND group don't match (there is no TestType1 kind in group "other", and no kind "TestType5" in the default group)
{
scheme: GetTestScheme(),
in: &TestType1{A: "test"},
gv: runtime.NewMultiGroupVersioner(schema.GroupVersion{Group: "custom", Version: "v1"}, schema.GroupKind{Group: "other"}, schema.GroupKind{Kind: "TestType5"}),
errFn: func(err error) bool {
return err != nil && strings.Contains(err.Error(), "is not suitable for converting")
},
},
// multi group versioner recognizes multiple groups and forces the output to a particular version, performs no copy
{
scheme: GetTestScheme(),
in: &ExternalTestType1{A: "test"},
gv: runtime.NewMultiGroupVersioner(schema.GroupVersion{Group: "", Version: "v1"}, schema.GroupKind{Group: "custom", Kind: "TestType3"}, schema.GroupKind{Kind: "TestType1"}),
same: true,
out: &ExternalTestType1{
MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType1"},
A: "test",
},
},
// multi group versioner recognizes multiple groups and forces the output to a particular version, performs no copy
{
scheme: GetTestScheme(),
in: &ExternalTestType1{A: "test"},
gv: runtime.NewMultiGroupVersioner(schema.GroupVersion{Group: "", Version: "v1"}, schema.GroupKind{Kind: "TestType1"}, schema.GroupKind{Group: "custom", Kind: "TestType3"}),
same: true,
out: &ExternalTestType1{
MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType1"},
A: "test",
},
},
// group versioner can choose a particular target kind for a given input when kind is the same across group versions
{
scheme: GetTestScheme(),
in: &TestType1{A: "test"},
gv: testGroupVersioner{ok: true, target: schema.GroupVersionKind{Version: "v1", Kind: "TestType3"}},
out: &ExternalTestType1{
MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "v1", ObjectKind: "TestType3"},
A: "test",
},
},
// group versioner can choose a different kind
{
scheme: GetTestScheme(),
in: &TestType1{A: "test"},
gv: testGroupVersioner{ok: true, target: schema.GroupVersionKind{Kind: "TestType5", Group: "custom", Version: "v1"}},
out: &ExternalTestType1{
MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{APIVersion: "custom/v1", ObjectKind: "TestType5"},
A: "test",
},
},
}
for i, test := range testCases {
original, _ := test.scheme.DeepCopy(test.in)
out, err := test.scheme.ConvertToVersion(test.in, test.gv)
switch {
case test.errFn != nil:
if !test.errFn(err) {
t.Errorf("%d: unexpected error: %v", i, err)
}
continue
case err != nil:
t.Errorf("%d: unexpected error: %v", i, err)
continue
}
if out == test.in {
t.Errorf("%d: ConvertToVersion should always copy out: %#v", i, out)
continue
}
if test.same {
if !reflect.DeepEqual(original, test.in) {
t.Errorf("%d: unexpected mutation of input: %s", i, diff.ObjectReflectDiff(original, test.in))
continue
}
if !reflect.DeepEqual(out, test.out) {
t.Errorf("%d: unexpected out: %s", i, diff.ObjectReflectDiff(out, test.out))
continue
}
unsafe, err := test.scheme.UnsafeConvertToVersion(test.in, test.gv)
if err != nil {
t.Errorf("%d: unexpected error: %v", i, err)
continue
}
if !reflect.DeepEqual(unsafe, test.out) {
t.Errorf("%d: unexpected unsafe: %s", i, diff.ObjectReflectDiff(unsafe, test.out))
continue
}
if unsafe != test.in {
t.Errorf("%d: UnsafeConvertToVersion should return same object: %#v", i, unsafe)
continue
}
continue
}
if !reflect.DeepEqual(out, test.out) {
t.Errorf("%d: unexpected out: %s", i, diff.ObjectReflectDiff(out, test.out))
continue
}
}
}
func TestMetaValues(t *testing.T) {
internalGV := schema.GroupVersion{Group: "test.group", Version: "__internal"}
externalGV := schema.GroupVersion{Group: "test.group", Version: "externalVersion"}
s := runtime.NewScheme()
s.AddKnownTypeWithName(internalGV.WithKind("Simple"), &InternalSimple{})
s.AddKnownTypeWithName(externalGV.WithKind("Simple"), &ExternalSimple{})
internalToExternalCalls := 0
externalToInternalCalls := 0
// Register functions to verify that scope.Meta() gets set correctly.
err := s.AddConversionFuncs(
func(in *InternalSimple, out *ExternalSimple, scope conversion.Scope) error {
t.Logf("internal -> external")
scope.Convert(&in.TestString, &out.TestString, 0)
internalToExternalCalls++
return nil
},
func(in *ExternalSimple, out *InternalSimple, scope conversion.Scope) error {
t.Logf("external -> internal")
scope.Convert(&in.TestString, &out.TestString, 0)
externalToInternalCalls++
return nil
},
)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
simple := &InternalSimple{
TestString: "foo",
}
s.Log(t)
out, err := s.ConvertToVersion(simple, externalGV)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
internal, err := s.ConvertToVersion(out, internalGV)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if e, a := simple, internal; !reflect.DeepEqual(e, a) {
t.Errorf("Expected:\n %#v,\n Got:\n %#v", e, a)
}
if e, a := 1, internalToExternalCalls; e != a {
t.Errorf("Expected %v, got %v", e, a)
}
if e, a := 1, externalToInternalCalls; e != a {
t.Errorf("Expected %v, got %v", e, a)
}
}
func TestMetaValuesUnregisteredConvert(t *testing.T) {
type InternalSimple struct {
Version string `json:"apiVersion,omitempty"`
Kind string `json:"kind,omitempty"`
TestString string `json:"testString"`
}
type ExternalSimple struct {
Version string `json:"apiVersion,omitempty"`
Kind string `json:"kind,omitempty"`
TestString string `json:"testString"`
}
s := runtime.NewScheme()
// We deliberately don't register the types.
internalToExternalCalls := 0
// Register functions to verify that scope.Meta() gets set correctly.
err := s.AddConversionFuncs(
func(in *InternalSimple, out *ExternalSimple, scope conversion.Scope) error {
scope.Convert(&in.TestString, &out.TestString, 0)
internalToExternalCalls++
return nil
},
)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
simple := &InternalSimple{TestString: "foo"}
external := &ExternalSimple{}
err = s.Convert(simple, external, nil)
if err != nil {
t.Fatalf("Unexpected error: %v", err)
}
if e, a := simple.TestString, external.TestString; e != a {
t.Errorf("Expected %v, got %v", e, a)
}
// Verify that our conversion handler got called.
if e, a := 1, internalToExternalCalls; e != a {
t.Errorf("Expected %v, got %v", e, a)
}
}

View file

@ -1,426 +0,0 @@
/*
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 serializer
import (
"encoding/json"
"fmt"
"log"
"os"
"reflect"
"strings"
"testing"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/conversion"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/diff"
"github.com/ghodss/yaml"
"github.com/google/gofuzz"
flag "github.com/spf13/pflag"
)
var fuzzIters = flag.Int("fuzz-iters", 50, "How many fuzzing iterations to do.")
type testMetaFactory struct{}
func (testMetaFactory) Interpret(data []byte) (*schema.GroupVersionKind, error) {
findKind := struct {
APIVersion string `json:"myVersionKey,omitempty"`
ObjectKind string `json:"myKindKey,omitempty"`
}{}
// yaml is a superset of json, so we use it to decode here. That way,
// we understand both.
if err := yaml.Unmarshal(data, &findKind); err != nil {
return nil, fmt.Errorf("couldn't get version/kind: %v", err)
}
gv, err := schema.ParseGroupVersion(findKind.APIVersion)
if err != nil {
return nil, err
}
return &schema.GroupVersionKind{Group: gv.Group, Version: gv.Version, Kind: findKind.ObjectKind}, nil
}
// Test a weird version/kind embedding format.
type MyWeirdCustomEmbeddedVersionKindField struct {
ID string `json:"ID,omitempty"`
APIVersion string `json:"myVersionKey,omitempty"`
ObjectKind string `json:"myKindKey,omitempty"`
Z string `json:"Z,omitempty"`
Y uint64 `json:"Y,omitempty"`
}
type TestType1 struct {
MyWeirdCustomEmbeddedVersionKindField `json:",inline"`
A string `json:"A,omitempty"`
B int `json:"B,omitempty"`
C int8 `json:"C,omitempty"`
D int16 `json:"D,omitempty"`
E int32 `json:"E,omitempty"`
F int64 `json:"F,omitempty"`
G uint `json:"G,omitempty"`
H uint8 `json:"H,omitempty"`
I uint16 `json:"I,omitempty"`
J uint32 `json:"J,omitempty"`
K uint64 `json:"K,omitempty"`
L bool `json:"L,omitempty"`
M map[string]int `json:"M,omitempty"`
N map[string]TestType2 `json:"N,omitempty"`
O *TestType2 `json:"O,omitempty"`
P []TestType2 `json:"Q,omitempty"`
}
type TestType2 struct {
A string `json:"A,omitempty"`
B int `json:"B,omitempty"`
}
type ExternalTestType2 struct {
A string `json:"A,omitempty"`
B int `json:"B,omitempty"`
}
type ExternalTestType1 struct {
MyWeirdCustomEmbeddedVersionKindField `json:",inline"`
A string `json:"A,omitempty"`
B int `json:"B,omitempty"`
C int8 `json:"C,omitempty"`
D int16 `json:"D,omitempty"`
E int32 `json:"E,omitempty"`
F int64 `json:"F,omitempty"`
G uint `json:"G,omitempty"`
H uint8 `json:"H,omitempty"`
I uint16 `json:"I,omitempty"`
J uint32 `json:"J,omitempty"`
K uint64 `json:"K,omitempty"`
L bool `json:"L,omitempty"`
M map[string]int `json:"M,omitempty"`
N map[string]ExternalTestType2 `json:"N,omitempty"`
O *ExternalTestType2 `json:"O,omitempty"`
P []ExternalTestType2 `json:"Q,omitempty"`
}
type ExternalInternalSame struct {
MyWeirdCustomEmbeddedVersionKindField `json:",inline"`
A TestType2 `json:"A,omitempty"`
}
// TestObjectFuzzer can randomly populate all the above objects.
var TestObjectFuzzer = fuzz.New().NilChance(.5).NumElements(1, 100).Funcs(
func(j *MyWeirdCustomEmbeddedVersionKindField, c fuzz.Continue) {
c.FuzzNoCustom(j)
j.APIVersion = ""
j.ObjectKind = ""
},
)
func (obj *MyWeirdCustomEmbeddedVersionKindField) GetObjectKind() schema.ObjectKind { return obj }
func (obj *MyWeirdCustomEmbeddedVersionKindField) SetGroupVersionKind(gvk schema.GroupVersionKind) {
obj.APIVersion, obj.ObjectKind = gvk.ToAPIVersionAndKind()
}
func (obj *MyWeirdCustomEmbeddedVersionKindField) GroupVersionKind() schema.GroupVersionKind {
return schema.FromAPIVersionAndKind(obj.APIVersion, obj.ObjectKind)
}
func (obj *ExternalInternalSame) GetObjectKind() schema.ObjectKind {
return &obj.MyWeirdCustomEmbeddedVersionKindField
}
func (obj *TestType1) GetObjectKind() schema.ObjectKind {
return &obj.MyWeirdCustomEmbeddedVersionKindField
}
func (obj *ExternalTestType1) GetObjectKind() schema.ObjectKind {
return &obj.MyWeirdCustomEmbeddedVersionKindField
}
func (obj *TestType2) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind }
func (obj *ExternalTestType2) GetObjectKind() schema.ObjectKind {
return schema.EmptyObjectKind
}
// Returns a new Scheme set up with the test objects.
func GetTestScheme() (*runtime.Scheme, runtime.Codec) {
internalGV := schema.GroupVersion{Version: runtime.APIVersionInternal}
externalGV := schema.GroupVersion{Version: "v1"}
externalGV2 := schema.GroupVersion{Version: "v2"}
s := runtime.NewScheme()
// Ordinarily, we wouldn't add TestType2, but because this is a test and
// both types are from the same package, we need to get it into the system
// so that converter will match it with ExternalType2.
s.AddKnownTypes(internalGV, &TestType1{}, &TestType2{}, &ExternalInternalSame{})
s.AddKnownTypes(externalGV, &ExternalInternalSame{})
s.AddKnownTypeWithName(externalGV.WithKind("TestType1"), &ExternalTestType1{})
s.AddKnownTypeWithName(externalGV.WithKind("TestType2"), &ExternalTestType2{})
s.AddKnownTypeWithName(internalGV.WithKind("TestType3"), &TestType1{})
s.AddKnownTypeWithName(externalGV.WithKind("TestType3"), &ExternalTestType1{})
s.AddKnownTypeWithName(externalGV2.WithKind("TestType1"), &ExternalTestType1{})
s.AddUnversionedTypes(externalGV, &metav1.Status{})
cf := newCodecFactory(s, newSerializersForScheme(s, testMetaFactory{}))
codec := cf.LegacyCodec(schema.GroupVersion{Version: "v1"})
return s, codec
}
var semantic = conversion.EqualitiesOrDie(
func(a, b MyWeirdCustomEmbeddedVersionKindField) bool {
a.APIVersion, a.ObjectKind = "", ""
b.APIVersion, b.ObjectKind = "", ""
return a == b
},
)
func runTest(t *testing.T, source interface{}) {
name := reflect.TypeOf(source).Elem().Name()
TestObjectFuzzer.Fuzz(source)
_, codec := GetTestScheme()
data, err := runtime.Encode(codec, source.(runtime.Object))
if err != nil {
t.Errorf("%v: %v (%#v)", name, err, source)
return
}
obj2, err := runtime.Decode(codec, data)
if err != nil {
t.Errorf("%v: %v (%v)", name, err, string(data))
return
}
if !semantic.DeepEqual(source, obj2) {
t.Errorf("1: %v: diff: %v", name, diff.ObjectGoPrintSideBySide(source, obj2))
return
}
obj3 := reflect.New(reflect.TypeOf(source).Elem()).Interface()
if err := runtime.DecodeInto(codec, data, obj3.(runtime.Object)); err != nil {
t.Errorf("2: %v: %v", name, err)
return
}
if !semantic.DeepEqual(source, obj3) {
t.Errorf("3: %v: diff: %v", name, diff.ObjectDiff(source, obj3))
return
}
}
func TestTypes(t *testing.T) {
table := []interface{}{
&TestType1{},
&ExternalInternalSame{},
}
for _, item := range table {
// Try a few times, since runTest uses random values.
for i := 0; i < *fuzzIters; i++ {
runTest(t, item)
}
}
}
func TestVersionedEncoding(t *testing.T) {
s, _ := GetTestScheme()
cf := newCodecFactory(s, newSerializersForScheme(s, testMetaFactory{}))
info, _ := runtime.SerializerInfoForMediaType(cf.SupportedMediaTypes(), runtime.ContentTypeJSON)
encoder := info.Serializer
codec := cf.CodecForVersions(encoder, nil, schema.GroupVersion{Version: "v2"}, nil)
out, err := runtime.Encode(codec, &TestType1{})
if err != nil {
t.Fatal(err)
}
if string(out) != `{"myVersionKey":"v2","myKindKey":"TestType1"}`+"\n" {
t.Fatal(string(out))
}
codec = cf.CodecForVersions(encoder, nil, schema.GroupVersion{Version: "v3"}, nil)
_, err = runtime.Encode(codec, &TestType1{})
if err == nil {
t.Fatal(err)
}
// unversioned encode with no versions is written directly to wire
codec = cf.CodecForVersions(encoder, nil, runtime.InternalGroupVersioner, nil)
out, err = runtime.Encode(codec, &TestType1{})
if err != nil {
t.Fatal(err)
}
if string(out) != `{}`+"\n" {
t.Fatal(string(out))
}
}
func TestMultipleNames(t *testing.T) {
_, codec := GetTestScheme()
obj, _, err := codec.Decode([]byte(`{"myKindKey":"TestType3","myVersionKey":"v1","A":"value"}`), nil, nil)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
internal := obj.(*TestType1)
if internal.A != "value" {
t.Fatalf("unexpected decoded object: %#v", internal)
}
out, err := runtime.Encode(codec, internal)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !strings.Contains(string(out), `"myKindKey":"TestType1"`) {
t.Errorf("unexpected encoded output: %s", string(out))
}
}
func TestConvertTypesWhenDefaultNamesMatch(t *testing.T) {
internalGV := schema.GroupVersion{Version: runtime.APIVersionInternal}
externalGV := schema.GroupVersion{Version: "v1"}
s := runtime.NewScheme()
// create two names internally, with TestType1 being preferred
s.AddKnownTypeWithName(internalGV.WithKind("TestType1"), &TestType1{})
s.AddKnownTypeWithName(internalGV.WithKind("OtherType1"), &TestType1{})
// create two names externally, with TestType1 being preferred
s.AddKnownTypeWithName(externalGV.WithKind("TestType1"), &ExternalTestType1{})
s.AddKnownTypeWithName(externalGV.WithKind("OtherType1"), &ExternalTestType1{})
ext := &ExternalTestType1{}
ext.APIVersion = "v1"
ext.ObjectKind = "OtherType1"
ext.A = "test"
data, err := json.Marshal(ext)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
expect := &TestType1{A: "test"}
codec := newCodecFactory(s, newSerializersForScheme(s, testMetaFactory{})).LegacyCodec(schema.GroupVersion{Version: "v1"})
obj, err := runtime.Decode(codec, data)
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !semantic.DeepEqual(expect, obj) {
t.Errorf("unexpected object: %#v", obj)
}
into := &TestType1{}
if err := runtime.DecodeInto(codec, data, into); err != nil {
t.Fatalf("unexpected error: %v", err)
}
if !semantic.DeepEqual(expect, into) {
t.Errorf("unexpected object: %#v", obj)
}
}
func TestEncode_Ptr(t *testing.T) {
_, codec := GetTestScheme()
tt := &TestType1{A: "I am a pointer object"}
data, err := runtime.Encode(codec, tt)
obj2, err2 := runtime.Decode(codec, data)
if err != nil || err2 != nil {
t.Fatalf("Failure: '%v' '%v'\n%s", err, err2, data)
}
if _, ok := obj2.(*TestType1); !ok {
t.Fatalf("Got wrong type")
}
if !semantic.DeepEqual(obj2, tt) {
t.Errorf("Expected:\n %#v,\n Got:\n %#v", tt, obj2)
}
}
func TestBadJSONRejection(t *testing.T) {
log.SetOutput(os.Stderr)
_, codec := GetTestScheme()
badJSONs := [][]byte{
[]byte(`{"myVersionKey":"v1"}`), // Missing kind
[]byte(`{"myVersionKey":"v1","myKindKey":"bar"}`), // Unknown kind
[]byte(`{"myVersionKey":"bar","myKindKey":"TestType1"}`), // Unknown version
[]byte(`{"myKindKey":"TestType1"}`), // Missing version
}
for _, b := range badJSONs {
if _, err := runtime.Decode(codec, b); err == nil {
t.Errorf("Did not reject bad json: %s", string(b))
}
}
badJSONKindMismatch := []byte(`{"myVersionKey":"v1","myKindKey":"ExternalInternalSame"}`)
if err := runtime.DecodeInto(codec, badJSONKindMismatch, &TestType1{}); err == nil {
t.Errorf("Kind is set but doesn't match the object type: %s", badJSONKindMismatch)
}
if err := runtime.DecodeInto(codec, []byte(``), &TestType1{}); err != nil {
t.Errorf("Should allow empty decode: %v", err)
}
if _, _, err := codec.Decode([]byte(``), &schema.GroupVersionKind{Kind: "ExternalInternalSame"}, nil); err == nil {
t.Errorf("Did not give error for empty data with only kind default")
}
if _, _, err := codec.Decode([]byte(`{"myVersionKey":"v1"}`), &schema.GroupVersionKind{Kind: "ExternalInternalSame"}, nil); err != nil {
t.Errorf("Gave error for version and kind default")
}
if _, _, err := codec.Decode([]byte(`{"myKindKey":"ExternalInternalSame"}`), &schema.GroupVersionKind{Version: "v1"}, nil); err != nil {
t.Errorf("Gave error for version and kind default")
}
if _, _, err := codec.Decode([]byte(``), &schema.GroupVersionKind{Kind: "ExternalInternalSame", Version: "v1"}, nil); err != nil {
t.Errorf("Gave error for version and kind defaulted: %v", err)
}
if _, err := runtime.Decode(codec, []byte(``)); err == nil {
t.Errorf("Did not give error for empty data")
}
}
// Returns a new Scheme set up with the test objects needed by TestDirectCodec.
func GetDirectCodecTestScheme() *runtime.Scheme {
internalGV := schema.GroupVersion{Version: runtime.APIVersionInternal}
externalGV := schema.GroupVersion{Version: "v1"}
s := runtime.NewScheme()
// Ordinarily, we wouldn't add TestType2, but because this is a test and
// both types are from the same package, we need to get it into the system
// so that converter will match it with ExternalType2.
s.AddKnownTypes(internalGV, &TestType1{})
s.AddKnownTypes(externalGV, &ExternalTestType1{})
s.AddUnversionedTypes(externalGV, &metav1.Status{})
return s
}
func TestDirectCodec(t *testing.T) {
s := GetDirectCodecTestScheme()
cf := newCodecFactory(s, newSerializersForScheme(s, testMetaFactory{}))
info, _ := runtime.SerializerInfoForMediaType(cf.SupportedMediaTypes(), runtime.ContentTypeJSON)
serializer := info.Serializer
df := DirectCodecFactory{cf}
ignoredGV, err := schema.ParseGroupVersion("ignored group/ignored version")
if err != nil {
t.Fatal(err)
}
directEncoder := df.EncoderForVersion(serializer, ignoredGV)
directDecoder := df.DecoderToVersion(serializer, ignoredGV)
out, err := runtime.Encode(directEncoder, &ExternalTestType1{})
if err != nil {
t.Fatal(err)
}
if string(out) != `{"myVersionKey":"v1","myKindKey":"ExternalTestType1"}`+"\n" {
t.Fatal(string(out))
}
a, _, err := directDecoder.Decode(out, nil, nil)
e := &ExternalTestType1{
MyWeirdCustomEmbeddedVersionKindField: MyWeirdCustomEmbeddedVersionKindField{
APIVersion: "v1",
ObjectKind: "ExternalTestType1",
},
}
if !semantic.DeepEqual(e, a) {
t.Fatalf("expect %v, got %v", e, a)
}
}

View file

@ -1,264 +0,0 @@
/*
Copyright 2015 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package json_test
import (
"fmt"
"reflect"
"strings"
"testing"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer/json"
"k8s.io/apimachinery/pkg/util/diff"
)
type testDecodable struct {
Other string
Value int `json:"value"`
gvk schema.GroupVersionKind
}
func (d *testDecodable) GetObjectKind() schema.ObjectKind { return d }
func (d *testDecodable) SetGroupVersionKind(gvk schema.GroupVersionKind) { d.gvk = gvk }
func (d *testDecodable) GroupVersionKind() schema.GroupVersionKind { return d.gvk }
func TestDecode(t *testing.T) {
testCases := []struct {
creater runtime.ObjectCreater
typer runtime.ObjectTyper
yaml bool
pretty bool
data []byte
defaultGVK *schema.GroupVersionKind
into runtime.Object
errFn func(error) bool
expectedObject runtime.Object
expectedGVK *schema.GroupVersionKind
}{
{
data: []byte("{}"),
expectedGVK: &schema.GroupVersionKind{},
errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'Kind' is missing in") },
},
{
data: []byte("{}"),
defaultGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
creater: &mockCreater{err: fmt.Errorf("fake error")},
expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
errFn: func(err error) bool { return err.Error() == "fake error" },
},
{
data: []byte("{}"),
defaultGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
creater: &mockCreater{obj: &testDecodable{}},
expectedObject: &testDecodable{},
expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
},
// version without group is not defaulted
{
data: []byte(`{"apiVersion":"blah"}`),
defaultGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
creater: &mockCreater{obj: &testDecodable{}},
expectedObject: &testDecodable{},
expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "", Version: "blah"},
},
// group without version is defaulted
{
data: []byte(`{"apiVersion":"other/"}`),
defaultGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
creater: &mockCreater{obj: &testDecodable{}},
expectedObject: &testDecodable{},
expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
},
// accept runtime.Unknown as into and bypass creator
{
data: []byte(`{}`),
into: &runtime.Unknown{},
expectedGVK: &schema.GroupVersionKind{},
expectedObject: &runtime.Unknown{
Raw: []byte(`{}`),
ContentType: runtime.ContentTypeJSON,
},
},
{
data: []byte(`{"test":"object"}`),
into: &runtime.Unknown{},
expectedGVK: &schema.GroupVersionKind{},
expectedObject: &runtime.Unknown{
Raw: []byte(`{"test":"object"}`),
ContentType: runtime.ContentTypeJSON,
},
},
{
data: []byte(`{"test":"object"}`),
into: &runtime.Unknown{},
defaultGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
expectedObject: &runtime.Unknown{
TypeMeta: runtime.TypeMeta{APIVersion: "other/blah", Kind: "Test"},
Raw: []byte(`{"test":"object"}`),
ContentType: runtime.ContentTypeJSON,
},
},
// unregistered objects can be decoded into directly
{
data: []byte(`{"kind":"Test","apiVersion":"other/blah","value":1,"Other":"test"}`),
into: &testDecodable{},
typer: &mockTyper{err: runtime.NewNotRegisteredErr(schema.GroupVersionKind{}, nil)},
expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
expectedObject: &testDecodable{
Other: "test",
Value: 1,
},
},
// registered types get defaulted by the into object kind
{
data: []byte(`{"value":1,"Other":"test"}`),
into: &testDecodable{},
typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
expectedObject: &testDecodable{
Other: "test",
Value: 1,
},
},
// registered types get defaulted by the into object kind even without version, but return an error
{
data: []byte(`{"value":1,"Other":"test"}`),
into: &testDecodable{},
typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: ""}},
expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: ""},
errFn: func(err error) bool { return strings.Contains(err.Error(), "Object 'apiVersion' is missing in") },
expectedObject: &testDecodable{
Other: "test",
Value: 1,
},
},
// runtime.VersionedObjects are decoded
{
data: []byte(`{"value":1,"Other":"test"}`),
into: &runtime.VersionedObjects{Objects: []runtime.Object{}},
creater: &mockCreater{obj: &testDecodable{}},
typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
defaultGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
expectedObject: &runtime.VersionedObjects{
Objects: []runtime.Object{
&testDecodable{
Other: "test",
Value: 1,
},
},
},
},
// runtime.VersionedObjects with an object are decoded into
{
data: []byte(`{"Other":"test"}`),
into: &runtime.VersionedObjects{Objects: []runtime.Object{&testDecodable{Value: 2}}},
typer: &mockTyper{gvk: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}},
expectedGVK: &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"},
expectedObject: &runtime.VersionedObjects{
Objects: []runtime.Object{
&testDecodable{
Other: "test",
Value: 2,
},
},
},
},
}
for i, test := range testCases {
var s runtime.Serializer
if test.yaml {
s = json.NewYAMLSerializer(json.DefaultMetaFactory, test.creater, test.typer)
} else {
s = json.NewSerializer(json.DefaultMetaFactory, test.creater, test.typer, test.pretty)
}
obj, gvk, err := s.Decode([]byte(test.data), test.defaultGVK, test.into)
if !reflect.DeepEqual(test.expectedGVK, gvk) {
t.Errorf("%d: unexpected GVK: %v", i, gvk)
}
switch {
case err == nil && test.errFn != nil:
t.Errorf("%d: failed: %v", i, err)
continue
case err != nil && test.errFn == nil:
t.Errorf("%d: failed: %v", i, err)
continue
case err != nil:
if !test.errFn(err) {
t.Errorf("%d: failed: %v", i, err)
}
if obj != nil {
t.Errorf("%d: should have returned nil object", i)
}
continue
}
if test.into != nil && test.into != obj {
t.Errorf("%d: expected into to be returned: %v", i, obj)
continue
}
if !reflect.DeepEqual(test.expectedObject, obj) {
t.Errorf("%d: unexpected object:\n%s", i, diff.ObjectGoPrintSideBySide(test.expectedObject, obj))
}
}
}
type mockCreater struct {
apiVersion string
kind string
err error
obj runtime.Object
}
func (c *mockCreater) New(kind schema.GroupVersionKind) (runtime.Object, error) {
c.apiVersion, c.kind = kind.GroupVersion().String(), kind.Kind
return c.obj, c.err
}
type mockTyper struct {
gvk *schema.GroupVersionKind
err error
}
func (t *mockTyper) ObjectKinds(obj runtime.Object) ([]schema.GroupVersionKind, bool, error) {
if t.gvk == nil {
return nil, false, t.err
}
return []schema.GroupVersionKind{*t.gvk}, false, t.err
}
func (t *mockTyper) Recognizes(_ schema.GroupVersionKind) bool {
return false
}

View file

@ -1,45 +0,0 @@
/*
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 json
import "testing"
func TestSimpleMetaFactoryInterpret(t *testing.T) {
factory := SimpleMetaFactory{}
gvk, err := factory.Interpret([]byte(`{"apiVersion":"1","kind":"object"}`))
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if gvk.Version != "1" || gvk.Kind != "object" {
t.Errorf("unexpected interpret: %#v", gvk)
}
// no kind or version
gvk, err = factory.Interpret([]byte(`{}`))
if err != nil {
t.Fatalf("unexpected error: %v", err)
}
if gvk.Version != "" || gvk.Kind != "" {
t.Errorf("unexpected interpret: %#v", gvk)
}
// unparsable
gvk, err = factory.Interpret([]byte(`{`))
if err == nil {
t.Errorf("unexpected non-error")
}
}

View file

@ -1,58 +0,0 @@
/*
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 testing
import (
"testing"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer/json"
"k8s.io/apimachinery/pkg/runtime/serializer/recognizer"
)
type A struct{}
func (A) GetObjectKind() schema.ObjectKind { return schema.EmptyObjectKind }
func TestRecognizer(t *testing.T) {
s := runtime.NewScheme()
s.AddKnownTypes(schema.GroupVersion{Version: "v1"}, &A{})
d := recognizer.NewDecoder(
json.NewSerializer(json.DefaultMetaFactory, s, s, false),
json.NewYAMLSerializer(json.DefaultMetaFactory, s, s),
)
out, _, err := d.Decode([]byte(`
kind: A
apiVersion: v1
`), nil, nil)
if err != nil {
t.Fatal(err)
}
t.Logf("%#v", out)
out, _, err = d.Decode([]byte(`
{
"kind":"A",
"apiVersion":"v1"
}
`), nil, nil)
if err != nil {
t.Fatal(err)
}
t.Logf("%#v", out)
}

View file

@ -1,84 +0,0 @@
/*
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 streaming
import (
"bytes"
"io"
"io/ioutil"
"testing"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/framer"
)
type fakeDecoder struct {
got []byte
obj runtime.Object
err error
}
func (d *fakeDecoder) Decode(data []byte, gvk *schema.GroupVersionKind, into runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) {
d.got = data
return d.obj, nil, d.err
}
func TestEmptyDecoder(t *testing.T) {
buf := bytes.NewBuffer([]byte{})
d := &fakeDecoder{}
_, _, err := NewDecoder(ioutil.NopCloser(buf), d).Decode(nil, nil)
if err != io.EOF {
t.Fatal(err)
}
}
func TestDecoder(t *testing.T) {
frames := [][]byte{
make([]byte, 1025),
make([]byte, 1024*5),
make([]byte, 1024*1024*5),
make([]byte, 1025),
}
pr, pw := io.Pipe()
fw := framer.NewLengthDelimitedFrameWriter(pw)
go func() {
for i := range frames {
fw.Write(frames[i])
}
pw.Close()
}()
r := framer.NewLengthDelimitedFrameReader(pr)
d := &fakeDecoder{}
dec := NewDecoder(r, d)
if _, _, err := dec.Decode(nil, nil); err != nil || !bytes.Equal(d.got, frames[0]) {
t.Fatalf("unexpected %v %v", err, len(d.got))
}
if _, _, err := dec.Decode(nil, nil); err != nil || !bytes.Equal(d.got, frames[1]) {
t.Fatalf("unexpected %v %v", err, len(d.got))
}
if _, _, err := dec.Decode(nil, nil); err != ErrObjectTooLarge || !bytes.Equal(d.got, frames[1]) {
t.Fatalf("unexpected %v %v", err, len(d.got))
}
if _, _, err := dec.Decode(nil, nil); err != nil || !bytes.Equal(d.got, frames[3]) {
t.Fatalf("unexpected %v %v", err, len(d.got))
}
if _, _, err := dec.Decode(nil, nil); err != io.EOF {
t.Fatalf("unexpected %v %v", err, len(d.got))
}
}

View file

@ -1,399 +0,0 @@
/*
Copyright 2015 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package versioning
import (
"fmt"
"io"
"io/ioutil"
"reflect"
"testing"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/diff"
)
type testDecodable struct {
Other string
Value int `json:"value"`
gvk schema.GroupVersionKind
}
func (d *testDecodable) GetObjectKind() schema.ObjectKind { return d }
func (d *testDecodable) SetGroupVersionKind(gvk schema.GroupVersionKind) { d.gvk = gvk }
func (d *testDecodable) GroupVersionKind() schema.GroupVersionKind { return d.gvk }
type testNestedDecodable struct {
Other string
Value int `json:"value"`
gvk schema.GroupVersionKind
nestedCalled bool
nestedErr error
}
func (d *testNestedDecodable) GetObjectKind() schema.ObjectKind { return d }
func (d *testNestedDecodable) SetGroupVersionKind(gvk schema.GroupVersionKind) { d.gvk = gvk }
func (d *testNestedDecodable) GroupVersionKind() schema.GroupVersionKind { return d.gvk }
func (d *testNestedDecodable) EncodeNestedObjects(e runtime.Encoder) error {
d.nestedCalled = true
return d.nestedErr
}
func (d *testNestedDecodable) DecodeNestedObjects(_ runtime.Decoder) error {
d.nestedCalled = true
return d.nestedErr
}
func TestNestedDecode(t *testing.T) {
n := &testNestedDecodable{nestedErr: fmt.Errorf("unable to decode")}
decoder := &mockSerializer{obj: n}
codec := NewCodec(nil, decoder, nil, nil, nil, nil, nil, nil, nil)
if _, _, err := codec.Decode([]byte(`{}`), nil, n); err != n.nestedErr {
t.Errorf("unexpected error: %v", err)
}
if !n.nestedCalled {
t.Errorf("did not invoke nested decoder")
}
}
func TestNestedEncode(t *testing.T) {
n := &testNestedDecodable{nestedErr: fmt.Errorf("unable to decode")}
n2 := &testNestedDecodable{nestedErr: fmt.Errorf("unable to decode 2")}
encoder := &mockSerializer{obj: n}
codec := NewCodec(
encoder, nil,
&checkConvertor{obj: n2, groupVersion: schema.GroupVersion{Group: "other"}},
nil, nil,
&mockTyper{gvks: []schema.GroupVersionKind{{Kind: "test"}}},
nil,
schema.GroupVersion{Group: "other"}, nil,
)
if err := codec.Encode(n, ioutil.Discard); err != n2.nestedErr {
t.Errorf("unexpected error: %v", err)
}
if n.nestedCalled || !n2.nestedCalled {
t.Errorf("did not invoke correct nested decoder")
}
}
func TestDecode(t *testing.T) {
gvk1 := &schema.GroupVersionKind{Kind: "Test", Group: "other", Version: "blah"}
decodable1 := &testDecodable{}
decodable2 := &testDecodable{}
decodable3 := &testDecodable{}
versionedDecodable1 := &runtime.VersionedObjects{Objects: []runtime.Object{decodable1}}
testCases := []struct {
serializer runtime.Serializer
convertor runtime.ObjectConvertor
creater runtime.ObjectCreater
copier runtime.ObjectCopier
typer runtime.ObjectTyper
defaulter runtime.ObjectDefaulter
yaml bool
pretty bool
encodes, decodes runtime.GroupVersioner
defaultGVK *schema.GroupVersionKind
into runtime.Object
errFn func(error) bool
expectedObject runtime.Object
sameObject runtime.Object
expectedGVK *schema.GroupVersionKind
}{
{
serializer: &mockSerializer{actual: gvk1},
convertor: &checkConvertor{groupVersion: schema.GroupVersion{Group: "other", Version: "__internal"}},
expectedGVK: gvk1,
decodes: schema.GroupVersion{Group: "other", Version: "__internal"},
},
{
serializer: &mockSerializer{actual: gvk1, obj: decodable1},
convertor: &checkConvertor{in: decodable1, obj: decodable2, groupVersion: schema.GroupVersion{Group: "other", Version: "__internal"}},
expectedGVK: gvk1,
sameObject: decodable2,
decodes: schema.GroupVersion{Group: "other", Version: "__internal"},
},
// defaultGVK.Group is allowed to force a conversion to the destination group
{
serializer: &mockSerializer{actual: gvk1, obj: decodable1},
defaultGVK: &schema.GroupVersionKind{Group: "force"},
convertor: &checkConvertor{in: decodable1, obj: decodable2, groupVersion: schema.GroupVersion{Group: "force", Version: "__internal"}},
expectedGVK: gvk1,
sameObject: decodable2,
decodes: schema.GroupVersion{Group: "force", Version: "__internal"},
},
// uses direct conversion for into when objects differ
{
into: decodable3,
serializer: &mockSerializer{actual: gvk1, obj: decodable1},
convertor: &checkConvertor{in: decodable1, obj: decodable3, directConvert: true},
expectedGVK: gvk1,
sameObject: decodable3,
},
{
into: versionedDecodable1,
serializer: &mockSerializer{actual: gvk1, obj: decodable3},
convertor: &checkConvertor{in: decodable3, obj: decodable1, directConvert: true},
expectedGVK: gvk1,
sameObject: versionedDecodable1,
},
// returns directly when serializer returns into
{
into: decodable3,
serializer: &mockSerializer{actual: gvk1, obj: decodable3},
expectedGVK: gvk1,
sameObject: decodable3,
},
// returns directly when serializer returns into
{
into: versionedDecodable1,
serializer: &mockSerializer{actual: gvk1, obj: decodable1},
expectedGVK: gvk1,
sameObject: versionedDecodable1,
},
// runtime.VersionedObjects are decoded
{
into: &runtime.VersionedObjects{Objects: []runtime.Object{}},
serializer: &mockSerializer{actual: gvk1, obj: decodable1},
copier: &checkCopy{in: decodable1, obj: decodable1},
convertor: &checkConvertor{in: decodable1, obj: decodable2, groupVersion: schema.GroupVersion{Group: "other", Version: "__internal"}},
expectedGVK: gvk1,
expectedObject: &runtime.VersionedObjects{Objects: []runtime.Object{decodable1, decodable2}},
decodes: schema.GroupVersion{Group: "other", Version: "__internal"},
},
{
into: &runtime.VersionedObjects{Objects: []runtime.Object{}},
serializer: &mockSerializer{actual: gvk1, obj: decodable1},
copier: &checkCopy{in: decodable1, obj: nil, err: fmt.Errorf("error on copy")},
convertor: &checkConvertor{in: decodable1, obj: decodable2, groupVersion: schema.GroupVersion{Group: "other", Version: "__internal"}},
expectedGVK: gvk1,
expectedObject: &runtime.VersionedObjects{Objects: []runtime.Object{decodable1, decodable2}},
decodes: schema.GroupVersion{Group: "other", Version: "__internal"},
},
// decode into the same version as the serialized object
{
decodes: schema.GroupVersions{gvk1.GroupVersion()},
serializer: &mockSerializer{actual: gvk1, obj: decodable1},
convertor: &checkConvertor{in: decodable1, obj: decodable1, groupVersion: schema.GroupVersions{{Group: "other", Version: "blah"}}},
expectedGVK: gvk1,
expectedObject: decodable1,
},
{
into: &runtime.VersionedObjects{Objects: []runtime.Object{}},
decodes: schema.GroupVersions{gvk1.GroupVersion()},
serializer: &mockSerializer{actual: gvk1, obj: decodable1},
convertor: &checkConvertor{in: decodable1, obj: decodable1, groupVersion: schema.GroupVersions{{Group: "other", Version: "blah"}}},
copier: &checkCopy{in: decodable1, obj: decodable1, err: nil},
expectedGVK: gvk1,
expectedObject: &runtime.VersionedObjects{Objects: []runtime.Object{decodable1}},
},
// codec with non matching version skips conversion altogether
{
decodes: schema.GroupVersions{{Group: "something", Version: "else"}},
serializer: &mockSerializer{actual: gvk1, obj: decodable1},
convertor: &checkConvertor{in: decodable1, obj: decodable1, groupVersion: schema.GroupVersions{{Group: "something", Version: "else"}}},
expectedGVK: gvk1,
expectedObject: decodable1,
},
{
into: &runtime.VersionedObjects{Objects: []runtime.Object{}},
decodes: schema.GroupVersions{{Group: "something", Version: "else"}},
serializer: &mockSerializer{actual: gvk1, obj: decodable1},
convertor: &checkConvertor{in: decodable1, obj: decodable1, groupVersion: schema.GroupVersions{{Group: "something", Version: "else"}}},
copier: &checkCopy{in: decodable1, obj: decodable1, err: nil},
expectedGVK: gvk1,
expectedObject: &runtime.VersionedObjects{Objects: []runtime.Object{decodable1}},
},
}
for i, test := range testCases {
t.Logf("%d", i)
s := NewCodec(test.serializer, test.serializer, test.convertor, test.creater, test.copier, test.typer, test.defaulter, test.encodes, test.decodes)
obj, gvk, err := s.Decode([]byte(`{}`), test.defaultGVK, test.into)
if !reflect.DeepEqual(test.expectedGVK, gvk) {
t.Errorf("%d: unexpected GVK: %v", i, gvk)
}
switch {
case err == nil && test.errFn != nil:
t.Errorf("%d: failed: %v", i, err)
continue
case err != nil && test.errFn == nil:
t.Errorf("%d: failed: %v", i, err)
continue
case err != nil:
if !test.errFn(err) {
t.Errorf("%d: failed: %v", i, err)
}
if obj != nil {
t.Errorf("%d: should have returned nil object", i)
}
continue
}
if test.into != nil && test.into != obj {
t.Errorf("%d: expected into to be returned: %v", i, obj)
continue
}
switch {
case test.expectedObject != nil:
if !reflect.DeepEqual(test.expectedObject, obj) {
t.Errorf("%d: unexpected object:\n%s", i, diff.ObjectGoPrintSideBySide(test.expectedObject, obj))
}
case test.sameObject != nil:
if test.sameObject != obj {
t.Errorf("%d: unexpected object:\n%s", i, diff.ObjectGoPrintSideBySide(test.sameObject, obj))
}
case obj != nil:
t.Errorf("%d: unexpected object: %#v", i, obj)
}
}
}
type checkCopy struct {
in, obj runtime.Object
err error
}
func (c *checkCopy) Copy(obj runtime.Object) (runtime.Object, error) {
if c.in != nil && c.in != obj {
return nil, fmt.Errorf("unexpected input to copy: %#v", obj)
}
return c.obj, c.err
}
type checkConvertor struct {
err error
in, obj runtime.Object
groupVersion runtime.GroupVersioner
directConvert bool
}
func (c *checkConvertor) Convert(in, out, context interface{}) error {
if !c.directConvert {
return fmt.Errorf("unexpected call to Convert")
}
if c.in != nil && c.in != in {
return fmt.Errorf("unexpected in: %s", in)
}
if c.obj != nil && c.obj != out {
return fmt.Errorf("unexpected out: %s", out)
}
return c.err
}
func (c *checkConvertor) ConvertToVersion(in runtime.Object, outVersion runtime.GroupVersioner) (out runtime.Object, err error) {
if c.directConvert {
return nil, fmt.Errorf("unexpected call to ConvertToVersion")
}
if c.in != nil && c.in != in {
return nil, fmt.Errorf("unexpected in: %s", in)
}
if !reflect.DeepEqual(c.groupVersion, outVersion) {
return nil, fmt.Errorf("unexpected outversion: %s (%s)", outVersion, c.groupVersion)
}
return c.obj, c.err
}
func (c *checkConvertor) ConvertFieldLabel(version, kind, label, value string) (string, string, error) {
return "", "", fmt.Errorf("unexpected call to ConvertFieldLabel")
}
type mockSerializer struct {
err error
obj runtime.Object
encodingObjGVK schema.GroupVersionKind
defaults, actual *schema.GroupVersionKind
into runtime.Object
}
func (s *mockSerializer) Decode(data []byte, defaults *schema.GroupVersionKind, into runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) {
s.defaults = defaults
s.into = into
return s.obj, s.actual, s.err
}
func (s *mockSerializer) Encode(obj runtime.Object, w io.Writer) error {
s.obj = obj
s.encodingObjGVK = obj.GetObjectKind().GroupVersionKind()
return s.err
}
type mockCreater struct {
err error
obj runtime.Object
}
func (c *mockCreater) New(kind schema.GroupVersionKind) (runtime.Object, error) {
return c.obj, c.err
}
type mockTyper struct {
gvks []schema.GroupVersionKind
unversioned bool
err error
}
func (t *mockTyper) ObjectKinds(obj runtime.Object) ([]schema.GroupVersionKind, bool, error) {
return t.gvks, t.unversioned, t.err
}
func (t *mockTyper) Recognizes(_ schema.GroupVersionKind) bool {
return true
}
func TestDirectCodecEncode(t *testing.T) {
serializer := mockSerializer{}
typer := mockTyper{
gvks: []schema.GroupVersionKind{
{
Group: "wrong_group",
Kind: "some_kind",
},
{
Group: "expected_group",
Kind: "some_kind",
},
},
}
c := DirectEncoder{
Version: schema.GroupVersion{Group: "expected_group"},
Encoder: &serializer,
ObjectTyper: &typer,
}
c.Encode(&testDecodable{}, ioutil.Discard)
if e, a := "expected_group", serializer.encodingObjGVK.Group; e != a {
t.Errorf("expected group to be %v, got %v", e, a)
}
}

View file

@ -1,46 +0,0 @@
/*
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 yaml
import (
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/util/yaml"
)
// yamlSerializer converts YAML passed to the Decoder methods to JSON.
type yamlSerializer struct {
// the nested serializer
runtime.Serializer
}
// yamlSerializer implements Serializer
var _ runtime.Serializer = yamlSerializer{}
// NewDecodingSerializer adds YAML decoding support to a serializer that supports JSON.
func NewDecodingSerializer(jsonSerializer runtime.Serializer) runtime.Serializer {
return &yamlSerializer{jsonSerializer}
}
func (c yamlSerializer) Decode(data []byte, gvk *schema.GroupVersionKind, into runtime.Object) (runtime.Object, *schema.GroupVersionKind, error) {
out, err := yaml.ToJSON(data)
if err != nil {
return nil, nil, err
}
data = out
return c.Serializer.Decode(data, gvk, into)
}

View file

@ -1,43 +0,0 @@
/*
Copyright 2015 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package runtime
import (
"testing"
)
func TestFmtRawDoc(t *testing.T) {
tests := []struct {
t, expected string
}{
{"aaa\n --- asd\n TODO: tooooodo\n toooodoooooo\n", "aaa"},
{"aaa\nasd\n TODO: tooooodo\nbbbb\n --- toooodoooooo\n", "aaa asd bbbb"},
{" TODO: tooooodo\n", ""},
{"Par1\n\nPar2\n\n", "Par1\\n\\nPar2"},
{"", ""},
{" ", ""},
{" \n", ""},
{" \n\n ", ""},
{"Example:\n\tl1\n\t\tl2\n", "Example:\\n\\tl1\\n\\t\\tl2"},
}
for _, test := range tests {
if o := fmtRawDoc(test.t); o != test.expected {
t.Fatalf("Expected: %q, got %q", test.expected, o)
}
}
}

View file

@ -1,280 +0,0 @@
/*
Copyright 2014 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package diff
import (
"bytes"
"encoding/json"
"fmt"
"reflect"
"sort"
"strings"
"text/tabwriter"
"github.com/davecgh/go-spew/spew"
"k8s.io/apimachinery/pkg/util/validation/field"
)
// StringDiff diffs a and b and returns a human readable diff.
func StringDiff(a, b string) string {
ba := []byte(a)
bb := []byte(b)
out := []byte{}
i := 0
for ; i < len(ba) && i < len(bb); i++ {
if ba[i] != bb[i] {
break
}
out = append(out, ba[i])
}
out = append(out, []byte("\n\nA: ")...)
out = append(out, ba[i:]...)
out = append(out, []byte("\n\nB: ")...)
out = append(out, bb[i:]...)
out = append(out, []byte("\n\n")...)
return string(out)
}
// ObjectDiff writes the two objects out as JSON and prints out the identical part of
// the objects followed by the remaining part of 'a' and finally the remaining part of 'b'.
// For debugging tests.
func ObjectDiff(a, b interface{}) string {
ab, err := json.Marshal(a)
if err != nil {
panic(fmt.Sprintf("a: %v", err))
}
bb, err := json.Marshal(b)
if err != nil {
panic(fmt.Sprintf("b: %v", err))
}
return StringDiff(string(ab), string(bb))
}
// ObjectGoPrintDiff is like ObjectDiff, but uses go-spew to print the objects,
// which shows absolutely everything by recursing into every single pointer
// (go's %#v formatters OTOH stop at a certain point). This is needed when you
// can't figure out why reflect.DeepEqual is returning false and nothing is
// showing you differences. This will.
func ObjectGoPrintDiff(a, b interface{}) string {
s := spew.ConfigState{DisableMethods: true}
return StringDiff(
s.Sprintf("%#v", a),
s.Sprintf("%#v", b),
)
}
func ObjectReflectDiff(a, b interface{}) string {
vA, vB := reflect.ValueOf(a), reflect.ValueOf(b)
if vA.Type() != vB.Type() {
return fmt.Sprintf("type A %T and type B %T do not match", a, b)
}
diffs := objectReflectDiff(field.NewPath("object"), vA, vB)
if len(diffs) == 0 {
return "<no diffs>"
}
out := []string{""}
for _, d := range diffs {
out = append(out,
fmt.Sprintf("%s:", d.path),
limit(fmt.Sprintf(" a: %#v", d.a), 80),
limit(fmt.Sprintf(" b: %#v", d.b), 80),
)
}
return strings.Join(out, "\n")
}
func limit(s string, max int) string {
if len(s) > max {
return s[:max]
}
return s
}
func public(s string) bool {
if len(s) == 0 {
return false
}
return s[:1] == strings.ToUpper(s[:1])
}
type diff struct {
path *field.Path
a, b interface{}
}
type orderedDiffs []diff
func (d orderedDiffs) Len() int { return len(d) }
func (d orderedDiffs) Swap(i, j int) { d[i], d[j] = d[j], d[i] }
func (d orderedDiffs) Less(i, j int) bool {
a, b := d[i].path.String(), d[j].path.String()
if a < b {
return true
}
return false
}
func objectReflectDiff(path *field.Path, a, b reflect.Value) []diff {
switch a.Type().Kind() {
case reflect.Struct:
var changes []diff
for i := 0; i < a.Type().NumField(); i++ {
if !public(a.Type().Field(i).Name) {
if reflect.DeepEqual(a.Interface(), b.Interface()) {
continue
}
return []diff{{path: path, a: fmt.Sprintf("%#v", a), b: fmt.Sprintf("%#v", b)}}
}
if sub := objectReflectDiff(path.Child(a.Type().Field(i).Name), a.Field(i), b.Field(i)); len(sub) > 0 {
changes = append(changes, sub...)
} else {
if !reflect.DeepEqual(a.Field(i).Interface(), b.Field(i).Interface()) {
changes = append(changes, diff{path: path, a: a.Field(i).Interface(), b: b.Field(i).Interface()})
}
}
}
return changes
case reflect.Ptr, reflect.Interface:
if a.IsNil() || b.IsNil() {
switch {
case a.IsNil() && b.IsNil():
return nil
case a.IsNil():
return []diff{{path: path, a: nil, b: b.Interface()}}
default:
return []diff{{path: path, a: a.Interface(), b: nil}}
}
}
return objectReflectDiff(path, a.Elem(), b.Elem())
case reflect.Chan:
if !reflect.DeepEqual(a.Interface(), b.Interface()) {
return []diff{{path: path, a: a.Interface(), b: b.Interface()}}
}
return nil
case reflect.Slice:
lA, lB := a.Len(), b.Len()
l := lA
if lB < lA {
l = lB
}
if lA == lB && lA == 0 {
if a.IsNil() != b.IsNil() {
return []diff{{path: path, a: a.Interface(), b: b.Interface()}}
}
return nil
}
for i := 0; i < l; i++ {
if !reflect.DeepEqual(a.Index(i), b.Index(i)) {
return objectReflectDiff(path.Index(i), a.Index(i), b.Index(i))
}
}
var diffs []diff
for i := l; i < lA; i++ {
diffs = append(diffs, diff{path: path.Index(i), a: a.Index(i), b: nil})
}
for i := l; i < lB; i++ {
diffs = append(diffs, diff{path: path.Index(i), a: nil, b: b.Index(i)})
}
if len(diffs) == 0 {
diffs = append(diffs, diff{path: path, a: a, b: b})
}
return diffs
case reflect.Map:
if reflect.DeepEqual(a.Interface(), b.Interface()) {
return nil
}
aKeys := make(map[interface{}]interface{})
for _, key := range a.MapKeys() {
aKeys[key.Interface()] = a.MapIndex(key).Interface()
}
var missing []diff
for _, key := range b.MapKeys() {
if _, ok := aKeys[key.Interface()]; ok {
delete(aKeys, key.Interface())
if reflect.DeepEqual(a.MapIndex(key).Interface(), b.MapIndex(key).Interface()) {
continue
}
missing = append(missing, objectReflectDiff(path.Key(fmt.Sprintf("%s", key.Interface())), a.MapIndex(key), b.MapIndex(key))...)
continue
}
missing = append(missing, diff{path: path.Key(fmt.Sprintf("%s", key.Interface())), a: nil, b: b.MapIndex(key).Interface()})
}
for key, value := range aKeys {
missing = append(missing, diff{path: path.Key(fmt.Sprintf("%s", key)), a: value, b: nil})
}
if len(missing) == 0 {
missing = append(missing, diff{path: path, a: a.Interface(), b: b.Interface()})
}
sort.Sort(orderedDiffs(missing))
return missing
default:
if reflect.DeepEqual(a.Interface(), b.Interface()) {
return nil
}
if !a.CanInterface() {
return []diff{{path: path, a: fmt.Sprintf("%#v", a), b: fmt.Sprintf("%#v", b)}}
}
return []diff{{path: path, a: a.Interface(), b: b.Interface()}}
}
}
// ObjectGoPrintSideBySide prints a and b as textual dumps side by side,
// enabling easy visual scanning for mismatches.
func ObjectGoPrintSideBySide(a, b interface{}) string {
s := spew.ConfigState{
Indent: " ",
// Extra deep spew.
DisableMethods: true,
}
sA := s.Sdump(a)
sB := s.Sdump(b)
linesA := strings.Split(sA, "\n")
linesB := strings.Split(sB, "\n")
width := 0
for _, s := range linesA {
l := len(s)
if l > width {
width = l
}
}
for _, s := range linesB {
l := len(s)
if l > width {
width = l
}
}
buf := &bytes.Buffer{}
w := tabwriter.NewWriter(buf, width, 0, 1, ' ', 0)
max := len(linesA)
if len(linesB) > max {
max = len(linesB)
}
for i := 0; i < max; i++ {
var a, b string
if i < len(linesA) {
a = linesA[i]
}
if i < len(linesB) {
b = linesB[i]
}
fmt.Fprintf(w, "%s\t%s\n", a, b)
}
w.Flush()
return buf.String()
}

View file

@ -1,88 +0,0 @@
/*
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 diff
import (
"testing"
)
func TestObjectReflectDiff(t *testing.T) {
type struct1 struct{ A []int }
testCases := map[string]struct {
a, b interface{}
out string
}{
"map": {
a: map[string]int{},
b: map[string]int{},
},
"detect nil map": {
a: map[string]int(nil),
b: map[string]int{},
out: `
object:
a: map[string]int(nil)
b: map[string]int{}`,
},
"detect map changes": {
a: map[string]int{"test": 1, "other": 2},
b: map[string]int{"test": 2, "third": 3},
out: `
object[other]:
a: 2
b: <nil>
object[test]:
a: 1
b: 2
object[third]:
a: <nil>
b: 3`,
},
"nil slice": {a: struct1{A: nil}, b: struct1{A: nil}},
"empty slice": {a: struct1{A: []int{}}, b: struct1{A: []int{}}},
"detect slice changes 1": {a: struct1{A: []int{1}}, b: struct1{A: []int{2}}, out: `
object.A[0]:
a: 1
b: 2`,
},
"detect slice changes 2": {a: struct1{A: []int{}}, b: struct1{A: []int{2}}, out: `
object.A[0]:
a: <nil>
b: 2`,
},
"detect slice changes 3": {a: struct1{A: []int{1}}, b: struct1{A: []int{}}, out: `
object.A[0]:
a: 1
b: <nil>`,
},
"detect nil vs empty slices": {a: struct1{A: nil}, b: struct1{A: []int{}}, out: `
object.A:
a: []int(nil)
b: []int{}`,
},
}
for name, test := range testCases {
expect := test.out
if len(expect) == 0 {
expect = "<no diffs>"
}
if actual := ObjectReflectDiff(test.a, test.b); actual != expect {
t.Errorf("%s: unexpected output: %s", name, actual)
}
}
}

View file

@ -1,326 +0,0 @@
/*
Copyright 2015 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package errors
import (
"fmt"
"reflect"
"testing"
)
func TestEmptyAggregate(t *testing.T) {
var slice []error
var agg Aggregate
var err error
agg = NewAggregate(slice)
if agg != nil {
t.Errorf("expected nil, got %#v", agg)
}
err = NewAggregate(slice)
if err != nil {
t.Errorf("expected nil, got %#v", err)
}
// This is not normally possible, but pedantry demands I test it.
agg = aggregate(slice) // empty aggregate
if s := agg.Error(); s != "" {
t.Errorf("expected empty string, got %q", s)
}
if s := agg.Errors(); len(s) != 0 {
t.Errorf("expected empty slice, got %#v", s)
}
err = agg.(error)
if s := err.Error(); s != "" {
t.Errorf("expected empty string, got %q", s)
}
}
func TestAggregateWithNil(t *testing.T) {
var slice []error
slice = []error{nil}
var agg Aggregate
var err error
agg = NewAggregate(slice)
if agg != nil {
t.Errorf("expected nil, got %#v", agg)
}
err = NewAggregate(slice)
if err != nil {
t.Errorf("expected nil, got %#v", err)
}
// Append a non-nil error
slice = append(slice, fmt.Errorf("err"))
agg = NewAggregate(slice)
if agg == nil {
t.Errorf("expected non-nil")
}
if s := agg.Error(); s != "err" {
t.Errorf("expected 'err', got %q", s)
}
if s := agg.Errors(); len(s) != 1 {
t.Errorf("expected one-element slice, got %#v", s)
}
if s := agg.Errors()[0].Error(); s != "err" {
t.Errorf("expected 'err', got %q", s)
}
err = agg.(error)
if err == nil {
t.Errorf("expected non-nil")
}
if s := err.Error(); s != "err" {
t.Errorf("expected 'err', got %q", s)
}
}
func TestSingularAggregate(t *testing.T) {
var slice []error = []error{fmt.Errorf("err")}
var agg Aggregate
var err error
agg = NewAggregate(slice)
if agg == nil {
t.Errorf("expected non-nil")
}
if s := agg.Error(); s != "err" {
t.Errorf("expected 'err', got %q", s)
}
if s := agg.Errors(); len(s) != 1 {
t.Errorf("expected one-element slice, got %#v", s)
}
if s := agg.Errors()[0].Error(); s != "err" {
t.Errorf("expected 'err', got %q", s)
}
err = agg.(error)
if err == nil {
t.Errorf("expected non-nil")
}
if s := err.Error(); s != "err" {
t.Errorf("expected 'err', got %q", s)
}
}
func TestPluralAggregate(t *testing.T) {
var slice []error = []error{fmt.Errorf("abc"), fmt.Errorf("123")}
var agg Aggregate
var err error
agg = NewAggregate(slice)
if agg == nil {
t.Errorf("expected non-nil")
}
if s := agg.Error(); s != "[abc, 123]" {
t.Errorf("expected '[abc, 123]', got %q", s)
}
if s := agg.Errors(); len(s) != 2 {
t.Errorf("expected two-elements slice, got %#v", s)
}
if s := agg.Errors()[0].Error(); s != "abc" {
t.Errorf("expected '[abc, 123]', got %q", s)
}
err = agg.(error)
if err == nil {
t.Errorf("expected non-nil")
}
if s := err.Error(); s != "[abc, 123]" {
t.Errorf("expected '[abc, 123]', got %q", s)
}
}
func TestFilterOut(t *testing.T) {
testCases := []struct {
err error
filter []Matcher
expected error
}{
{
nil,
[]Matcher{},
nil,
},
{
aggregate{},
[]Matcher{},
nil,
},
{
aggregate{fmt.Errorf("abc")},
[]Matcher{},
aggregate{fmt.Errorf("abc")},
},
{
aggregate{fmt.Errorf("abc")},
[]Matcher{func(err error) bool { return false }},
aggregate{fmt.Errorf("abc")},
},
{
aggregate{fmt.Errorf("abc")},
[]Matcher{func(err error) bool { return true }},
nil,
},
{
aggregate{fmt.Errorf("abc")},
[]Matcher{func(err error) bool { return false }, func(err error) bool { return false }},
aggregate{fmt.Errorf("abc")},
},
{
aggregate{fmt.Errorf("abc")},
[]Matcher{func(err error) bool { return false }, func(err error) bool { return true }},
nil,
},
{
aggregate{fmt.Errorf("abc"), fmt.Errorf("def"), fmt.Errorf("ghi")},
[]Matcher{func(err error) bool { return err.Error() == "def" }},
aggregate{fmt.Errorf("abc"), fmt.Errorf("ghi")},
},
{
aggregate{aggregate{fmt.Errorf("abc")}},
[]Matcher{},
aggregate{aggregate{fmt.Errorf("abc")}},
},
{
aggregate{aggregate{fmt.Errorf("abc"), aggregate{fmt.Errorf("def")}}},
[]Matcher{},
aggregate{aggregate{fmt.Errorf("abc"), aggregate{fmt.Errorf("def")}}},
},
{
aggregate{aggregate{fmt.Errorf("abc"), aggregate{fmt.Errorf("def")}}},
[]Matcher{func(err error) bool { return err.Error() == "def" }},
aggregate{aggregate{fmt.Errorf("abc")}},
},
}
for i, testCase := range testCases {
err := FilterOut(testCase.err, testCase.filter...)
if !reflect.DeepEqual(testCase.expected, err) {
t.Errorf("%d: expected %v, got %v", i, testCase.expected, err)
}
}
}
func TestFlatten(t *testing.T) {
testCases := []struct {
agg Aggregate
expected Aggregate
}{
{
nil,
nil,
},
{
aggregate{},
nil,
},
{
aggregate{fmt.Errorf("abc")},
aggregate{fmt.Errorf("abc")},
},
{
aggregate{fmt.Errorf("abc"), fmt.Errorf("def"), fmt.Errorf("ghi")},
aggregate{fmt.Errorf("abc"), fmt.Errorf("def"), fmt.Errorf("ghi")},
},
{
aggregate{aggregate{fmt.Errorf("abc")}},
aggregate{fmt.Errorf("abc")},
},
{
aggregate{aggregate{aggregate{fmt.Errorf("abc")}}},
aggregate{fmt.Errorf("abc")},
},
{
aggregate{aggregate{fmt.Errorf("abc"), aggregate{fmt.Errorf("def")}}},
aggregate{fmt.Errorf("abc"), fmt.Errorf("def")},
},
{
aggregate{aggregate{aggregate{fmt.Errorf("abc")}, fmt.Errorf("def"), aggregate{fmt.Errorf("ghi")}}},
aggregate{fmt.Errorf("abc"), fmt.Errorf("def"), fmt.Errorf("ghi")},
},
}
for i, testCase := range testCases {
agg := Flatten(testCase.agg)
if !reflect.DeepEqual(testCase.expected, agg) {
t.Errorf("%d: expected %v, got %v", i, testCase.expected, agg)
}
}
}
func TestAggregateGoroutines(t *testing.T) {
testCases := []struct {
errs []error
expected map[string]bool // can't compare directly to Aggregate due to non-deterministic ordering
}{
{
[]error{},
nil,
},
{
[]error{nil},
nil,
},
{
[]error{nil, nil},
nil,
},
{
[]error{fmt.Errorf("1")},
map[string]bool{"1": true},
},
{
[]error{fmt.Errorf("1"), nil},
map[string]bool{"1": true},
},
{
[]error{fmt.Errorf("1"), fmt.Errorf("267")},
map[string]bool{"1": true, "267": true},
},
{
[]error{fmt.Errorf("1"), nil, fmt.Errorf("1234")},
map[string]bool{"1": true, "1234": true},
},
{
[]error{nil, fmt.Errorf("1"), nil, fmt.Errorf("1234"), fmt.Errorf("22")},
map[string]bool{"1": true, "1234": true, "22": true},
},
}
for i, testCase := range testCases {
funcs := make([]func() error, len(testCase.errs))
for i := range testCase.errs {
err := testCase.errs[i]
funcs[i] = func() error { return err }
}
agg := AggregateGoroutines(funcs...)
if agg == nil {
if len(testCase.expected) > 0 {
t.Errorf("%d: expected %v, got nil", i, testCase.expected)
}
continue
}
if len(agg.Errors()) != len(testCase.expected) {
t.Errorf("%d: expected %d errors in aggregate, got %v", i, len(testCase.expected), agg)
continue
}
for _, err := range agg.Errors() {
if !testCase.expected[err.Error()] {
t.Errorf("%d: expected %v, got aggregate containing %v", i, testCase.expected, err)
}
}
}
}

View file

@ -1,176 +0,0 @@
/*
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 framer
import (
"bytes"
"io"
"io/ioutil"
"testing"
)
func TestRead(t *testing.T) {
data := []byte{
0x00, 0x00, 0x00, 0x04,
0x01, 0x02, 0x03, 0x04,
0x00, 0x00, 0x00, 0x03,
0x05, 0x06, 0x07,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01,
0x08,
}
b := bytes.NewBuffer(data)
r := NewLengthDelimitedFrameReader(ioutil.NopCloser(b))
buf := make([]byte, 1)
if n, err := r.Read(buf); err != io.ErrShortBuffer && n != 1 && bytes.Equal(buf, []byte{0x01}) {
t.Fatalf("unexpected: %v %d %v", err, n, buf)
}
if n, err := r.Read(buf); err != io.ErrShortBuffer && n != 1 && bytes.Equal(buf, []byte{0x02}) {
t.Fatalf("unexpected: %v %d %v", err, n, buf)
}
// read the remaining frame
buf = make([]byte, 2)
if n, err := r.Read(buf); err != nil && n != 2 && bytes.Equal(buf, []byte{0x03, 0x04}) {
t.Fatalf("unexpected: %v %d %v", err, n, buf)
}
// read with buffer equal to frame
buf = make([]byte, 3)
if n, err := r.Read(buf); err != nil && n != 3 && bytes.Equal(buf, []byte{0x05, 0x06, 0x07}) {
t.Fatalf("unexpected: %v %d %v", err, n, buf)
}
// read empty frame
buf = make([]byte, 3)
if n, err := r.Read(buf); err != nil && n != 0 && bytes.Equal(buf, []byte{}) {
t.Fatalf("unexpected: %v %d %v", err, n, buf)
}
// read with larger buffer than frame
buf = make([]byte, 3)
if n, err := r.Read(buf); err != nil && n != 1 && bytes.Equal(buf, []byte{0x08}) {
t.Fatalf("unexpected: %v %d %v", err, n, buf)
}
// read EOF
if n, err := r.Read(buf); err != io.EOF && n != 0 {
t.Fatalf("unexpected: %v %d", err, n)
}
}
func TestReadLarge(t *testing.T) {
data := []byte{
0x00, 0x00, 0x00, 0x04,
0x01, 0x02, 0x03, 0x04,
0x00, 0x00, 0x00, 0x03,
0x05, 0x06, 0x07,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x01,
0x08,
}
b := bytes.NewBuffer(data)
r := NewLengthDelimitedFrameReader(ioutil.NopCloser(b))
buf := make([]byte, 40)
if n, err := r.Read(buf); err != nil && n != 4 && bytes.Equal(buf, []byte{0x01, 0x02, 0x03, 0x04}) {
t.Fatalf("unexpected: %v %d %v", err, n, buf)
}
if n, err := r.Read(buf); err != nil && n != 3 && bytes.Equal(buf, []byte{0x05, 0x06, 0x7}) {
t.Fatalf("unexpected: %v %d %v", err, n, buf)
}
if n, err := r.Read(buf); err != nil && n != 0 && bytes.Equal(buf, []byte{}) {
t.Fatalf("unexpected: %v %d %v", err, n, buf)
}
if n, err := r.Read(buf); err != nil && n != 1 && bytes.Equal(buf, []byte{0x08}) {
t.Fatalf("unexpected: %v %d %v", err, n, buf)
}
// read EOF
if n, err := r.Read(buf); err != io.EOF && n != 0 {
t.Fatalf("unexpected: %v %d", err, n)
}
}
func TestReadInvalidFrame(t *testing.T) {
data := []byte{
0x00, 0x00, 0x00, 0x04,
0x01, 0x02,
}
b := bytes.NewBuffer(data)
r := NewLengthDelimitedFrameReader(ioutil.NopCloser(b))
buf := make([]byte, 1)
if n, err := r.Read(buf); err != io.ErrShortBuffer && n != 1 && bytes.Equal(buf, []byte{0x01}) {
t.Fatalf("unexpected: %v %d %v", err, n, buf)
}
// read the remaining frame
buf = make([]byte, 3)
if n, err := r.Read(buf); err != io.ErrUnexpectedEOF && n != 1 && bytes.Equal(buf, []byte{0x02}) {
t.Fatalf("unexpected: %v %d %v", err, n, buf)
}
// read EOF
if n, err := r.Read(buf); err != io.EOF && n != 0 {
t.Fatalf("unexpected: %v %d", err, n)
}
}
func TestJSONFrameReader(t *testing.T) {
b := bytes.NewBufferString("{\"test\":true}\n1\n[\"a\"]")
r := NewJSONFramedReader(ioutil.NopCloser(b))
buf := make([]byte, 20)
if n, err := r.Read(buf); err != nil || n != 13 || string(buf[:n]) != `{"test":true}` {
t.Fatalf("unexpected: %v %d %q", err, n, buf)
}
if n, err := r.Read(buf); err != nil || n != 1 || string(buf[:n]) != `1` {
t.Fatalf("unexpected: %v %d %q", err, n, buf)
}
if n, err := r.Read(buf); err != nil || n != 5 || string(buf[:n]) != `["a"]` {
t.Fatalf("unexpected: %v %d %q", err, n, buf)
}
if n, err := r.Read(buf); err != io.EOF || n != 0 {
t.Fatalf("unexpected: %v %d %q", err, n, buf)
}
}
func TestJSONFrameReaderShortBuffer(t *testing.T) {
b := bytes.NewBufferString("{\"test\":true}\n1\n[\"a\"]")
r := NewJSONFramedReader(ioutil.NopCloser(b))
buf := make([]byte, 3)
if n, err := r.Read(buf); err != io.ErrShortBuffer || n != 3 || string(buf[:n]) != `{"t` {
t.Fatalf("unexpected: %v %d %q", err, n, buf)
}
if n, err := r.Read(buf); err != io.ErrShortBuffer || n != 3 || string(buf[:n]) != `est` {
t.Fatalf("unexpected: %v %d %q", err, n, buf)
}
if n, err := r.Read(buf); err != io.ErrShortBuffer || n != 3 || string(buf[:n]) != `":t` {
t.Fatalf("unexpected: %v %d %q", err, n, buf)
}
if n, err := r.Read(buf); err != io.ErrShortBuffer || n != 3 || string(buf[:n]) != `rue` {
t.Fatalf("unexpected: %v %d %q", err, n, buf)
}
if n, err := r.Read(buf); err != nil || n != 1 || string(buf[:n]) != `}` {
t.Fatalf("unexpected: %v %d %q", err, n, buf)
}
if n, err := r.Read(buf); err != nil || n != 1 || string(buf[:n]) != `1` {
t.Fatalf("unexpected: %v %d %q", err, n, buf)
}
if n, err := r.Read(buf); err != io.ErrShortBuffer || n != 3 || string(buf[:n]) != `["a` {
t.Fatalf("unexpected: %v %d %q", err, n, buf)
}
if n, err := r.Read(buf); err != nil || n != 2 || string(buf[:n]) != `"]` {
t.Fatalf("unexpected: %v %d %q", err, n, buf)
}
if n, err := r.Read(buf); err != io.EOF || n != 0 {
t.Fatalf("unexpected: %v %d %q", err, n, buf)
}
}

View file

@ -1,125 +0,0 @@
/*
Copyright 2015 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package httpstream
import (
"net/http"
"reflect"
"testing"
)
type responseWriter struct {
header http.Header
statusCode *int
}
func newResponseWriter() *responseWriter {
return &responseWriter{
header: make(http.Header),
}
}
func (r *responseWriter) Header() http.Header {
return r.header
}
func (r *responseWriter) WriteHeader(code int) {
r.statusCode = &code
}
func (r *responseWriter) Write([]byte) (int, error) {
return 0, nil
}
func TestHandshake(t *testing.T) {
tests := map[string]struct {
clientProtocols []string
serverProtocols []string
expectedProtocol string
expectError bool
}{
"no client protocols": {
clientProtocols: []string{},
serverProtocols: []string{"a", "b"},
expectedProtocol: "",
},
"no common protocol": {
clientProtocols: []string{"c"},
serverProtocols: []string{"a", "b"},
expectedProtocol: "",
expectError: true,
},
"common protocol": {
clientProtocols: []string{"b"},
serverProtocols: []string{"a", "b"},
expectedProtocol: "b",
},
}
for name, test := range tests {
req, err := http.NewRequest("GET", "http://www.example.com/", nil)
if err != nil {
t.Fatalf("%s: error creating request: %v", name, err)
}
for _, p := range test.clientProtocols {
req.Header.Add(HeaderProtocolVersion, p)
}
w := newResponseWriter()
negotiated, err := Handshake(req, w, test.serverProtocols)
// verify negotiated protocol
if e, a := test.expectedProtocol, negotiated; e != a {
t.Errorf("%s: protocol: expected %q, got %q", name, e, a)
}
if test.expectError {
if err == nil {
t.Errorf("%s: expected error but did not get one", name)
}
if w.statusCode == nil {
t.Errorf("%s: expected w.statusCode to be set", name)
} else if e, a := http.StatusForbidden, *w.statusCode; e != a {
t.Errorf("%s: w.statusCode: expected %d, got %d", name, e, a)
}
if e, a := test.serverProtocols, w.Header()[HeaderAcceptedProtocolVersions]; !reflect.DeepEqual(e, a) {
t.Errorf("%s: accepted server protocols: expected %v, got %v", name, e, a)
}
continue
}
if !test.expectError && err != nil {
t.Errorf("%s: unexpected error: %v", name, err)
continue
}
if w.statusCode != nil {
t.Errorf("%s: unexpected non-nil w.statusCode: %d", name, w.statusCode)
}
if len(test.expectedProtocol) == 0 {
if len(w.Header()[HeaderProtocolVersion]) > 0 {
t.Errorf("%s: unexpected protocol version response header: %s", name, w.Header()[HeaderProtocolVersion])
}
continue
}
// verify response headers
if e, a := []string{test.expectedProtocol}, w.Header()[HeaderProtocolVersion]; !reflect.DeepEqual(e, a) {
t.Errorf("%s: protocol response header: expected %v, got %v", name, e, a)
}
}
}

View file

@ -1,164 +0,0 @@
/*
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 spdy
import (
"io"
"net"
"net/http"
"sync"
"testing"
"time"
"k8s.io/apimachinery/pkg/util/httpstream"
)
func runProxy(t *testing.T, backendUrl string, proxyUrl chan<- string, proxyDone chan<- struct{}) {
listener, err := net.Listen("tcp4", "localhost:0")
if err != nil {
t.Fatalf("error listening: %v", err)
}
defer listener.Close()
proxyUrl <- listener.Addr().String()
clientConn, err := listener.Accept()
if err != nil {
t.Errorf("proxy: error accepting client connection: %v", err)
return
}
backendConn, err := net.Dial("tcp4", backendUrl)
if err != nil {
t.Errorf("proxy: error dialing backend: %v", err)
return
}
defer backendConn.Close()
var wg sync.WaitGroup
wg.Add(2)
go func() {
defer wg.Done()
io.Copy(backendConn, clientConn)
}()
go func() {
defer wg.Done()
io.Copy(clientConn, backendConn)
}()
wg.Wait()
proxyDone <- struct{}{}
}
func runServer(t *testing.T, backendUrl chan<- string, serverDone chan<- struct{}) {
listener, err := net.Listen("tcp4", "localhost:0")
if err != nil {
t.Fatalf("server: error listening: %v", err)
}
defer listener.Close()
backendUrl <- listener.Addr().String()
conn, err := listener.Accept()
if err != nil {
t.Errorf("server: error accepting connection: %v", err)
return
}
streamChan := make(chan httpstream.Stream)
replySentChan := make(chan (<-chan struct{}))
spdyConn, err := NewServerConnection(conn, func(stream httpstream.Stream, replySent <-chan struct{}) error {
streamChan <- stream
replySentChan <- replySent
return nil
})
if err != nil {
t.Errorf("server: error creating spdy connection: %v", err)
return
}
stream := <-streamChan
replySent := <-replySentChan
<-replySent
buf := make([]byte, 1)
_, err = stream.Read(buf)
if err != io.EOF {
t.Errorf("server: unexpected read error: %v", err)
return
}
<-spdyConn.CloseChan()
raw := spdyConn.(*connection).conn
if err := raw.Wait(15 * time.Second); err != nil {
t.Errorf("server: timed out waiting for connection closure: %v", err)
}
serverDone <- struct{}{}
}
func TestConnectionCloseIsImmediateThroughAProxy(t *testing.T) {
serverDone := make(chan struct{})
backendUrlChan := make(chan string)
go runServer(t, backendUrlChan, serverDone)
backendUrl := <-backendUrlChan
proxyDone := make(chan struct{})
proxyUrlChan := make(chan string)
go runProxy(t, backendUrl, proxyUrlChan, proxyDone)
proxyUrl := <-proxyUrlChan
conn, err := net.Dial("tcp4", proxyUrl)
if err != nil {
t.Fatalf("client: error connecting to proxy: %v", err)
}
spdyConn, err := NewClientConnection(conn)
if err != nil {
t.Fatalf("client: error creating spdy connection: %v", err)
}
if _, err := spdyConn.CreateStream(http.Header{}); err != nil {
t.Fatalf("client: error creating stream: %v", err)
}
spdyConn.Close()
raw := spdyConn.(*connection).conn
if err := raw.Wait(15 * time.Second); err != nil {
t.Fatalf("client: timed out waiting for connection closure: %v", err)
}
expired := time.NewTimer(15 * time.Second)
defer expired.Stop()
i := 0
for {
select {
case <-expired.C:
t.Fatalf("timed out waiting for proxy and/or server closure")
case <-serverDone:
i++
case <-proxyDone:
i++
}
if i == 2 {
break
}
}
}

View file

@ -1,424 +0,0 @@
/*
Copyright 2015 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package spdy
import (
"crypto/tls"
"crypto/x509"
"encoding/base64"
"io"
"net/http"
"net/http/httptest"
"net/url"
"testing"
"github.com/elazarl/goproxy"
"k8s.io/apimachinery/pkg/util/httpstream"
)
// be sure to unset environment variable https_proxy (if exported) before testing, otherwise the testing will fail unexpectedly.
func TestRoundTripAndNewConnection(t *testing.T) {
localhostPool := x509.NewCertPool()
if !localhostPool.AppendCertsFromPEM(localhostCert) {
t.Errorf("error setting up localhostCert pool")
}
httpsServerInvalidHostname := func(h http.Handler) *httptest.Server {
cert, err := tls.X509KeyPair(exampleCert, exampleKey)
if err != nil {
t.Errorf("https (invalid hostname): proxy_test: %v", err)
}
ts := httptest.NewUnstartedServer(h)
ts.TLS = &tls.Config{
Certificates: []tls.Certificate{cert},
}
ts.StartTLS()
return ts
}
httpsServerValidHostname := func(h http.Handler) *httptest.Server {
cert, err := tls.X509KeyPair(localhostCert, localhostKey)
if err != nil {
t.Errorf("https (valid hostname): proxy_test: %v", err)
}
ts := httptest.NewUnstartedServer(h)
ts.TLS = &tls.Config{
Certificates: []tls.Certificate{cert},
}
ts.StartTLS()
return ts
}
testCases := map[string]struct {
serverFunc func(http.Handler) *httptest.Server
proxyServerFunc func(http.Handler) *httptest.Server
proxyAuth *url.Userinfo
clientTLS *tls.Config
serverConnectionHeader string
serverUpgradeHeader string
serverStatusCode int
shouldError bool
}{
"no headers": {
serverFunc: httptest.NewServer,
serverConnectionHeader: "",
serverUpgradeHeader: "",
serverStatusCode: http.StatusSwitchingProtocols,
shouldError: true,
},
"no upgrade header": {
serverFunc: httptest.NewServer,
serverConnectionHeader: "Upgrade",
serverUpgradeHeader: "",
serverStatusCode: http.StatusSwitchingProtocols,
shouldError: true,
},
"no connection header": {
serverFunc: httptest.NewServer,
serverConnectionHeader: "",
serverUpgradeHeader: "SPDY/3.1",
serverStatusCode: http.StatusSwitchingProtocols,
shouldError: true,
},
"no switching protocol status code": {
serverFunc: httptest.NewServer,
serverConnectionHeader: "Upgrade",
serverUpgradeHeader: "SPDY/3.1",
serverStatusCode: http.StatusForbidden,
shouldError: true,
},
"http": {
serverFunc: httptest.NewServer,
serverConnectionHeader: "Upgrade",
serverUpgradeHeader: "SPDY/3.1",
serverStatusCode: http.StatusSwitchingProtocols,
shouldError: false,
},
"https (invalid hostname + InsecureSkipVerify)": {
serverFunc: httpsServerInvalidHostname,
clientTLS: &tls.Config{InsecureSkipVerify: true},
serverConnectionHeader: "Upgrade",
serverUpgradeHeader: "SPDY/3.1",
serverStatusCode: http.StatusSwitchingProtocols,
shouldError: false,
},
"https (invalid hostname + hostname verification)": {
serverFunc: httpsServerInvalidHostname,
clientTLS: &tls.Config{InsecureSkipVerify: false},
serverConnectionHeader: "Upgrade",
serverUpgradeHeader: "SPDY/3.1",
serverStatusCode: http.StatusSwitchingProtocols,
shouldError: true,
},
"https (valid hostname + RootCAs)": {
serverFunc: httpsServerValidHostname,
clientTLS: &tls.Config{RootCAs: localhostPool},
serverConnectionHeader: "Upgrade",
serverUpgradeHeader: "SPDY/3.1",
serverStatusCode: http.StatusSwitchingProtocols,
shouldError: false,
},
"proxied http->http": {
serverFunc: httptest.NewServer,
proxyServerFunc: httptest.NewServer,
serverConnectionHeader: "Upgrade",
serverUpgradeHeader: "SPDY/3.1",
serverStatusCode: http.StatusSwitchingProtocols,
shouldError: false,
},
"proxied https (invalid hostname + InsecureSkipVerify) -> http": {
serverFunc: httptest.NewServer,
proxyServerFunc: httpsServerInvalidHostname,
clientTLS: &tls.Config{InsecureSkipVerify: true},
serverConnectionHeader: "Upgrade",
serverUpgradeHeader: "SPDY/3.1",
serverStatusCode: http.StatusSwitchingProtocols,
shouldError: false,
},
"proxied https with auth (invalid hostname + InsecureSkipVerify) -> http": {
serverFunc: httptest.NewServer,
proxyServerFunc: httpsServerInvalidHostname,
proxyAuth: url.UserPassword("proxyuser", "proxypasswd"),
clientTLS: &tls.Config{InsecureSkipVerify: true},
serverConnectionHeader: "Upgrade",
serverUpgradeHeader: "SPDY/3.1",
serverStatusCode: http.StatusSwitchingProtocols,
shouldError: false,
},
"proxied https (invalid hostname + hostname verification) -> http": {
serverFunc: httptest.NewServer,
proxyServerFunc: httpsServerInvalidHostname,
clientTLS: &tls.Config{InsecureSkipVerify: false},
serverConnectionHeader: "Upgrade",
serverUpgradeHeader: "SPDY/3.1",
serverStatusCode: http.StatusSwitchingProtocols,
shouldError: true, // fails because the client doesn't trust the proxy
},
"proxied https (valid hostname + RootCAs) -> http": {
serverFunc: httptest.NewServer,
proxyServerFunc: httpsServerValidHostname,
clientTLS: &tls.Config{RootCAs: localhostPool},
serverConnectionHeader: "Upgrade",
serverUpgradeHeader: "SPDY/3.1",
serverStatusCode: http.StatusSwitchingProtocols,
shouldError: false,
},
"proxied https with auth (valid hostname + RootCAs) -> http": {
serverFunc: httptest.NewServer,
proxyServerFunc: httpsServerValidHostname,
proxyAuth: url.UserPassword("proxyuser", "proxypasswd"),
clientTLS: &tls.Config{RootCAs: localhostPool},
serverConnectionHeader: "Upgrade",
serverUpgradeHeader: "SPDY/3.1",
serverStatusCode: http.StatusSwitchingProtocols,
shouldError: false,
},
"proxied https (invalid hostname + InsecureSkipVerify) -> https (invalid hostname)": {
serverFunc: httpsServerInvalidHostname,
proxyServerFunc: httpsServerInvalidHostname,
clientTLS: &tls.Config{InsecureSkipVerify: true},
serverConnectionHeader: "Upgrade",
serverUpgradeHeader: "SPDY/3.1",
serverStatusCode: http.StatusSwitchingProtocols,
shouldError: false, // works because the test proxy ignores TLS errors
},
"proxied https with auth (invalid hostname + InsecureSkipVerify) -> https (invalid hostname)": {
serverFunc: httpsServerInvalidHostname,
proxyServerFunc: httpsServerInvalidHostname,
proxyAuth: url.UserPassword("proxyuser", "proxypasswd"),
clientTLS: &tls.Config{InsecureSkipVerify: true},
serverConnectionHeader: "Upgrade",
serverUpgradeHeader: "SPDY/3.1",
serverStatusCode: http.StatusSwitchingProtocols,
shouldError: false, // works because the test proxy ignores TLS errors
},
"proxied https (invalid hostname + hostname verification) -> https (invalid hostname)": {
serverFunc: httpsServerInvalidHostname,
proxyServerFunc: httpsServerInvalidHostname,
clientTLS: &tls.Config{InsecureSkipVerify: false},
serverConnectionHeader: "Upgrade",
serverUpgradeHeader: "SPDY/3.1",
serverStatusCode: http.StatusSwitchingProtocols,
shouldError: true, // fails because the client doesn't trust the proxy
},
"proxied https (valid hostname + RootCAs) -> https (valid hostname + RootCAs)": {
serverFunc: httpsServerValidHostname,
proxyServerFunc: httpsServerValidHostname,
clientTLS: &tls.Config{RootCAs: localhostPool},
serverConnectionHeader: "Upgrade",
serverUpgradeHeader: "SPDY/3.1",
serverStatusCode: http.StatusSwitchingProtocols,
shouldError: false,
},
"proxied https with auth (valid hostname + RootCAs) -> https (valid hostname + RootCAs)": {
serverFunc: httpsServerValidHostname,
proxyServerFunc: httpsServerValidHostname,
proxyAuth: url.UserPassword("proxyuser", "proxypasswd"),
clientTLS: &tls.Config{RootCAs: localhostPool},
serverConnectionHeader: "Upgrade",
serverUpgradeHeader: "SPDY/3.1",
serverStatusCode: http.StatusSwitchingProtocols,
shouldError: false,
},
}
for k, testCase := range testCases {
server := testCase.serverFunc(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
if testCase.shouldError {
if e, a := httpstream.HeaderUpgrade, req.Header.Get(httpstream.HeaderConnection); e != a {
t.Fatalf("%s: Expected connection=upgrade header, got '%s", k, a)
}
w.Header().Set(httpstream.HeaderConnection, testCase.serverConnectionHeader)
w.Header().Set(httpstream.HeaderUpgrade, testCase.serverUpgradeHeader)
w.WriteHeader(testCase.serverStatusCode)
return
}
streamCh := make(chan httpstream.Stream)
responseUpgrader := NewResponseUpgrader()
spdyConn := responseUpgrader.UpgradeResponse(w, req, func(s httpstream.Stream, replySent <-chan struct{}) error {
streamCh <- s
return nil
})
if spdyConn == nil {
t.Fatalf("%s: unexpected nil spdyConn", k)
}
defer spdyConn.Close()
stream := <-streamCh
io.Copy(stream, stream)
}))
defer server.Close()
serverURL, err := url.Parse(server.URL)
if err != nil {
t.Fatalf("%s: Error creating request: %s", k, err)
}
req, err := http.NewRequest("GET", server.URL, nil)
if err != nil {
t.Fatalf("%s: Error creating request: %s", k, err)
}
spdyTransport := NewSpdyRoundTripper(testCase.clientTLS)
var proxierCalled bool
var proxyCalledWithHost string
var proxyCalledWithAuth bool
var proxyCalledWithAuthHeader string
if testCase.proxyServerFunc != nil {
proxyHandler := goproxy.NewProxyHttpServer()
proxyHandler.OnRequest().HandleConnectFunc(func(host string, ctx *goproxy.ProxyCtx) (*goproxy.ConnectAction, string) {
proxyCalledWithHost = host
proxyAuthHeaderName := "Proxy-Authorization"
_, proxyCalledWithAuth = ctx.Req.Header[proxyAuthHeaderName]
proxyCalledWithAuthHeader = ctx.Req.Header.Get(proxyAuthHeaderName)
return goproxy.OkConnect, host
})
proxy := testCase.proxyServerFunc(proxyHandler)
spdyTransport.proxier = func(proxierReq *http.Request) (*url.URL, error) {
proxierCalled = true
proxyURL, err := url.Parse(proxy.URL)
if err != nil {
return nil, err
}
proxyURL.User = testCase.proxyAuth
return proxyURL, nil
}
defer proxy.Close()
}
client := &http.Client{Transport: spdyTransport}
resp, err := client.Do(req)
var conn httpstream.Connection
if err == nil {
conn, err = spdyTransport.NewConnection(resp)
}
haveErr := err != nil
if e, a := testCase.shouldError, haveErr; e != a {
t.Fatalf("%s: shouldError=%t, got %t: %v", k, e, a, err)
}
if testCase.shouldError {
continue
}
defer conn.Close()
if resp.StatusCode != http.StatusSwitchingProtocols {
t.Fatalf("%s: expected http 101 switching protocols, got %d", k, resp.StatusCode)
}
stream, err := conn.CreateStream(http.Header{})
if err != nil {
t.Fatalf("%s: error creating client stream: %s", k, err)
}
n, err := stream.Write([]byte("hello"))
if err != nil {
t.Fatalf("%s: error writing to stream: %s", k, err)
}
if n != 5 {
t.Fatalf("%s: Expected to write 5 bytes, but actually wrote %d", k, n)
}
b := make([]byte, 5)
n, err = stream.Read(b)
if err != nil {
t.Fatalf("%s: error reading from stream: %s", k, err)
}
if n != 5 {
t.Fatalf("%s: Expected to read 5 bytes, but actually read %d", k, n)
}
if e, a := "hello", string(b[0:n]); e != a {
t.Fatalf("%s: expected '%s', got '%s'", k, e, a)
}
if testCase.proxyServerFunc != nil {
if !proxierCalled {
t.Fatalf("%s: Expected to use a proxy but proxier in SpdyRoundTripper wasn't called", k)
}
if proxyCalledWithHost != serverURL.Host {
t.Fatalf("%s: Expected to see a call to the proxy for backend %q, got %q", k, serverURL.Host, proxyCalledWithHost)
}
}
var expectedProxyAuth string
if testCase.proxyAuth != nil {
encodedCredentials := base64.StdEncoding.EncodeToString([]byte(testCase.proxyAuth.String()))
expectedProxyAuth = "Basic " + encodedCredentials
}
if len(expectedProxyAuth) == 0 && proxyCalledWithAuth {
t.Fatalf("%s: Proxy authorization unexpected, got %q", k, proxyCalledWithAuthHeader)
}
if proxyCalledWithAuthHeader != expectedProxyAuth {
t.Fatalf("%s: Expected to see a call to the proxy with credentials %q, got %q", k, testCase.proxyAuth, proxyCalledWithAuthHeader)
}
}
}
// exampleCert was generated from crypto/tls/generate_cert.go with the following command:
// go run generate_cert.go --rsa-bits 512 --host example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
var exampleCert = []byte(`-----BEGIN CERTIFICATE-----
MIIBcjCCAR6gAwIBAgIQBOUTYowZaENkZi0faI9DgTALBgkqhkiG9w0BAQswEjEQ
MA4GA1UEChMHQWNtZSBDbzAgFw03MDAxMDEwMDAwMDBaGA8yMDg0MDEyOTE2MDAw
MFowEjEQMA4GA1UEChMHQWNtZSBDbzBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQCZ
xfR3sgeHBraGFfF/24tTn4PRVAHOf2UOOxSQRs+aYjNqimFqf/SRIblQgeXdBJDR
gVK5F1Js2zwlehw0bHxRAgMBAAGjUDBOMA4GA1UdDwEB/wQEAwIApDATBgNVHSUE
DDAKBggrBgEFBQcDATAPBgNVHRMBAf8EBTADAQH/MBYGA1UdEQQPMA2CC2V4YW1w
bGUuY29tMAsGCSqGSIb3DQEBCwNBAI/mfBB8dm33IpUl+acSyWfL6gX5Wc0FFyVj
dKeesE1XBuPX1My/rzU6Oy/YwX7LOL4FaeNUS6bbL4axSLPKYSs=
-----END CERTIFICATE-----`)
var exampleKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
MIIBOgIBAAJBAJnF9HeyB4cGtoYV8X/bi1Ofg9FUAc5/ZQ47FJBGz5piM2qKYWp/
9JEhuVCB5d0EkNGBUrkXUmzbPCV6HDRsfFECAwEAAQJBAJLH9yPuButniACTn5L5
IJQw1mWQt6zBw9eCo41YWkA0866EgjC53aPZaRjXMp0uNJGdIsys2V5rCOOLWN2C
ODECIQDICHsi8QQQ9wpuJy8X5l8MAfxHL+DIqI84wQTeVM91FQIhAMTME8A18/7h
1Ad6drdnxAkuC0tX6Sx0LDozrmen+HFNAiAlcEDrt0RVkIcpOrg7tuhPLQf0oudl
Zvb3Xlj069awSQIgcT15E/43w2+RASifzVNhQ2MCTr1sSA8lL+xzK+REmnUCIBhQ
j4139pf8Re1J50zBxS/JlQfgDQi9sO9pYeiHIxNs
-----END RSA PRIVATE KEY-----`)
// localhostCert was generated from crypto/tls/generate_cert.go with the following command:
// go run generate_cert.go --rsa-bits 512 --host 127.0.0.1,::1,example.com --ca --start-date "Jan 1 00:00:00 1970" --duration=1000000h
var localhostCert = []byte(`-----BEGIN CERTIFICATE-----
MIIBdzCCASOgAwIBAgIBADALBgkqhkiG9w0BAQUwEjEQMA4GA1UEChMHQWNtZSBD
bzAeFw03MDAxMDEwMDAwMDBaFw00OTEyMzEyMzU5NTlaMBIxEDAOBgNVBAoTB0Fj
bWUgQ28wWjALBgkqhkiG9w0BAQEDSwAwSAJBAN55NcYKZeInyTuhcCwFMhDHCmwa
IUSdtXdcbItRB/yfXGBhiex00IaLXQnSU+QZPRZWYqeTEbFSgihqi1PUDy8CAwEA
AaNoMGYwDgYDVR0PAQH/BAQDAgCkMBMGA1UdJQQMMAoGCCsGAQUFBwMBMA8GA1Ud
EwEB/wQFMAMBAf8wLgYDVR0RBCcwJYILZXhhbXBsZS5jb22HBH8AAAGHEAAAAAAA
AAAAAAAAAAAAAAEwCwYJKoZIhvcNAQEFA0EAAoQn/ytgqpiLcZu9XKbCJsJcvkgk
Se6AbGXgSlq+ZCEVo0qIwSgeBqmsJxUu7NCSOwVJLYNEBO2DtIxoYVk+MA==
-----END CERTIFICATE-----`)
// localhostKey is the private key for localhostCert.
var localhostKey = []byte(`-----BEGIN RSA PRIVATE KEY-----
MIIBPAIBAAJBAN55NcYKZeInyTuhcCwFMhDHCmwaIUSdtXdcbItRB/yfXGBhiex0
0IaLXQnSU+QZPRZWYqeTEbFSgihqi1PUDy8CAwEAAQJBAQdUx66rfh8sYsgfdcvV
NoafYpnEcB5s4m/vSVe6SU7dCK6eYec9f9wpT353ljhDUHq3EbmE4foNzJngh35d
AekCIQDhRQG5Li0Wj8TM4obOnnXUXf1jRv0UkzE9AHWLG5q3AwIhAPzSjpYUDjVW
MCUXgckTpKCuGwbJk7424Nb8bLzf3kllAiA5mUBgjfr/WtFSJdWcPQ4Zt9KTMNKD
EUO0ukpTwEIl6wIhAMbGqZK3zAAFdq8DD2jPx+UJXnh0rnOkZBzDtJ6/iN69AiEA
1Aq8MJgTaYsDQWyU/hDq5YkDJc9e9DSCvUIzqxQWMQE=
-----END RSA PRIVATE KEY-----`)

View file

@ -21,8 +21,8 @@ import (
"net/http"
"strings"
"k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apimachinery/pkg/util/httpstream"
"k8s.io/apimachinery/pkg/util/runtime"
)
const HeaderSpdy31 = "SPDY/3.1"

View file

@ -1,93 +0,0 @@
/*
Copyright 2015 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package spdy
import (
"net/http"
"net/http/httptest"
"testing"
)
func TestUpgradeResponse(t *testing.T) {
testCases := []struct {
connectionHeader string
upgradeHeader string
shouldError bool
}{
{
connectionHeader: "",
upgradeHeader: "",
shouldError: true,
},
{
connectionHeader: "Upgrade",
upgradeHeader: "",
shouldError: true,
},
{
connectionHeader: "",
upgradeHeader: "SPDY/3.1",
shouldError: true,
},
{
connectionHeader: "Upgrade",
upgradeHeader: "SPDY/3.1",
shouldError: false,
},
}
for i, testCase := range testCases {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
upgrader := NewResponseUpgrader()
conn := upgrader.UpgradeResponse(w, req, nil)
haveErr := conn == nil
if e, a := testCase.shouldError, haveErr; e != a {
t.Fatalf("%d: expected shouldErr=%t, got %t", i, testCase.shouldError, haveErr)
}
if haveErr {
return
}
if conn == nil {
t.Fatalf("%d: unexpected nil conn", i)
}
defer conn.Close()
}))
defer server.Close()
req, err := http.NewRequest("GET", server.URL, nil)
if err != nil {
t.Fatalf("%d: error creating request: %s", i, err)
}
req.Header.Set("Connection", testCase.connectionHeader)
req.Header.Set("Upgrade", testCase.upgradeHeader)
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
t.Fatalf("%d: unexpected non-nil err from client.Do: %s", i, err)
}
if testCase.shouldError {
continue
}
if resp.StatusCode != http.StatusSwitchingProtocols {
t.Fatalf("%d: expected status 101 switching protocols, got %d", i, resp.StatusCode)
}
}
}

View file

@ -1,176 +0,0 @@
/*
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 intstr
import (
"encoding/json"
"reflect"
"testing"
"github.com/ghodss/yaml"
)
func TestFromInt(t *testing.T) {
i := FromInt(93)
if i.Type != Int || i.IntVal != 93 {
t.Errorf("Expected IntVal=93, got %+v", i)
}
}
func TestFromString(t *testing.T) {
i := FromString("76")
if i.Type != String || i.StrVal != "76" {
t.Errorf("Expected StrVal=\"76\", got %+v", i)
}
}
type IntOrStringHolder struct {
IOrS IntOrString `json:"val"`
}
func TestIntOrStringUnmarshalJSON(t *testing.T) {
cases := []struct {
input string
result IntOrString
}{
{"{\"val\": 123}", FromInt(123)},
{"{\"val\": \"123\"}", FromString("123")},
}
for _, c := range cases {
var result IntOrStringHolder
if err := json.Unmarshal([]byte(c.input), &result); err != nil {
t.Errorf("Failed to unmarshal input '%v': %v", c.input, err)
}
if result.IOrS != c.result {
t.Errorf("Failed to unmarshal input '%v': expected %+v, got %+v", c.input, c.result, result)
}
}
}
func TestIntOrStringMarshalJSON(t *testing.T) {
cases := []struct {
input IntOrString
result string
}{
{FromInt(123), "{\"val\":123}"},
{FromString("123"), "{\"val\":\"123\"}"},
}
for _, c := range cases {
input := IntOrStringHolder{c.input}
result, err := json.Marshal(&input)
if err != nil {
t.Errorf("Failed to marshal input '%v': %v", input, err)
}
if string(result) != c.result {
t.Errorf("Failed to marshal input '%v': expected: %+v, got %q", input, c.result, string(result))
}
}
}
func TestIntOrStringMarshalJSONUnmarshalYAML(t *testing.T) {
cases := []struct {
input IntOrString
}{
{FromInt(123)},
{FromString("123")},
}
for _, c := range cases {
input := IntOrStringHolder{c.input}
jsonMarshalled, err := json.Marshal(&input)
if err != nil {
t.Errorf("1: Failed to marshal input: '%v': %v", input, err)
}
var result IntOrStringHolder
err = yaml.Unmarshal(jsonMarshalled, &result)
if err != nil {
t.Errorf("2: Failed to unmarshal '%+v': %v", string(jsonMarshalled), err)
}
if !reflect.DeepEqual(input, result) {
t.Errorf("3: Failed to marshal input '%+v': got %+v", input, result)
}
}
}
func TestGetValueFromIntOrPercent(t *testing.T) {
tests := []struct {
input IntOrString
total int
roundUp bool
expectErr bool
expectVal int
}{
{
input: FromInt(123),
expectErr: false,
expectVal: 123,
},
{
input: FromString("90%"),
total: 100,
roundUp: true,
expectErr: false,
expectVal: 90,
},
{
input: FromString("90%"),
total: 95,
roundUp: true,
expectErr: false,
expectVal: 86,
},
{
input: FromString("90%"),
total: 95,
roundUp: false,
expectErr: false,
expectVal: 85,
},
{
input: FromString("%"),
expectErr: true,
},
{
input: FromString("90#"),
expectErr: true,
},
{
input: FromString("#%"),
expectErr: true,
},
}
for i, test := range tests {
t.Logf("test case %d", i)
value, err := GetValueFromIntOrPercent(&test.input, test.total, test.roundUp)
if test.expectErr && err == nil {
t.Errorf("expected error, but got none")
continue
}
if !test.expectErr && err != nil {
t.Errorf("unexpected err: %v", err)
continue
}
if test.expectVal != value {
t.Errorf("expected %v, but got %v", test.expectVal, value)
}
}
}

View file

@ -1,317 +0,0 @@
/*
Copyright 2015 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package json
import (
"fmt"
"math"
"reflect"
"strconv"
"strings"
"testing"
)
func TestEvaluateTypes(t *testing.T) {
testCases := []struct {
In string
Data interface{}
Out string
Err bool
}{
// Invalid syntaxes
{
In: `x`,
Err: true,
},
{
In: ``,
Err: true,
},
// Null
{
In: `null`,
Data: nil,
Out: `null`,
},
// Booleans
{
In: `true`,
Data: true,
Out: `true`,
},
{
In: `false`,
Data: false,
Out: `false`,
},
// Integers
{
In: `0`,
Data: int64(0),
Out: `0`,
},
{
In: `-0`,
Data: int64(-0),
Out: `0`,
},
{
In: `1`,
Data: int64(1),
Out: `1`,
},
{
In: `2147483647`,
Data: int64(math.MaxInt32),
Out: `2147483647`,
},
{
In: `-2147483648`,
Data: int64(math.MinInt32),
Out: `-2147483648`,
},
{
In: `9223372036854775807`,
Data: int64(math.MaxInt64),
Out: `9223372036854775807`,
},
{
In: `-9223372036854775808`,
Data: int64(math.MinInt64),
Out: `-9223372036854775808`,
},
// Int overflow
{
In: `9223372036854775808`, // MaxInt64 + 1
Data: float64(9223372036854775808),
Out: strconv.FormatFloat(9223372036854775808, 'g', -1, 64),
},
{
In: `-9223372036854775809`, // MinInt64 - 1
Data: float64(math.MinInt64),
Out: strconv.FormatFloat(-9223372036854775809, 'g', -1, 64),
},
// Floats
{
In: `0.0`,
Data: float64(0),
Out: `0`,
},
{
In: `-0.0`,
Data: float64(-0.0),
Out: `-0`,
},
{
In: `0.5`,
Data: float64(0.5),
Out: `0.5`,
},
{
In: `1e3`,
Data: float64(1e3),
Out: `1000`,
},
{
In: `1.5`,
Data: float64(1.5),
Out: `1.5`,
},
{
In: `-0.3`,
Data: float64(-.3),
Out: `-0.3`,
},
{
// Largest representable float32
In: `3.40282346638528859811704183484516925440e+38`,
Data: float64(math.MaxFloat32),
Out: strconv.FormatFloat(math.MaxFloat32, 'g', -1, 64),
},
{
// Smallest float32 without losing precision
In: `1.175494351e-38`,
Data: float64(1.175494351e-38),
Out: `1.175494351e-38`,
},
{
// float32 closest to zero
In: `1.401298464324817070923729583289916131280e-45`,
Data: float64(math.SmallestNonzeroFloat32),
Out: strconv.FormatFloat(math.SmallestNonzeroFloat32, 'g', -1, 64),
},
{
// Largest representable float64
In: `1.797693134862315708145274237317043567981e+308`,
Data: float64(math.MaxFloat64),
Out: strconv.FormatFloat(math.MaxFloat64, 'g', -1, 64),
},
{
// Closest to zero without losing precision
In: `2.2250738585072014e-308`,
Data: float64(2.2250738585072014e-308),
Out: `2.2250738585072014e-308`,
},
{
// float64 closest to zero
In: `4.940656458412465441765687928682213723651e-324`,
Data: float64(math.SmallestNonzeroFloat64),
Out: strconv.FormatFloat(math.SmallestNonzeroFloat64, 'g', -1, 64),
},
{
// math.MaxFloat64 + 2 overflow
In: `1.7976931348623159e+308`,
Err: true,
},
// Strings
{
In: `""`,
Data: string(""),
Out: `""`,
},
{
In: `"0"`,
Data: string("0"),
Out: `"0"`,
},
{
In: `"A"`,
Data: string("A"),
Out: `"A"`,
},
{
In: `"Iñtërnâtiônàlizætiøn"`,
Data: string("Iñtërnâtiônàlizætiøn"),
Out: `"Iñtërnâtiônàlizætiøn"`,
},
// Arrays
{
In: `[]`,
Data: []interface{}{},
Out: `[]`,
},
{
In: `[` + strings.Join([]string{
`null`,
`true`,
`false`,
`0`,
`9223372036854775807`,
`0.0`,
`0.5`,
`1.0`,
`1.797693134862315708145274237317043567981e+308`,
`"0"`,
`"A"`,
`"Iñtërnâtiônàlizætiøn"`,
`[null,true,1,1.0,1.5]`,
`{"boolkey":true,"floatkey":1.0,"intkey":1,"nullkey":null}`,
}, ",") + `]`,
Data: []interface{}{
nil,
true,
false,
int64(0),
int64(math.MaxInt64),
float64(0.0),
float64(0.5),
float64(1.0),
float64(math.MaxFloat64),
string("0"),
string("A"),
string("Iñtërnâtiônàlizætiøn"),
[]interface{}{nil, true, int64(1), float64(1.0), float64(1.5)},
map[string]interface{}{"nullkey": nil, "boolkey": true, "intkey": int64(1), "floatkey": float64(1.0)},
},
Out: `[` + strings.Join([]string{
`null`,
`true`,
`false`,
`0`,
`9223372036854775807`,
`0`,
`0.5`,
`1`,
strconv.FormatFloat(math.MaxFloat64, 'g', -1, 64),
`"0"`,
`"A"`,
`"Iñtërnâtiônàlizætiøn"`,
`[null,true,1,1,1.5]`,
`{"boolkey":true,"floatkey":1,"intkey":1,"nullkey":null}`, // gets alphabetized by Marshal
}, ",") + `]`,
},
// Maps
{
In: `{}`,
Data: map[string]interface{}{},
Out: `{}`,
},
{
In: `{"boolkey":true,"floatkey":1.0,"intkey":1,"nullkey":null}`,
Data: map[string]interface{}{"nullkey": nil, "boolkey": true, "intkey": int64(1), "floatkey": float64(1.0)},
Out: `{"boolkey":true,"floatkey":1,"intkey":1,"nullkey":null}`, // gets alphabetized by Marshal
},
}
for _, tc := range testCases {
inputJSON := fmt.Sprintf(`{"data":%s}`, tc.In)
expectedJSON := fmt.Sprintf(`{"data":%s}`, tc.Out)
m := map[string]interface{}{}
err := Unmarshal([]byte(inputJSON), &m)
if tc.Err && err != nil {
// Expected error
continue
}
if err != nil {
t.Errorf("%s: error decoding: %v", tc.In, err)
continue
}
if tc.Err {
t.Errorf("%s: expected error, got none", tc.In)
continue
}
data, ok := m["data"]
if !ok {
t.Errorf("%s: decoded object missing data key: %#v", tc.In, m)
continue
}
if !reflect.DeepEqual(tc.Data, data) {
t.Errorf("%s: expected\n\t%#v (%v), got\n\t%#v (%v)", tc.In, tc.Data, reflect.TypeOf(tc.Data), data, reflect.TypeOf(data))
continue
}
outputJSON, err := Marshal(m)
if err != nil {
t.Errorf("%s: error encoding: %v", tc.In, err)
continue
}
if expectedJSON != string(outputJSON) {
t.Errorf("%s: expected\n\t%s, got\n\t%s", tc.In, expectedJSON, string(outputJSON))
continue
}
}
}

View file

@ -0,0 +1,66 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package mergepatch
import (
"errors"
"fmt"
)
var ErrBadJSONDoc = errors.New("Invalid JSON document")
var ErrNoListOfLists = errors.New("Lists of lists are not supported")
var ErrBadPatchFormatForPrimitiveList = errors.New("Invalid patch format of primitive list")
// IsPreconditionFailed returns true if the provided error indicates
// a precondition failed.
func IsPreconditionFailed(err error) bool {
_, ok := err.(ErrPreconditionFailed)
return ok
}
type ErrPreconditionFailed struct {
message string
}
func NewErrPreconditionFailed(target map[string]interface{}) ErrPreconditionFailed {
s := fmt.Sprintf("precondition failed for: %v", target)
return ErrPreconditionFailed{s}
}
func (err ErrPreconditionFailed) Error() string {
return err.message
}
type ErrConflict struct {
message string
}
func NewErrConflict(patch, current string) ErrConflict {
s := fmt.Sprintf("patch:\n%s\nconflicts with changes made from original to current:\n%s\n", patch, current)
return ErrConflict{s}
}
func (err ErrConflict) Error() string {
return err.message
}
// IsConflict returns true if the provided error indicates
// a conflict between the patch and the current configuration.
func IsConflict(err error) bool {
_, ok := err.(ErrConflict)
return ok
}

133
vendor/k8s.io/apimachinery/pkg/util/mergepatch/util.go generated vendored Normal file
View file

@ -0,0 +1,133 @@
/*
Copyright 2017 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package mergepatch
import (
"fmt"
"reflect"
"github.com/davecgh/go-spew/spew"
"github.com/ghodss/yaml"
)
// PreconditionFunc asserts that an incompatible change is not present within a patch.
type PreconditionFunc func(interface{}) bool
// RequireKeyUnchanged returns a precondition function that fails if the provided key
// is present in the patch (indicating that its value has changed).
func RequireKeyUnchanged(key string) PreconditionFunc {
return func(patch interface{}) bool {
patchMap, ok := patch.(map[string]interface{})
if !ok {
return true
}
// The presence of key means that its value has been changed, so the test fails.
_, ok = patchMap[key]
return !ok
}
}
// RequireMetadataKeyUnchanged creates a precondition function that fails
// if the metadata.key is present in the patch (indicating its value
// has changed).
func RequireMetadataKeyUnchanged(key string) PreconditionFunc {
return func(patch interface{}) bool {
patchMap, ok := patch.(map[string]interface{})
if !ok {
return true
}
patchMap1, ok := patchMap["metadata"]
if !ok {
return true
}
patchMap2, ok := patchMap1.(map[string]interface{})
if !ok {
return true
}
_, ok = patchMap2[key]
return !ok
}
}
func ToYAMLOrError(v interface{}) string {
y, err := toYAML(v)
if err != nil {
return err.Error()
}
return y
}
func toYAML(v interface{}) (string, error) {
y, err := yaml.Marshal(v)
if err != nil {
return "", fmt.Errorf("yaml marshal failed:%v\n%v\n", err, spew.Sdump(v))
}
return string(y), nil
}
// HasConflicts returns true if the left and right JSON interface objects overlap with
// different values in any key. All keys are required to be strings. Since patches of the
// same Type have congruent keys, this is valid for multiple patch types. This method
// supports JSON merge patch semantics.
//
// NOTE: Numbers with different types (e.g. int(0) vs int64(0)) will be detected as conflicts.
// Make sure the unmarshaling of left and right are consistent (e.g. use the same library).
func HasConflicts(left, right interface{}) (bool, error) {
switch typedLeft := left.(type) {
case map[string]interface{}:
switch typedRight := right.(type) {
case map[string]interface{}:
for key, leftValue := range typedLeft {
rightValue, ok := typedRight[key]
if !ok {
continue
}
if conflict, err := HasConflicts(leftValue, rightValue); err != nil || conflict {
return conflict, err
}
}
return false, nil
default:
return true, nil
}
case []interface{}:
switch typedRight := right.(type) {
case []interface{}:
if len(typedLeft) != len(typedRight) {
return true, nil
}
for i := range typedLeft {
if conflict, err := HasConflicts(typedLeft[i], typedRight[i]); err != nil || conflict {
return conflict, err
}
}
return false, nil
default:
return true, nil
}
case string, float64, bool, int, int64, nil:
return !reflect.DeepEqual(left, right), nil
default:
return true, fmt.Errorf("unknown type: %v", reflect.TypeOf(left))
}
}

View file

@ -1,246 +0,0 @@
/*
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 net
import (
"crypto/tls"
"net"
"net/http"
"net/url"
"os"
"reflect"
"runtime"
"strings"
"testing"
"k8s.io/apimachinery/pkg/util/sets"
)
func TestCloneTLSConfig(t *testing.T) {
expected := sets.NewString(
// These fields are copied in CloneTLSConfig
"Rand",
"Time",
"Certificates",
"RootCAs",
"NextProtos",
"ServerName",
"InsecureSkipVerify",
"CipherSuites",
"PreferServerCipherSuites",
"MinVersion",
"MaxVersion",
"CurvePreferences",
"NameToCertificate",
"GetCertificate",
"ClientAuth",
"ClientCAs",
"ClientSessionCache",
// These fields are not copied
"SessionTicketsDisabled",
"SessionTicketKey",
// These fields are unexported
"serverInitOnce",
"mutex",
"sessionTicketKeys",
)
// See #33936.
if strings.HasPrefix(runtime.Version(), "go1.7") {
expected.Insert("DynamicRecordSizingDisabled", "Renegotiation")
}
fields := sets.NewString()
structType := reflect.TypeOf(tls.Config{})
for i := 0; i < structType.NumField(); i++ {
fields.Insert(structType.Field(i).Name)
}
if missing := expected.Difference(fields); len(missing) > 0 {
t.Errorf("Expected fields that were not seen in http.Transport: %v", missing.List())
}
if extra := fields.Difference(expected); len(extra) > 0 {
t.Errorf("New fields seen in http.Transport: %v\nAdd to CopyClientTLSConfig if client-relevant, then add to expected list in TestCopyClientTLSConfig", extra.List())
}
}
func TestGetClientIP(t *testing.T) {
ipString := "10.0.0.1"
ip := net.ParseIP(ipString)
invalidIPString := "invalidIPString"
testCases := []struct {
Request http.Request
ExpectedIP net.IP
}{
{
Request: http.Request{},
},
{
Request: http.Request{
Header: map[string][]string{
"X-Real-Ip": {ipString},
},
},
ExpectedIP: ip,
},
{
Request: http.Request{
Header: map[string][]string{
"X-Real-Ip": {invalidIPString},
},
},
},
{
Request: http.Request{
Header: map[string][]string{
"X-Forwarded-For": {ipString},
},
},
ExpectedIP: ip,
},
{
Request: http.Request{
Header: map[string][]string{
"X-Forwarded-For": {invalidIPString},
},
},
},
{
Request: http.Request{
Header: map[string][]string{
"X-Forwarded-For": {invalidIPString + "," + ipString},
},
},
ExpectedIP: ip,
},
{
Request: http.Request{
// RemoteAddr is in the form host:port
RemoteAddr: ipString + ":1234",
},
ExpectedIP: ip,
},
{
Request: http.Request{
RemoteAddr: invalidIPString,
},
},
{
Request: http.Request{
Header: map[string][]string{
"X-Forwarded-For": {invalidIPString},
},
// RemoteAddr is in the form host:port
RemoteAddr: ipString,
},
ExpectedIP: ip,
},
}
for i, test := range testCases {
if a, e := GetClientIP(&test.Request), test.ExpectedIP; reflect.DeepEqual(e, a) != true {
t.Fatalf("test case %d failed. expected: %v, actual: %v", i, e, a)
}
}
}
func TestProxierWithNoProxyCIDR(t *testing.T) {
testCases := []struct {
name string
noProxy string
url string
expectedDelegated bool
}{
{
name: "no env",
url: "https://192.168.143.1/api",
expectedDelegated: true,
},
{
name: "no cidr",
noProxy: "192.168.63.1",
url: "https://192.168.143.1/api",
expectedDelegated: true,
},
{
name: "hostname",
noProxy: "192.168.63.0/24,192.168.143.0/24",
url: "https://my-hostname/api",
expectedDelegated: true,
},
{
name: "match second cidr",
noProxy: "192.168.63.0/24,192.168.143.0/24",
url: "https://192.168.143.1/api",
expectedDelegated: false,
},
{
name: "match second cidr with host:port",
noProxy: "192.168.63.0/24,192.168.143.0/24",
url: "https://192.168.143.1:8443/api",
expectedDelegated: false,
},
}
for _, test := range testCases {
os.Setenv("NO_PROXY", test.noProxy)
actualDelegated := false
proxyFunc := NewProxierWithNoProxyCIDR(func(req *http.Request) (*url.URL, error) {
actualDelegated = true
return nil, nil
})
req, err := http.NewRequest("GET", test.url, nil)
if err != nil {
t.Errorf("%s: unexpected err: %v", test.name, err)
continue
}
if _, err := proxyFunc(req); err != nil {
t.Errorf("%s: unexpected err: %v", test.name, err)
continue
}
if test.expectedDelegated != actualDelegated {
t.Errorf("%s: expected %v, got %v", test.name, test.expectedDelegated, actualDelegated)
continue
}
}
}
type fakeTLSClientConfigHolder struct {
called bool
}
func (f *fakeTLSClientConfigHolder) TLSClientConfig() *tls.Config {
f.called = true
return nil
}
func (f *fakeTLSClientConfigHolder) RoundTrip(*http.Request) (*http.Response, error) {
return nil, nil
}
func TestTLSClientConfigHolder(t *testing.T) {
rt := &fakeTLSClientConfigHolder{}
TLSClientConfig(rt)
if !rt.called {
t.Errorf("didn't find tls config")
}
}

View file

@ -1,300 +0,0 @@
/*
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 net
import (
"fmt"
"io"
"net"
"strings"
"testing"
)
const gatewayfirst = `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
eth3 00000000 0100FE0A 0003 0 0 1024 00000000 0 0 0
eth3 0000FE0A 00000000 0001 0 0 0 0080FFFF 0 0 0
docker0 000011AC 00000000 0001 0 0 0 0000FFFF 0 0 0
virbr0 007AA8C0 00000000 0001 0 0 0 00FFFFFF 0 0 0
`
const gatewaylast = `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
docker0 000011AC 00000000 0001 0 0 0 0000FFFF 0 0 0
virbr0 007AA8C0 00000000 0001 0 0 0 00FFFFFF 0 0 0
eth3 0000FE0A 00000000 0001 0 0 0 0080FFFF 0 0 0
eth3 00000000 0100FE0A 0003 0 0 1024 00000000 0 0 0
`
const gatewaymiddle = `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
eth3 0000FE0A 00000000 0001 0 0 0 0080FFFF 0 0 0
docker0 000011AC 00000000 0001 0 0 0 0000FFFF 0 0 0
eth3 00000000 0100FE0A 0003 0 0 1024 00000000 0 0 0
virbr0 007AA8C0 00000000 0001 0 0 0 00FFFFFF 0 0 0
`
const noInternetConnection = `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
docker0 000011AC 00000000 0001 0 0 0 0000FFFF 0 0 0
virbr0 007AA8C0 00000000 0001 0 0 0 00FFFFFF 0 0 0
`
const nothing = `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
`
const gatewayfirstIpv6_1 = `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
eth3 00000000 0100FE0A 0003 0 0 1024 00000000 0 0 0
eth3 0000FE0AA1 00000000 0001 0 0 0 0080FFFF 0 0 0
docker0 000011AC 00000000 0001 0 0 0 0000FFFF 0 0 0
virbr0 007AA8C0 00000000 0001 0 0 0 00FFFFFF 0 0 0
`
const gatewayfirstIpv6_2 = `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
eth3 00000000 0100FE0AA1 0003 0 0 1024 00000000 0 0 0
eth3 0000FE0A 00000000 0001 0 0 0 0080FFFF 0 0 0
docker0 000011AC 00000000 0001 0 0 0 0000FFFF 0 0 0
virbr0 007AA8C0 00000000 0001 0 0 0 00FFFFFF 0 0 0
`
const route_Invalidhex = `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
eth3 00000000 0100FE0AA 0003 0 0 1024 00000000 0 0 0
eth3 0000FE0A 00000000 0001 0 0 0 0080FFFF 0 0 0
docker0 000011AC 00000000 0001 0 0 0 0000FFFF 0 0 0
virbr0 007AA8C0 00000000 0001 0 0 0 00FFFFFF 0 0 0
`
// Based on DigitalOcean COREOS
const gatewayfirstLinkLocal = `Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
eth0 00000000 0120372D 0001 0 0 0 00000000 0 0 0
eth0 00000000 00000000 0001 0 0 2048 00000000 0 0 0
`
func TestGetRoutes(t *testing.T) {
testCases := []struct {
tcase string
route string
expected int
}{
{"gatewayfirst", gatewayfirst, 4},
{"gatewaymiddle", gatewaymiddle, 4},
{"gatewaylast", gatewaylast, 4},
{"nothing", nothing, 0},
{"gatewayfirstIpv6_1", gatewayfirstIpv6_1, 0},
{"gatewayfirstIpv6_2", gatewayfirstIpv6_2, 0},
{"route_Invalidhex", route_Invalidhex, 0},
}
for _, tc := range testCases {
r := strings.NewReader(tc.route)
routes, err := getRoutes(r)
if len(routes) != tc.expected {
t.Errorf("case[%v]: expected %v, got %v .err : %v", tc.tcase, tc.expected, len(routes), err)
}
}
}
func TestParseIP(t *testing.T) {
testCases := []struct {
tcase string
ip string
success bool
expected net.IP
}{
{"empty", "", false, nil},
{"too short", "AA", false, nil},
{"too long", "0011223344", false, nil},
{"invalid", "invalid!", false, nil},
{"zero", "00000000", true, net.IP{0, 0, 0, 0}},
{"ffff", "FFFFFFFF", true, net.IP{0xff, 0xff, 0xff, 0xff}},
{"valid", "12345678", true, net.IP{120, 86, 52, 18}},
}
for _, tc := range testCases {
ip, err := parseIP(tc.ip)
if !ip.Equal(tc.expected) {
t.Errorf("case[%v]: expected %q, got %q . err : %v", tc.tcase, tc.expected, ip, err)
}
}
}
func TestIsInterfaceUp(t *testing.T) {
testCases := []struct {
tcase string
intf net.Interface
expected bool
}{
{"up", net.Interface{Index: 0, MTU: 0, Name: "eth3", HardwareAddr: nil, Flags: net.FlagUp}, true},
{"down", net.Interface{Index: 0, MTU: 0, Name: "eth3", HardwareAddr: nil, Flags: 0}, false},
{"nothing", net.Interface{}, false},
}
for _, tc := range testCases {
it := isInterfaceUp(&tc.intf)
if it != tc.expected {
t.Errorf("case[%v]: expected %v, got %v .", tc.tcase, tc.expected, it)
}
}
}
type addrStruct struct{ val string }
func (a addrStruct) Network() string {
return a.val
}
func (a addrStruct) String() string {
return a.val
}
func TestFinalIP(t *testing.T) {
testCases := []struct {
tcase string
addr []net.Addr
expected net.IP
}{
{"ipv6", []net.Addr{addrStruct{val: "fe80::2f7:6fff:fe6e:2956/64"}}, nil},
{"invalidCIDR", []net.Addr{addrStruct{val: "fe80::2f7:67fff:fe6e:2956/64"}}, nil},
{"loopback", []net.Addr{addrStruct{val: "127.0.0.1/24"}}, nil},
{"ip4", []net.Addr{addrStruct{val: "10.254.12.132/17"}}, net.ParseIP("10.254.12.132")},
{"nothing", []net.Addr{}, nil},
}
for _, tc := range testCases {
ip, err := getFinalIP(tc.addr)
if !ip.Equal(tc.expected) {
t.Errorf("case[%v]: expected %v, got %v .err : %v", tc.tcase, tc.expected, ip, err)
}
}
}
func TestAddrs(t *testing.T) {
var nw networkInterfacer = validNetworkInterface{}
intf := net.Interface{Index: 0, MTU: 0, Name: "eth3", HardwareAddr: nil, Flags: 0}
addrs, err := nw.Addrs(&intf)
if err != nil {
t.Errorf("expected no error got : %v", err)
}
if len(addrs) != 2 {
t.Errorf("expected addrs: 2 got null")
}
}
type validNetworkInterface struct {
}
func (_ validNetworkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
c := net.Interface{Index: 0, MTU: 0, Name: "eth3", HardwareAddr: nil, Flags: net.FlagUp}
return &c, nil
}
func (_ validNetworkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
var ifat []net.Addr
ifat = []net.Addr{
addrStruct{val: "fe80::2f7:6fff:fe6e:2956/64"}, addrStruct{val: "10.254.71.145/17"}}
return ifat, nil
}
type validNetworkInterfaceWithLinkLocal struct {
}
func (_ validNetworkInterfaceWithLinkLocal) InterfaceByName(intfName string) (*net.Interface, error) {
c := net.Interface{Index: 0, MTU: 0, Name: "eth0", HardwareAddr: nil, Flags: net.FlagUp}
return &c, nil
}
func (_ validNetworkInterfaceWithLinkLocal) Addrs(intf *net.Interface) ([]net.Addr, error) {
var ifat []net.Addr
ifat = []net.Addr{addrStruct{val: "169.254.162.166/16"}, addrStruct{val: "45.55.47.146/19"}}
return ifat, nil
}
type validNetworkInterfacewithIpv6Only struct {
}
func (_ validNetworkInterfacewithIpv6Only) InterfaceByName(intfName string) (*net.Interface, error) {
c := net.Interface{Index: 0, MTU: 0, Name: "eth3", HardwareAddr: nil, Flags: net.FlagUp}
return &c, nil
}
func (_ validNetworkInterfacewithIpv6Only) Addrs(intf *net.Interface) ([]net.Addr, error) {
var ifat []net.Addr
ifat = []net.Addr{addrStruct{val: "fe80::2f7:6fff:fe6e:2956/64"}}
return ifat, nil
}
type noNetworkInterface struct {
}
func (_ noNetworkInterface) InterfaceByName(intfName string) (*net.Interface, error) {
return nil, fmt.Errorf("unable get Interface")
}
func (_ noNetworkInterface) Addrs(intf *net.Interface) ([]net.Addr, error) {
return nil, nil
}
type networkInterfacewithNoAddrs struct {
}
func (_ networkInterfacewithNoAddrs) InterfaceByName(intfName string) (*net.Interface, error) {
c := net.Interface{Index: 0, MTU: 0, Name: "eth3", HardwareAddr: nil, Flags: net.FlagUp}
return &c, nil
}
func (_ networkInterfacewithNoAddrs) Addrs(intf *net.Interface) ([]net.Addr, error) {
return nil, fmt.Errorf("unable get Addrs")
}
type networkInterfacewithIpv6addrs struct {
}
func (_ networkInterfacewithIpv6addrs) InterfaceByName(intfName string) (*net.Interface, error) {
c := net.Interface{Index: 0, MTU: 0, Name: "eth3", HardwareAddr: nil, Flags: net.FlagUp}
return &c, nil
}
func (_ networkInterfacewithIpv6addrs) Addrs(intf *net.Interface) ([]net.Addr, error) {
var ifat []net.Addr
ifat = []net.Addr{addrStruct{val: "fe80::2f7:6ffff:fe6e:2956/64"}}
return ifat, nil
}
func TestGetIPFromInterface(t *testing.T) {
testCases := []struct {
tcase string
nwname string
nw networkInterfacer
expected net.IP
}{
{"valid", "eth3", validNetworkInterface{}, net.ParseIP("10.254.71.145")},
{"ipv6", "eth3", validNetworkInterfacewithIpv6Only{}, nil},
{"nothing", "eth3", noNetworkInterface{}, nil},
}
for _, tc := range testCases {
ip, err := getIPFromInterface(tc.nwname, tc.nw)
if !ip.Equal(tc.expected) {
t.Errorf("case[%v]: expected %v, got %+v .err : %v", tc.tcase, tc.expected, ip, err)
}
}
}
func TestChooseHostInterfaceFromRoute(t *testing.T) {
testCases := []struct {
tcase string
inFile io.Reader
nw networkInterfacer
expected net.IP
}{
{"valid_routefirst", strings.NewReader(gatewayfirst), validNetworkInterface{}, net.ParseIP("10.254.71.145")},
{"valid_routelast", strings.NewReader(gatewaylast), validNetworkInterface{}, net.ParseIP("10.254.71.145")},
{"valid_routemiddle", strings.NewReader(gatewaymiddle), validNetworkInterface{}, net.ParseIP("10.254.71.145")},
{"valid_routemiddle_ipv6", strings.NewReader(gatewaymiddle), validNetworkInterfacewithIpv6Only{}, nil},
{"no internet connection", strings.NewReader(noInternetConnection), validNetworkInterface{}, nil},
{"no non-link-local ip", strings.NewReader(gatewayfirstLinkLocal), validNetworkInterfaceWithLinkLocal{}, net.ParseIP("45.55.47.146")},
{"no route", strings.NewReader(nothing), validNetworkInterface{}, nil},
{"no route file", nil, validNetworkInterface{}, nil},
{"no interfaces", nil, noNetworkInterface{}, nil},
{"no interface Addrs", strings.NewReader(gatewaymiddle), networkInterfacewithNoAddrs{}, nil},
{"Invalid Addrs", strings.NewReader(gatewaymiddle), networkInterfacewithIpv6addrs{}, nil},
}
for _, tc := range testCases {
ip, err := chooseHostInterfaceFromRoute(tc.inFile, tc.nw)
if !ip.Equal(tc.expected) {
t.Errorf("case[%v]: expected %v, got %+v .err : %v", tc.tcase, tc.expected, ip, err)
}
}
}

View file

@ -1,69 +0,0 @@
/*
Copyright 2015 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package net
import (
"testing"
flag "github.com/spf13/pflag"
)
func TestPortRange(t *testing.T) {
testCases := []struct {
input string
success bool
expected string
included int
excluded int
}{
{"100-200", true, "100-200", 200, 201},
{" 100-200 ", true, "100-200", 200, 201},
{"0-0", true, "0-0", 0, 1},
{"", true, "", -1, 0},
{"100", false, "", -1, -1},
{"100 - 200", false, "", -1, -1},
{"-100", false, "", -1, -1},
{"100-", false, "", -1, -1},
{"200-100", false, "", -1, -1},
{"60000-70000", false, "", -1, -1},
{"70000-80000", false, "", -1, -1},
}
for i := range testCases {
tc := &testCases[i]
pr := &PortRange{}
var f flag.Value = pr
err := f.Set(tc.input)
if err != nil && tc.success == true {
t.Errorf("expected success, got %q", err)
continue
} else if err == nil && tc.success == false {
t.Errorf("expected failure")
continue
} else if tc.success {
if f.String() != tc.expected {
t.Errorf("expected %q, got %q", tc.expected, f.String())
}
if tc.included >= 0 && !pr.Contains(tc.included) {
t.Errorf("expected %q to include %d", f.String(), tc.included)
}
if tc.excluded >= 0 && pr.Contains(tc.excluded) {
t.Errorf("expected %q to exclude %d", f.String(), tc.excluded)
}
}
}
}

View file

@ -1,121 +0,0 @@
/*
Copyright 2015 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package net
import (
"testing"
)
func TestSplitSchemeNamePort(t *testing.T) {
table := []struct {
in string
name, port, scheme string
valid bool
normalized bool
}{
{
in: "aoeu:asdf",
name: "aoeu",
port: "asdf",
valid: true,
normalized: true,
}, {
in: "http:aoeu:asdf",
scheme: "http",
name: "aoeu",
port: "asdf",
valid: true,
normalized: true,
}, {
in: "https:aoeu:",
scheme: "https",
name: "aoeu",
port: "",
valid: true,
normalized: false,
}, {
in: "https:aoeu:asdf",
scheme: "https",
name: "aoeu",
port: "asdf",
valid: true,
normalized: true,
}, {
in: "aoeu:",
name: "aoeu",
valid: true,
normalized: false,
}, {
in: "aoeu",
name: "aoeu",
valid: true,
normalized: true,
}, {
in: ":asdf",
valid: false,
}, {
in: "aoeu:asdf:htns",
valid: false,
}, {
in: "http::asdf",
valid: false,
}, {
in: "http::",
valid: false,
}, {
in: "",
valid: false,
},
}
for _, item := range table {
scheme, name, port, valid := SplitSchemeNamePort(item.in)
if e, a := item.scheme, scheme; e != a {
t.Errorf("%q: Wanted %q, got %q", item.in, e, a)
}
if e, a := item.name, name; e != a {
t.Errorf("%q: Wanted %q, got %q", item.in, e, a)
}
if e, a := item.port, port; e != a {
t.Errorf("%q: Wanted %q, got %q", item.in, e, a)
}
if e, a := item.valid, valid; e != a {
t.Errorf("%q: Wanted %t, got %t", item.in, e, a)
}
// Make sure valid items round trip through JoinSchemeNamePort
if item.valid {
out := JoinSchemeNamePort(scheme, name, port)
if item.normalized && out != item.in {
t.Errorf("%q: Wanted %s, got %s", item.in, item.in, out)
}
scheme, name, port, valid := SplitSchemeNamePort(out)
if e, a := item.scheme, scheme; e != a {
t.Errorf("%q: Wanted %q, got %q", item.in, e, a)
}
if e, a := item.name, name; e != a {
t.Errorf("%q: Wanted %q, got %q", item.in, e, a)
}
if e, a := item.port, port; e != a {
t.Errorf("%q: Wanted %q, got %q", item.in, e, a)
}
if e, a := item.valid, valid; e != a {
t.Errorf("%q: Wanted %t, got %t", item.in, e, a)
}
}
}
}

View file

@ -1,68 +0,0 @@
/*
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 net
import (
"net"
"testing"
)
func getIPNet(cidr string) *net.IPNet {
_, ipnet, _ := net.ParseCIDR(cidr)
return ipnet
}
func TestIPNetEqual(t *testing.T) {
testCases := []struct {
ipnet1 *net.IPNet
ipnet2 *net.IPNet
expect bool
}{
//null case
{
getIPNet("10.0.0.1/24"),
getIPNet(""),
false,
},
{
getIPNet("10.0.0.0/24"),
getIPNet("10.0.0.0/24"),
true,
},
{
getIPNet("10.0.0.0/24"),
getIPNet("10.0.0.1/24"),
true,
},
{
getIPNet("10.0.0.0/25"),
getIPNet("10.0.0.0/24"),
false,
},
{
getIPNet("10.0.1.0/24"),
getIPNet("10.0.0.0/24"),
false,
},
}
for _, tc := range testCases {
if tc.expect != IPNetEqual(tc.ipnet1, tc.ipnet2) {
t.Errorf("Expect equality of %s and %s be to %v", tc.ipnet1.String(), tc.ipnet2.String(), tc.expect)
}
}
}

View file

@ -1,101 +0,0 @@
/*
Copyright 2015 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package rand
import (
"math/rand"
"strings"
"testing"
)
const (
maxRangeTestCount = 500
)
func TestString(t *testing.T) {
valid := "0123456789abcdefghijklmnopqrstuvwxyz"
for _, l := range []int{0, 1, 2, 10, 123} {
s := String(l)
if len(s) != l {
t.Errorf("expected string of size %d, got %q", l, s)
}
for _, c := range s {
if !strings.ContainsRune(valid, c) {
t.Errorf("expected valid charaters, got %v", c)
}
}
}
}
// Confirm that panic occurs on invalid input.
func TestRangePanic(t *testing.T) {
defer func() {
if err := recover(); err == nil {
t.Errorf("Panic didn't occur!")
}
}()
// Should result in an error...
Intn(0)
}
func TestIntn(t *testing.T) {
// 0 is invalid.
for _, max := range []int{1, 2, 10, 123} {
inrange := Intn(max)
if inrange < 0 || inrange > max {
t.Errorf("%v out of range (0,%v)", inrange, max)
}
}
}
func TestPerm(t *testing.T) {
Seed(5)
rand.Seed(5)
for i := 1; i < 20; i++ {
actual := Perm(i)
expected := rand.Perm(i)
for j := 0; j < i; j++ {
if actual[j] != expected[j] {
t.Errorf("Perm call result is unexpected")
}
}
}
}
func TestIntnRange(t *testing.T) {
// 0 is invalid.
for min, max := range map[int]int{1: 2, 10: 123, 100: 500} {
for i := 0; i < maxRangeTestCount; i++ {
inrange := IntnRange(min, max)
if inrange < min || inrange >= max {
t.Errorf("%v out of range (%v,%v)", inrange, min, max)
}
}
}
}
func TestInt63nRange(t *testing.T) {
// 0 is invalid.
for min, max := range map[int64]int64{1: 2, 10: 123, 100: 500} {
for i := 0; i < maxRangeTestCount; i++ {
inrange := Int63nRange(min, max)
if inrange < min || inrange >= max {
t.Errorf("%v out of range (%v,%v)", inrange, min, max)
}
}
}
}

View file

@ -1,71 +0,0 @@
/*
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 runtime
import (
"fmt"
"testing"
)
func TestHandleCrash(t *testing.T) {
defer func() {
if x := recover(); x == nil {
t.Errorf("Expected a panic to recover from")
}
}()
defer HandleCrash()
panic("Test Panic")
}
func TestCustomHandleCrash(t *testing.T) {
old := PanicHandlers
defer func() { PanicHandlers = old }()
var result interface{}
PanicHandlers = []func(interface{}){
func(r interface{}) {
result = r
},
}
func() {
defer func() {
if x := recover(); x == nil {
t.Errorf("Expected a panic to recover from")
}
}()
defer HandleCrash()
panic("test")
}()
if result != "test" {
t.Errorf("did not receive custom handler")
}
}
func TestCustomHandleError(t *testing.T) {
old := ErrorHandlers
defer func() { ErrorHandlers = old }()
var result error
ErrorHandlers = []func(error){
func(err error) {
result = err
},
}
err := fmt.Errorf("test")
HandleError(err)
if result != err {
t.Errorf("did not receive custom handler")
}
}

View file

@ -1,270 +0,0 @@
/*
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 sets
import (
"reflect"
"testing"
)
func TestStringSet(t *testing.T) {
s := String{}
s2 := String{}
if len(s) != 0 {
t.Errorf("Expected len=0: %d", len(s))
}
s.Insert("a", "b")
if len(s) != 2 {
t.Errorf("Expected len=2: %d", len(s))
}
s.Insert("c")
if s.Has("d") {
t.Errorf("Unexpected contents: %#v", s)
}
if !s.Has("a") {
t.Errorf("Missing contents: %#v", s)
}
s.Delete("a")
if s.Has("a") {
t.Errorf("Unexpected contents: %#v", s)
}
s.Insert("a")
if s.HasAll("a", "b", "d") {
t.Errorf("Unexpected contents: %#v", s)
}
if !s.HasAll("a", "b") {
t.Errorf("Missing contents: %#v", s)
}
s2.Insert("a", "b", "d")
if s.IsSuperset(s2) {
t.Errorf("Unexpected contents: %#v", s)
}
s2.Delete("d")
if !s.IsSuperset(s2) {
t.Errorf("Missing contents: %#v", s)
}
}
func TestStringSetDeleteMultiples(t *testing.T) {
s := String{}
s.Insert("a", "b", "c")
if len(s) != 3 {
t.Errorf("Expected len=3: %d", len(s))
}
s.Delete("a", "c")
if len(s) != 1 {
t.Errorf("Expected len=1: %d", len(s))
}
if s.Has("a") {
t.Errorf("Unexpected contents: %#v", s)
}
if s.Has("c") {
t.Errorf("Unexpected contents: %#v", s)
}
if !s.Has("b") {
t.Errorf("Missing contents: %#v", s)
}
}
func TestNewStringSet(t *testing.T) {
s := NewString("a", "b", "c")
if len(s) != 3 {
t.Errorf("Expected len=3: %d", len(s))
}
if !s.Has("a") || !s.Has("b") || !s.Has("c") {
t.Errorf("Unexpected contents: %#v", s)
}
}
func TestStringSetList(t *testing.T) {
s := NewString("z", "y", "x", "a")
if !reflect.DeepEqual(s.List(), []string{"a", "x", "y", "z"}) {
t.Errorf("List gave unexpected result: %#v", s.List())
}
}
func TestStringSetDifference(t *testing.T) {
a := NewString("1", "2", "3")
b := NewString("1", "2", "4", "5")
c := a.Difference(b)
d := b.Difference(a)
if len(c) != 1 {
t.Errorf("Expected len=1: %d", len(c))
}
if !c.Has("3") {
t.Errorf("Unexpected contents: %#v", c.List())
}
if len(d) != 2 {
t.Errorf("Expected len=2: %d", len(d))
}
if !d.Has("4") || !d.Has("5") {
t.Errorf("Unexpected contents: %#v", d.List())
}
}
func TestStringSetHasAny(t *testing.T) {
a := NewString("1", "2", "3")
if !a.HasAny("1", "4") {
t.Errorf("expected true, got false")
}
if a.HasAny("0", "4") {
t.Errorf("expected false, got true")
}
}
func TestStringSetEquals(t *testing.T) {
// Simple case (order doesn't matter)
a := NewString("1", "2")
b := NewString("2", "1")
if !a.Equal(b) {
t.Errorf("Expected to be equal: %v vs %v", a, b)
}
// It is a set; duplicates are ignored
b = NewString("2", "2", "1")
if !a.Equal(b) {
t.Errorf("Expected to be equal: %v vs %v", a, b)
}
// Edge cases around empty sets / empty strings
a = NewString()
b = NewString()
if !a.Equal(b) {
t.Errorf("Expected to be equal: %v vs %v", a, b)
}
b = NewString("1", "2", "3")
if a.Equal(b) {
t.Errorf("Expected to be not-equal: %v vs %v", a, b)
}
b = NewString("1", "2", "")
if a.Equal(b) {
t.Errorf("Expected to be not-equal: %v vs %v", a, b)
}
// Check for equality after mutation
a = NewString()
a.Insert("1")
if a.Equal(b) {
t.Errorf("Expected to be not-equal: %v vs %v", a, b)
}
a.Insert("2")
if a.Equal(b) {
t.Errorf("Expected to be not-equal: %v vs %v", a, b)
}
a.Insert("")
if !a.Equal(b) {
t.Errorf("Expected to be equal: %v vs %v", a, b)
}
a.Delete("")
if a.Equal(b) {
t.Errorf("Expected to be not-equal: %v vs %v", a, b)
}
}
func TestStringUnion(t *testing.T) {
tests := []struct {
s1 String
s2 String
expected String
}{
{
NewString("1", "2", "3", "4"),
NewString("3", "4", "5", "6"),
NewString("1", "2", "3", "4", "5", "6"),
},
{
NewString("1", "2", "3", "4"),
NewString(),
NewString("1", "2", "3", "4"),
},
{
NewString(),
NewString("1", "2", "3", "4"),
NewString("1", "2", "3", "4"),
},
{
NewString(),
NewString(),
NewString(),
},
}
for _, test := range tests {
union := test.s1.Union(test.s2)
if union.Len() != test.expected.Len() {
t.Errorf("Expected union.Len()=%d but got %d", test.expected.Len(), union.Len())
}
if !union.Equal(test.expected) {
t.Errorf("Expected union.Equal(expected) but not true. union:%v expected:%v", union.List(), test.expected.List())
}
}
}
func TestStringIntersection(t *testing.T) {
tests := []struct {
s1 String
s2 String
expected String
}{
{
NewString("1", "2", "3", "4"),
NewString("3", "4", "5", "6"),
NewString("3", "4"),
},
{
NewString("1", "2", "3", "4"),
NewString("1", "2", "3", "4"),
NewString("1", "2", "3", "4"),
},
{
NewString("1", "2", "3", "4"),
NewString(),
NewString(),
},
{
NewString(),
NewString("1", "2", "3", "4"),
NewString(),
},
{
NewString(),
NewString(),
NewString(),
},
}
for _, test := range tests {
intersection := test.s1.Intersection(test.s2)
if intersection.Len() != test.expected.Len() {
t.Errorf("Expected intersection.Len()=%d but got %d", test.expected.Len(), intersection.Len())
}
if !intersection.Equal(test.expected) {
t.Errorf("Expected intersection.Equal(expected) but not true. intersection:%v expected:%v", intersection.List(), test.expected.List())
}
}
}

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