mirror of https://github.com/k3s-io/k3s
Merge pull request #3854 from yujuhong/defaults
Migrate API defaulting to a centralized locationpull/6/head
commit
3f28badae3
|
@ -319,6 +319,8 @@ func runSelfLinkTest(c *client.Client) {
|
|||
Selector: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
Protocol: "TCP",
|
||||
SessionAffinity: "None",
|
||||
},
|
||||
},
|
||||
).Do().Into(&svc)
|
||||
|
@ -381,6 +383,8 @@ func runAtomicPutTest(c *client.Client) {
|
|||
Selector: map[string]string{
|
||||
"foo": "bar",
|
||||
},
|
||||
Protocol: "TCP",
|
||||
SessionAffinity: "None",
|
||||
},
|
||||
},
|
||||
).Do().Into(&svc)
|
||||
|
@ -521,8 +525,11 @@ func runServiceTest(client *client.Client) {
|
|||
Ports: []api.Port{
|
||||
{ContainerPort: 1234},
|
||||
},
|
||||
ImagePullPolicy: "PullIfNotPresent",
|
||||
},
|
||||
},
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
Status: api.PodStatus{
|
||||
PodIP: "1.2.3.4",
|
||||
|
@ -541,7 +548,9 @@ func runServiceTest(client *client.Client) {
|
|||
Selector: map[string]string{
|
||||
"name": "thisisalonglabel",
|
||||
},
|
||||
Port: 8080,
|
||||
Port: 8080,
|
||||
Protocol: "TCP",
|
||||
SessionAffinity: "None",
|
||||
},
|
||||
}
|
||||
_, err = client.Services(api.NamespaceDefault).Create(&svc1)
|
||||
|
@ -558,7 +567,9 @@ func runServiceTest(client *client.Client) {
|
|||
Selector: map[string]string{
|
||||
"name": "thisisalonglabel",
|
||||
},
|
||||
Port: 8080,
|
||||
Port: 8080,
|
||||
Protocol: "TCP",
|
||||
SessionAffinity: "None",
|
||||
},
|
||||
}
|
||||
_, err = client.Services(api.NamespaceDefault).Create(&svc2)
|
||||
|
|
|
@ -47,14 +47,6 @@ func init() {
|
|||
out.Spec.DNSPolicy = in.DNSPolicy
|
||||
out.Name = in.ID
|
||||
out.UID = in.UUID
|
||||
// TODO(dchen1107): Move this conversion to pkg/api/v1beta[123]/conversion.go
|
||||
// along with fixing #1502
|
||||
for i := range out.Spec.Containers {
|
||||
ctr := &out.Spec.Containers[i]
|
||||
if len(ctr.TerminationMessagePath) == 0 {
|
||||
ctr.TerminationMessagePath = TerminationMessagePathDefault
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
func(in *BoundPod, out *ContainerManifest, s conversion.Scope) error {
|
||||
|
@ -65,12 +57,6 @@ func init() {
|
|||
out.Version = "v1beta2"
|
||||
out.ID = in.Name
|
||||
out.UUID = in.UID
|
||||
for i := range out.Containers {
|
||||
ctr := &out.Containers[i]
|
||||
if len(ctr.TerminationMessagePath) == 0 {
|
||||
ctr.TerminationMessagePath = TerminationMessagePathDefault
|
||||
}
|
||||
}
|
||||
return nil
|
||||
},
|
||||
|
||||
|
|
|
@ -158,6 +158,10 @@ func TestEncode_Ptr(t *testing.T) {
|
|||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: map[string]string{"name": "foo"},
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
}
|
||||
obj := runtime.Object(pod)
|
||||
data, err := latest.Codec.Encode(obj)
|
||||
|
@ -169,7 +173,7 @@ func TestEncode_Ptr(t *testing.T) {
|
|||
t.Fatalf("Got wrong type")
|
||||
}
|
||||
if !api.Semantic.DeepEqual(obj2, pod) {
|
||||
t.Errorf("Expected:\n %#v,\n Got:\n %#v", &pod, obj2)
|
||||
t.Errorf("Expected:\n %#v,\n Got:\n %#v", pod, obj2)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -32,6 +32,14 @@ import (
|
|||
"speter.net/go/exp/math/dec/inf"
|
||||
)
|
||||
|
||||
func fuzzOneOf(c fuzz.Continue, objs ...interface{}) {
|
||||
// Use a new fuzzer which cannot populate nil to ensure one obj will be set.
|
||||
// FIXME: would be nicer to use FuzzOnePtr() and reflect.
|
||||
f := fuzz.New().NilChance(0).NumElements(1, 1)
|
||||
i := c.RandUint64() % uint64(len(objs))
|
||||
f.Fuzz(objs[i])
|
||||
}
|
||||
|
||||
// FuzzerFor can randomly populate api objects that are destined for version.
|
||||
func FuzzerFor(t *testing.T, version string, src rand.Source) *fuzz.Fuzzer {
|
||||
f := fuzz.New().NilChance(.5).NumElements(1, 1)
|
||||
|
@ -84,15 +92,18 @@ func FuzzerFor(t *testing.T, version string, src rand.Source) *fuzz.Fuzzer {
|
|||
statuses := []api.PodPhase{api.PodPending, api.PodRunning, api.PodFailed, api.PodUnknown}
|
||||
*j = statuses[c.Rand.Intn(len(statuses))]
|
||||
},
|
||||
func(j *api.PodTemplateSpec, c fuzz.Continue) {
|
||||
// TODO: v1beta1/2 can't round trip a nil template correctly, fix by having v1beta1/2
|
||||
// conversion compare converted object to nil via DeepEqual
|
||||
j.ObjectMeta = api.ObjectMeta{}
|
||||
c.Fuzz(&j.ObjectMeta)
|
||||
j.ObjectMeta = api.ObjectMeta{Labels: j.ObjectMeta.Labels}
|
||||
j.Spec = api.PodSpec{}
|
||||
c.Fuzz(&j.Spec)
|
||||
},
|
||||
func(j *api.ReplicationControllerSpec, c fuzz.Continue) {
|
||||
// TemplateRef must be nil for round trip
|
||||
// TemplateRef is set to nil by omission; this is required for round trip
|
||||
c.Fuzz(&j.Template)
|
||||
if j.Template == nil {
|
||||
// TODO: v1beta1/2 can't round trip a nil template correctly, fix by having v1beta1/2
|
||||
// conversion compare converted object to nil via DeepEqual
|
||||
j.Template = &api.PodTemplateSpec{}
|
||||
}
|
||||
j.Template.ObjectMeta = api.ObjectMeta{Labels: j.Template.ObjectMeta.Labels}
|
||||
c.Fuzz(&j.Selector)
|
||||
j.Replicas = int(c.RandUint64())
|
||||
},
|
||||
|
@ -160,6 +171,46 @@ func FuzzerFor(t *testing.T, version string, src rand.Source) *fuzz.Fuzzer {
|
|||
policies := []api.PullPolicy{api.PullAlways, api.PullNever, api.PullIfNotPresent}
|
||||
*p = policies[c.Rand.Intn(len(policies))]
|
||||
},
|
||||
func(rp *api.RestartPolicy, c fuzz.Continue) {
|
||||
// Exactly one of the fields should be set.
|
||||
fuzzOneOf(c, &rp.Always, &rp.OnFailure, &rp.Never)
|
||||
},
|
||||
func(vs *api.VolumeSource, c fuzz.Continue) {
|
||||
// Exactly one of the fields should be set.
|
||||
//FIXME: the fuzz can still end up nil. What if fuzz allowed me to say that?
|
||||
fuzzOneOf(c, &vs.HostPath, &vs.EmptyDir, &vs.GCEPersistentDisk, &vs.GitRepo)
|
||||
},
|
||||
func(d *api.DNSPolicy, c fuzz.Continue) {
|
||||
policies := []api.DNSPolicy{api.DNSClusterFirst, api.DNSDefault}
|
||||
*d = policies[c.Rand.Intn(len(policies))]
|
||||
},
|
||||
func(p *api.Protocol, c fuzz.Continue) {
|
||||
protocols := []api.Protocol{api.ProtocolTCP, api.ProtocolUDP}
|
||||
*p = protocols[c.Rand.Intn(len(protocols))]
|
||||
},
|
||||
func(p *api.AffinityType, c fuzz.Continue) {
|
||||
types := []api.AffinityType{api.AffinityTypeClientIP, api.AffinityTypeNone}
|
||||
*p = types[c.Rand.Intn(len(types))]
|
||||
},
|
||||
func(ct *api.Container, c fuzz.Continue) {
|
||||
// This function exists soley to set TerminationMessagePath to a
|
||||
// non-empty string. TODO: consider making TerminationMessagePath a
|
||||
// new type to simplify fuzzing.
|
||||
ct.TerminationMessagePath = api.TerminationMessagePathDefault
|
||||
// Let fuzzer handle the rest of the fileds.
|
||||
c.Fuzz(&ct.Name)
|
||||
c.Fuzz(&ct.Image)
|
||||
c.Fuzz(&ct.Command)
|
||||
c.Fuzz(&ct.Ports)
|
||||
c.Fuzz(&ct.WorkingDir)
|
||||
c.Fuzz(&ct.Env)
|
||||
c.Fuzz(&ct.VolumeMounts)
|
||||
c.Fuzz(&ct.LivenessProbe)
|
||||
c.Fuzz(&ct.Lifecycle)
|
||||
c.Fuzz(&ct.ImagePullPolicy)
|
||||
c.Fuzz(&ct.Privileged)
|
||||
c.Fuzz(&ct.Capabilities)
|
||||
},
|
||||
)
|
||||
return f
|
||||
}
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT 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 v1beta1
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||
)
|
||||
|
||||
func init() {
|
||||
api.Scheme.AddDefaultingFuncs(
|
||||
func(obj *Volume) {
|
||||
if util.AllPtrFieldsNil(&obj.Source) {
|
||||
obj.Source = VolumeSource{
|
||||
EmptyDir: &EmptyDir{},
|
||||
}
|
||||
}
|
||||
},
|
||||
func(obj *Port) {
|
||||
if obj.Protocol == "" {
|
||||
obj.Protocol = ProtocolTCP
|
||||
}
|
||||
},
|
||||
func(obj *Container) {
|
||||
if obj.ImagePullPolicy == "" {
|
||||
// TODO(dchen1107): Move ParseImageName code to pkg/util
|
||||
parts := strings.Split(obj.Image, ":")
|
||||
// Check image tag
|
||||
if parts[len(parts)-1] == "latest" {
|
||||
obj.ImagePullPolicy = PullAlways
|
||||
} else {
|
||||
obj.ImagePullPolicy = PullIfNotPresent
|
||||
}
|
||||
}
|
||||
if obj.TerminationMessagePath == "" {
|
||||
obj.TerminationMessagePath = TerminationMessagePathDefault
|
||||
}
|
||||
},
|
||||
func(obj *RestartPolicy) {
|
||||
if util.AllPtrFieldsNil(obj) {
|
||||
obj.Always = &RestartPolicyAlways{}
|
||||
}
|
||||
},
|
||||
func(obj *Service) {
|
||||
if obj.Protocol == "" {
|
||||
obj.Protocol = ProtocolTCP
|
||||
}
|
||||
if obj.SessionAffinity == "" {
|
||||
obj.SessionAffinity = AffinityTypeNone
|
||||
}
|
||||
},
|
||||
func(obj *PodSpec) {
|
||||
if obj.DNSPolicy == "" {
|
||||
obj.DNSPolicy = DNSClusterFirst
|
||||
}
|
||||
},
|
||||
func(obj *ContainerManifest) {
|
||||
if obj.DNSPolicy == "" {
|
||||
obj.DNSPolicy = DNSClusterFirst
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
Copyright 2015 Google Inc. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT 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 v1beta1_test
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
current "github.com/GoogleCloudPlatform/kubernetes/pkg/api/v1beta1"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||
)
|
||||
|
||||
func roundTrip(t *testing.T, obj runtime.Object) runtime.Object {
|
||||
data, err := current.Codec.Encode(obj)
|
||||
if err != nil {
|
||||
t.Errorf("%v\n %#v", err, obj)
|
||||
return nil
|
||||
}
|
||||
obj2 := reflect.New(reflect.TypeOf(obj).Elem()).Interface().(runtime.Object)
|
||||
err = current.Codec.DecodeInto(data, obj2)
|
||||
if err != nil {
|
||||
t.Errorf("%v\nData: %s\nSource: %#v", err, string(data), obj)
|
||||
return nil
|
||||
}
|
||||
return obj2
|
||||
}
|
||||
|
||||
func TestSetDefaultService(t *testing.T) {
|
||||
svc := ¤t.Service{}
|
||||
obj2 := roundTrip(t, runtime.Object(svc))
|
||||
svc2 := obj2.(*current.Service)
|
||||
if svc2.Protocol != current.ProtocolTCP {
|
||||
t.Errorf("Expected default protocol :%s, got: %s", current.ProtocolTCP, svc2.Protocol)
|
||||
}
|
||||
if svc2.SessionAffinity != current.AffinityTypeNone {
|
||||
t.Errorf("Expected default sesseion affinity type:%s, got: %s", current.AffinityTypeNone, svc2.SessionAffinity)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSetDefaulPodSpec(t *testing.T) {
|
||||
bp := ¤t.BoundPod{}
|
||||
obj2 := roundTrip(t, runtime.Object(bp))
|
||||
bp2 := obj2.(*current.BoundPod)
|
||||
if bp2.Spec.DNSPolicy != current.DNSClusterFirst {
|
||||
t.Errorf("Expected default dns policy :%s, got: %s", current.DNSClusterFirst, bp2.Spec.DNSPolicy)
|
||||
}
|
||||
policy := bp2.Spec.RestartPolicy
|
||||
if policy.Never != nil || policy.OnFailure != nil || policy.Always == nil {
|
||||
t.Errorf("Expected only policy.Aways is set, got: %s", policy)
|
||||
}
|
||||
}
|
|
@ -71,6 +71,11 @@ type ContainerManifestList struct {
|
|||
Items []ContainerManifest `json:"items" description:"list of pod container manifests"`
|
||||
}
|
||||
|
||||
const (
|
||||
// TerminationMessagePathDefault means the default path to capture the application termination message running in a container
|
||||
TerminationMessagePathDefault string = "/dev/termination-log"
|
||||
)
|
||||
|
||||
// Volume represents a named volume in a pod that may be accessed by any containers in the pod.
|
||||
type Volume struct {
|
||||
// Required: This must be a DNS_LABEL. Each volume in a pod must have
|
||||
|
|
|
@ -0,0 +1,79 @@
|
|||
/*
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT 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 v1beta2
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||
)
|
||||
|
||||
func init() {
|
||||
api.Scheme.AddDefaultingFuncs(
|
||||
func(obj *Volume) {
|
||||
if util.AllPtrFieldsNil(&obj.Source) {
|
||||
obj.Source = VolumeSource{
|
||||
EmptyDir: &EmptyDir{},
|
||||
}
|
||||
}
|
||||
},
|
||||
func(obj *Port) {
|
||||
if obj.Protocol == "" {
|
||||
obj.Protocol = ProtocolTCP
|
||||
}
|
||||
},
|
||||
func(obj *Container) {
|
||||
if obj.ImagePullPolicy == "" {
|
||||
// TODO(dchen1107): Move ParseImageName code to pkg/util
|
||||
parts := strings.Split(obj.Image, ":")
|
||||
// Check image tag
|
||||
if parts[len(parts)-1] == "latest" {
|
||||
obj.ImagePullPolicy = PullAlways
|
||||
} else {
|
||||
obj.ImagePullPolicy = PullIfNotPresent
|
||||
}
|
||||
}
|
||||
if obj.TerminationMessagePath == "" {
|
||||
obj.TerminationMessagePath = TerminationMessagePathDefault
|
||||
}
|
||||
},
|
||||
func(obj *RestartPolicy) {
|
||||
if util.AllPtrFieldsNil(obj) {
|
||||
obj.Always = &RestartPolicyAlways{}
|
||||
}
|
||||
},
|
||||
func(obj *Service) {
|
||||
if obj.Protocol == "" {
|
||||
obj.Protocol = ProtocolTCP
|
||||
}
|
||||
if obj.SessionAffinity == "" {
|
||||
obj.SessionAffinity = AffinityTypeNone
|
||||
}
|
||||
},
|
||||
func(obj *PodSpec) {
|
||||
if obj.DNSPolicy == "" {
|
||||
obj.DNSPolicy = DNSClusterFirst
|
||||
}
|
||||
},
|
||||
func(obj *ContainerManifest) {
|
||||
if obj.DNSPolicy == "" {
|
||||
obj.DNSPolicy = DNSClusterFirst
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
|
@ -252,6 +252,11 @@ type Container struct {
|
|||
Capabilities Capabilities `json:"capabilities,omitempty" description:"capabilities for container"`
|
||||
}
|
||||
|
||||
const (
|
||||
// TerminationMessagePathDefault means the default path to capture the application termination message running in a container
|
||||
TerminationMessagePathDefault string = "/dev/termination-log"
|
||||
)
|
||||
|
||||
// Handler defines a specific action that should be taken
|
||||
// TODO: pass structured data to these actions, and document that data here.
|
||||
type Handler struct {
|
||||
|
|
|
@ -0,0 +1,74 @@
|
|||
/*
|
||||
Copyright 2014 Google Inc. All rights reserved.
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT 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 v1beta3
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||
)
|
||||
|
||||
func init() {
|
||||
api.Scheme.AddDefaultingFuncs(
|
||||
func(obj *Volume) {
|
||||
if util.AllPtrFieldsNil(&obj.Source) {
|
||||
obj.Source = VolumeSource{
|
||||
EmptyDir: &EmptyDir{},
|
||||
}
|
||||
}
|
||||
},
|
||||
func(obj *Port) {
|
||||
if obj.Protocol == "" {
|
||||
obj.Protocol = ProtocolTCP
|
||||
}
|
||||
},
|
||||
func(obj *Container) {
|
||||
if obj.ImagePullPolicy == "" {
|
||||
// TODO(dchen1107): Move ParseImageName code to pkg/util
|
||||
parts := strings.Split(obj.Image, ":")
|
||||
// Check image tag
|
||||
if parts[len(parts)-1] == "latest" {
|
||||
obj.ImagePullPolicy = PullAlways
|
||||
} else {
|
||||
obj.ImagePullPolicy = PullIfNotPresent
|
||||
}
|
||||
}
|
||||
if obj.TerminationMessagePath == "" {
|
||||
obj.TerminationMessagePath = TerminationMessagePathDefault
|
||||
}
|
||||
},
|
||||
func(obj *RestartPolicy) {
|
||||
if util.AllPtrFieldsNil(obj) {
|
||||
obj.Always = &RestartPolicyAlways{}
|
||||
}
|
||||
},
|
||||
func(obj *Service) {
|
||||
if obj.Spec.Protocol == "" {
|
||||
obj.Spec.Protocol = ProtocolTCP
|
||||
}
|
||||
if obj.Spec.SessionAffinity == "" {
|
||||
obj.Spec.SessionAffinity = AffinityTypeNone
|
||||
}
|
||||
},
|
||||
func(obj *PodSpec) {
|
||||
if obj.DNSPolicy == "" {
|
||||
obj.DNSPolicy = DNSClusterFirst
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
|
@ -341,6 +341,11 @@ type ResourceRequirementSpec struct {
|
|||
Limits ResourceList `json:"limits,omitempty" description:"Maximum amount of compute resources allowed"`
|
||||
}
|
||||
|
||||
const (
|
||||
// TerminationMessagePathDefault means the default path to capture the application termination message running in a container
|
||||
TerminationMessagePathDefault string = "/dev/termination-log"
|
||||
)
|
||||
|
||||
// Container represents a single container that is expected to be run on the host.
|
||||
type Container struct {
|
||||
// Required: This must be a DNS_LABEL. Each container in a pod must
|
||||
|
|
|
@ -103,18 +103,17 @@ var invalidPod2 = `{
|
|||
"manifest": {
|
||||
"version": "v1beta1",
|
||||
"id": "apache-php",
|
||||
"containers": [
|
||||
{
|
||||
"name": "apache-php",
|
||||
"image": "php:5.6.2-apache",
|
||||
"ports": [{ "name": "apache", "containerPort": 80, "hostPort":"13380", "protocol":"TCP" }],
|
||||
"volumeMounts": [{"name": "shared-disk","mountPath": "/var/www/html", "readOnly": false}]
|
||||
}
|
||||
]
|
||||
"containers": [{
|
||||
"name": "apache-php",
|
||||
"image": "php:5.6.2-apache",
|
||||
"ports": [{ "name": "apache", "containerPort": 80, "hostPort":"13380", "protocol":"TCP" }],
|
||||
"volumeMounts": [{"name": "shared-disk","mountPath": "/var/www/html", "readOnly": false}]
|
||||
}]
|
||||
}
|
||||
},
|
||||
"labels": { "name": "apache-php" },
|
||||
"restartPolicy": {"always": {}},
|
||||
"dnsPolicy": "ClusterFirst",
|
||||
"volumes": [
|
||||
"name": "shared-disk",
|
||||
"source": {
|
||||
|
@ -134,18 +133,17 @@ var invalidPod3 = `{
|
|||
"manifest": {
|
||||
"version": "v1beta1",
|
||||
"id": "apache-php",
|
||||
"containers": [
|
||||
{
|
||||
"name": "apache-php",
|
||||
"image": "php:5.6.2-apache",
|
||||
"ports": [{ "name": "apache", "containerPort": 80, "hostPort":"13380", "protocol":"TCP" }],
|
||||
"volumeMounts": [{"name": "shared-disk","mountPath": "/var/www/html", "readOnly": false}]
|
||||
}
|
||||
]
|
||||
"containers": [{
|
||||
"name": "apache-php",
|
||||
"image": "php:5.6.2-apache",
|
||||
"ports": [{ "name": "apache", "containerPort": 80, "hostPort":"13380", "protocol":"TCP" }],
|
||||
"volumeMounts": [{"name": "shared-disk","mountPath": "/var/www/html", "readOnly": false}]
|
||||
}]
|
||||
}
|
||||
},
|
||||
"labels": { "name": "apache-php" },
|
||||
"restartPolicy": {"always": {}},
|
||||
"dnsPolicy": "ClusterFirst",
|
||||
"volumes": [
|
||||
{
|
||||
"name": "shared-disk",
|
||||
|
|
|
@ -189,8 +189,7 @@ func validateVolumes(volumes []api.Volume) (util.StringSet, errs.ValidationError
|
|||
allErrs := errs.ValidationErrorList{}
|
||||
|
||||
allNames := util.StringSet{}
|
||||
for i := range volumes {
|
||||
vol := &volumes[i] // so we can set default values
|
||||
for i, vol := range volumes {
|
||||
el := validateSource(&vol.Source).Prefix("source")
|
||||
if len(vol.Name) == 0 {
|
||||
el = append(el, errs.NewFieldRequired("name", vol.Name))
|
||||
|
@ -227,10 +226,7 @@ func validateSource(source *api.VolumeSource) errs.ValidationErrorList {
|
|||
numVolumes++
|
||||
allErrs = append(allErrs, validateGCEPersistentDisk(source.GCEPersistentDisk).Prefix("persistentDisk")...)
|
||||
}
|
||||
if numVolumes == 0 {
|
||||
// TODO: Enforce that a source is set once we deprecate the implied form.
|
||||
source.EmptyDir = &api.EmptyDir{}
|
||||
} else if numVolumes != 1 {
|
||||
if numVolumes != 1 {
|
||||
allErrs = append(allErrs, errs.NewFieldInvalid("", source, "exactly 1 volume type is required"))
|
||||
}
|
||||
return allErrs
|
||||
|
@ -272,9 +268,8 @@ func validatePorts(ports []api.Port) errs.ValidationErrorList {
|
|||
allErrs := errs.ValidationErrorList{}
|
||||
|
||||
allNames := util.StringSet{}
|
||||
for i := range ports {
|
||||
for i, port := range ports {
|
||||
pErrs := errs.ValidationErrorList{}
|
||||
port := &ports[i] // so we can set default values
|
||||
if len(port.Name) > 0 {
|
||||
if len(port.Name) > 63 || !util.IsDNSLabel(port.Name) {
|
||||
pErrs = append(pErrs, errs.NewFieldInvalid("name", port.Name, ""))
|
||||
|
@ -293,7 +288,7 @@ func validatePorts(ports []api.Port) errs.ValidationErrorList {
|
|||
pErrs = append(pErrs, errs.NewFieldInvalid("hostPort", port.HostPort, ""))
|
||||
}
|
||||
if len(port.Protocol) == 0 {
|
||||
port.Protocol = "TCP"
|
||||
pErrs = append(pErrs, errs.NewFieldRequired("protocol", port.Protocol))
|
||||
} else if !supportedPortProtocols.Has(strings.ToUpper(string(port.Protocol))) {
|
||||
pErrs = append(pErrs, errs.NewFieldNotSupported("protocol", port.Protocol))
|
||||
}
|
||||
|
@ -305,9 +300,8 @@ func validatePorts(ports []api.Port) errs.ValidationErrorList {
|
|||
func validateEnv(vars []api.EnvVar) errs.ValidationErrorList {
|
||||
allErrs := errs.ValidationErrorList{}
|
||||
|
||||
for i := range vars {
|
||||
for i, ev := range vars {
|
||||
vErrs := errs.ValidationErrorList{}
|
||||
ev := &vars[i] // so we can set default values
|
||||
if len(ev.Name) == 0 {
|
||||
vErrs = append(vErrs, errs.NewFieldRequired("name", ev.Name))
|
||||
}
|
||||
|
@ -322,9 +316,8 @@ func validateEnv(vars []api.EnvVar) errs.ValidationErrorList {
|
|||
func validateVolumeMounts(mounts []api.VolumeMount, volumes util.StringSet) errs.ValidationErrorList {
|
||||
allErrs := errs.ValidationErrorList{}
|
||||
|
||||
for i := range mounts {
|
||||
for i, mnt := range mounts {
|
||||
mErrs := errs.ValidationErrorList{}
|
||||
mnt := &mounts[i] // so we can set default values
|
||||
if len(mnt.Name) == 0 {
|
||||
mErrs = append(mErrs, errs.NewFieldRequired("name", mnt.Name))
|
||||
} else if !volumes.Has(mnt.Name) {
|
||||
|
@ -343,9 +336,8 @@ func validateVolumeMounts(mounts []api.VolumeMount, volumes util.StringSet) errs
|
|||
func AccumulateUniquePorts(containers []api.Container, accumulator map[int]bool, extract func(*api.Port) int) errs.ValidationErrorList {
|
||||
allErrs := errs.ValidationErrorList{}
|
||||
|
||||
for ci := range containers {
|
||||
for ci, ctr := range containers {
|
||||
cErrs := errs.ValidationErrorList{}
|
||||
ctr := &containers[ci]
|
||||
for pi := range ctr.Ports {
|
||||
port := extract(&ctr.Ports[pi])
|
||||
if port == 0 {
|
||||
|
@ -413,22 +405,14 @@ func validateLifecycle(lifecycle *api.Lifecycle) errs.ValidationErrorList {
|
|||
return allErrs
|
||||
}
|
||||
|
||||
// TODO(dchen1107): Move this along with other defaulting values
|
||||
func validatePullPolicyWithDefault(ctr *api.Container) errs.ValidationErrorList {
|
||||
func validatePullPolicy(ctr *api.Container) errs.ValidationErrorList {
|
||||
allErrors := errs.ValidationErrorList{}
|
||||
|
||||
switch ctr.ImagePullPolicy {
|
||||
case "":
|
||||
// TODO(dchen1107): Move ParseImageName code to pkg/util
|
||||
parts := strings.Split(ctr.Image, ":")
|
||||
// Check image tag
|
||||
if parts[len(parts)-1] == "latest" {
|
||||
ctr.ImagePullPolicy = api.PullAlways
|
||||
} else {
|
||||
ctr.ImagePullPolicy = api.PullIfNotPresent
|
||||
}
|
||||
case api.PullAlways, api.PullIfNotPresent, api.PullNever:
|
||||
break
|
||||
case "":
|
||||
allErrors = append(allErrors, errs.NewFieldRequired("", ctr.ImagePullPolicy))
|
||||
default:
|
||||
allErrors = append(allErrors, errs.NewFieldNotSupported("", ctr.ImagePullPolicy))
|
||||
}
|
||||
|
@ -440,9 +424,8 @@ func validateContainers(containers []api.Container, volumes util.StringSet) errs
|
|||
allErrs := errs.ValidationErrorList{}
|
||||
|
||||
allNames := util.StringSet{}
|
||||
for i := range containers {
|
||||
for i, ctr := range containers {
|
||||
cErrs := errs.ValidationErrorList{}
|
||||
ctr := &containers[i] // so we can set default values
|
||||
capabilities := capabilities.Get()
|
||||
if len(ctr.Name) == 0 {
|
||||
cErrs = append(cErrs, errs.NewFieldRequired("name", ctr.Name))
|
||||
|
@ -464,8 +447,8 @@ func validateContainers(containers []api.Container, volumes util.StringSet) errs
|
|||
cErrs = append(cErrs, validatePorts(ctr.Ports).Prefix("ports")...)
|
||||
cErrs = append(cErrs, validateEnv(ctr.Env).Prefix("env")...)
|
||||
cErrs = append(cErrs, validateVolumeMounts(ctr.VolumeMounts, volumes).Prefix("volumeMounts")...)
|
||||
cErrs = append(cErrs, validatePullPolicyWithDefault(ctr).Prefix("pullPolicy")...)
|
||||
cErrs = append(cErrs, validateResourceRequirements(ctr).Prefix("resources")...)
|
||||
cErrs = append(cErrs, validatePullPolicy(&ctr).Prefix("pullPolicy")...)
|
||||
cErrs = append(cErrs, validateResourceRequirements(&ctr).Prefix("resources")...)
|
||||
allErrs = append(allErrs, cErrs.PrefixIndex(i)...)
|
||||
}
|
||||
// Check for colliding ports across all containers.
|
||||
|
@ -513,10 +496,7 @@ func validateRestartPolicy(restartPolicy *api.RestartPolicy) errs.ValidationErro
|
|||
if restartPolicy.Never != nil {
|
||||
numPolicies++
|
||||
}
|
||||
if numPolicies == 0 {
|
||||
restartPolicy.Always = &api.RestartPolicyAlways{}
|
||||
}
|
||||
if numPolicies > 1 {
|
||||
if numPolicies != 1 {
|
||||
allErrors = append(allErrors, errs.NewFieldInvalid("", restartPolicy, "only 1 policy is allowed"))
|
||||
}
|
||||
return allErrors
|
||||
|
@ -525,11 +505,10 @@ func validateRestartPolicy(restartPolicy *api.RestartPolicy) errs.ValidationErro
|
|||
func validateDNSPolicy(dnsPolicy *api.DNSPolicy) errs.ValidationErrorList {
|
||||
allErrors := errs.ValidationErrorList{}
|
||||
switch *dnsPolicy {
|
||||
case "":
|
||||
// TODO: move this out to standard defaulting logic, when that is ready.
|
||||
*dnsPolicy = api.DNSClusterFirst // Default value.
|
||||
case api.DNSClusterFirst, api.DNSDefault:
|
||||
break
|
||||
case "":
|
||||
allErrors = append(allErrors, errs.NewFieldRequired("", *dnsPolicy))
|
||||
default:
|
||||
allErrors = append(allErrors, errs.NewFieldNotSupported("", dnsPolicy))
|
||||
}
|
||||
|
@ -598,7 +577,7 @@ func ValidateService(service *api.Service) errs.ValidationErrorList {
|
|||
allErrs = append(allErrs, errs.NewFieldInvalid("spec.port", service.Spec.Port, ""))
|
||||
}
|
||||
if len(service.Spec.Protocol) == 0 {
|
||||
service.Spec.Protocol = "TCP"
|
||||
allErrs = append(allErrs, errs.NewFieldRequired("spec.protocol", service.Spec.Protocol))
|
||||
} else if !supportedPortProtocols.Has(strings.ToUpper(string(service.Spec.Protocol))) {
|
||||
allErrs = append(allErrs, errs.NewFieldNotSupported("spec.protocol", service.Spec.Protocol))
|
||||
}
|
||||
|
@ -608,7 +587,7 @@ func ValidateService(service *api.Service) errs.ValidationErrorList {
|
|||
}
|
||||
|
||||
if service.Spec.SessionAffinity == "" {
|
||||
service.Spec.SessionAffinity = api.AffinityTypeNone
|
||||
allErrs = append(allErrs, errs.NewFieldRequired("spec.sessionAffinity", service.Spec.SessionAffinity))
|
||||
} else if !supportedSessionAffinityType.Has(string(service.Spec.SessionAffinity)) {
|
||||
allErrs = append(allErrs, errs.NewFieldNotSupported("spec.sessionAffinity", service.Spec.SessionAffinity))
|
||||
}
|
||||
|
|
|
@ -137,7 +137,7 @@ func TestValidateAnnotations(t *testing.T) {
|
|||
|
||||
func TestValidateVolumes(t *testing.T) {
|
||||
successCase := []api.Volume{
|
||||
{Name: "abc"},
|
||||
{Name: "abc", Source: api.VolumeSource{HostPath: &api.HostPath{"/mnt/path1"}}},
|
||||
{Name: "123", Source: api.VolumeSource{HostPath: &api.HostPath{"/mnt/path2"}}},
|
||||
{Name: "abc-123", Source: api.VolumeSource{HostPath: &api.HostPath{"/mnt/path3"}}},
|
||||
{Name: "empty", Source: api.VolumeSource{EmptyDir: &api.EmptyDir{}}},
|
||||
|
@ -151,16 +151,16 @@ func TestValidateVolumes(t *testing.T) {
|
|||
if len(names) != 6 || !names.HasAll("abc", "123", "abc-123", "empty", "gcepd", "gitrepo") {
|
||||
t.Errorf("wrong names result: %v", names)
|
||||
}
|
||||
|
||||
emptyVS := api.VolumeSource{EmptyDir: &api.EmptyDir{}}
|
||||
errorCases := map[string]struct {
|
||||
V []api.Volume
|
||||
T errors.ValidationErrorType
|
||||
F string
|
||||
}{
|
||||
"zero-length name": {[]api.Volume{{Name: ""}}, errors.ValidationErrorTypeRequired, "[0].name"},
|
||||
"name > 63 characters": {[]api.Volume{{Name: strings.Repeat("a", 64)}}, errors.ValidationErrorTypeInvalid, "[0].name"},
|
||||
"name not a DNS label": {[]api.Volume{{Name: "a.b.c"}}, errors.ValidationErrorTypeInvalid, "[0].name"},
|
||||
"name not unique": {[]api.Volume{{Name: "abc"}, {Name: "abc"}}, errors.ValidationErrorTypeDuplicate, "[1].name"},
|
||||
"zero-length name": {[]api.Volume{{Name: "", Source: emptyVS}}, errors.ValidationErrorTypeRequired, "[0].name"},
|
||||
"name > 63 characters": {[]api.Volume{{Name: strings.Repeat("a", 64), Source: emptyVS}}, errors.ValidationErrorTypeInvalid, "[0].name"},
|
||||
"name not a DNS label": {[]api.Volume{{Name: "a.b.c", Source: emptyVS}}, errors.ValidationErrorTypeInvalid, "[0].name"},
|
||||
"name not unique": {[]api.Volume{{Name: "abc", Source: emptyVS}, {Name: "abc", Source: emptyVS}}, errors.ValidationErrorTypeDuplicate, "[1].name"},
|
||||
}
|
||||
for k, v := range errorCases {
|
||||
_, errs := validateVolumes(v.V)
|
||||
|
@ -182,42 +182,39 @@ func TestValidateVolumes(t *testing.T) {
|
|||
func TestValidatePorts(t *testing.T) {
|
||||
successCase := []api.Port{
|
||||
{Name: "abc", ContainerPort: 80, HostPort: 80, Protocol: "TCP"},
|
||||
{Name: "123", ContainerPort: 81, HostPort: 81},
|
||||
{Name: "easy", ContainerPort: 82, Protocol: "TCP"},
|
||||
{Name: "as", ContainerPort: 83, Protocol: "UDP"},
|
||||
{Name: "do-re-me", ContainerPort: 84},
|
||||
{Name: "do-re-me", ContainerPort: 84, Protocol: "UDP"},
|
||||
{Name: "baby-you-and-me", ContainerPort: 82, Protocol: "tcp"},
|
||||
{ContainerPort: 85},
|
||||
{ContainerPort: 85, Protocol: "TCP"},
|
||||
}
|
||||
if errs := validatePorts(successCase); len(errs) != 0 {
|
||||
t.Errorf("expected success: %v", errs)
|
||||
}
|
||||
|
||||
nonCanonicalCase := []api.Port{
|
||||
{ContainerPort: 80},
|
||||
{ContainerPort: 80, Protocol: "TCP"},
|
||||
}
|
||||
if errs := validatePorts(nonCanonicalCase); len(errs) != 0 {
|
||||
t.Errorf("expected success: %v", errs)
|
||||
}
|
||||
if nonCanonicalCase[0].HostPort != 0 || nonCanonicalCase[0].Protocol != "TCP" {
|
||||
t.Errorf("expected default values: %+v", nonCanonicalCase[0])
|
||||
}
|
||||
|
||||
errorCases := map[string]struct {
|
||||
P []api.Port
|
||||
T errors.ValidationErrorType
|
||||
F string
|
||||
}{
|
||||
"name > 63 characters": {[]api.Port{{Name: strings.Repeat("a", 64), ContainerPort: 80}}, errors.ValidationErrorTypeInvalid, "[0].name"},
|
||||
"name not a DNS label": {[]api.Port{{Name: "a.b.c", ContainerPort: 80}}, errors.ValidationErrorTypeInvalid, "[0].name"},
|
||||
"name > 63 characters": {[]api.Port{{Name: strings.Repeat("a", 64), ContainerPort: 80, Protocol: "TCP"}}, errors.ValidationErrorTypeInvalid, "[0].name"},
|
||||
"name not a DNS label": {[]api.Port{{Name: "a.b.c", ContainerPort: 80, Protocol: "TCP"}}, errors.ValidationErrorTypeInvalid, "[0].name"},
|
||||
"name not unique": {[]api.Port{
|
||||
{Name: "abc", ContainerPort: 80},
|
||||
{Name: "abc", ContainerPort: 81},
|
||||
{Name: "abc", ContainerPort: 80, Protocol: "TCP"},
|
||||
{Name: "abc", ContainerPort: 81, Protocol: "TCP"},
|
||||
}, errors.ValidationErrorTypeDuplicate, "[1].name"},
|
||||
"zero container port": {[]api.Port{{ContainerPort: 0}}, errors.ValidationErrorTypeRequired, "[0].containerPort"},
|
||||
"invalid container port": {[]api.Port{{ContainerPort: 65536}}, errors.ValidationErrorTypeInvalid, "[0].containerPort"},
|
||||
"invalid host port": {[]api.Port{{ContainerPort: 80, HostPort: 65536}}, errors.ValidationErrorTypeInvalid, "[0].hostPort"},
|
||||
"zero container port": {[]api.Port{{ContainerPort: 0, Protocol: "TCP"}}, errors.ValidationErrorTypeRequired, "[0].containerPort"},
|
||||
"invalid container port": {[]api.Port{{ContainerPort: 65536, Protocol: "TCP"}}, errors.ValidationErrorTypeInvalid, "[0].containerPort"},
|
||||
"invalid host port": {[]api.Port{{ContainerPort: 80, HostPort: 65536, Protocol: "TCP"}}, errors.ValidationErrorTypeInvalid, "[0].hostPort"},
|
||||
"invalid protocol": {[]api.Port{{ContainerPort: 80, Protocol: "ICMP"}}, errors.ValidationErrorTypeNotSupported, "[0].protocol"},
|
||||
"protocol required": {[]api.Port{{Name: "abc", ContainerPort: 80}}, errors.ValidationErrorTypeRequired, "[0].protocol"}, //yjhong
|
||||
}
|
||||
for k, v := range errorCases {
|
||||
errs := validatePorts(v.P)
|
||||
|
@ -311,14 +308,10 @@ func TestValidatePullPolicy(t *testing.T) {
|
|||
api.Container{Name: "abc-1234", Image: "image", ImagePullPolicy: "Never"},
|
||||
api.PullNever,
|
||||
},
|
||||
"DefaultToNotPresent": {api.Container{Name: "notPresent", Image: "image"}, api.PullIfNotPresent},
|
||||
"DefaultToNotPresent2": {api.Container{Name: "notPresent1", Image: "image:sometag"}, api.PullIfNotPresent},
|
||||
"DefaultToAlways1": {api.Container{Name: "always", Image: "image:latest"}, api.PullAlways},
|
||||
"DefaultToAlways2": {api.Container{Name: "always", Image: "foo.bar.com:5000/my/image:latest"}, api.PullAlways},
|
||||
}
|
||||
for k, v := range testCases {
|
||||
ctr := &v.Container
|
||||
errs := validatePullPolicyWithDefault(ctr)
|
||||
errs := validatePullPolicy(ctr)
|
||||
if len(errs) != 0 {
|
||||
t.Errorf("case[%s] expected success, got %#v", k, errs)
|
||||
}
|
||||
|
@ -343,9 +336,9 @@ func TestValidateContainers(t *testing.T) {
|
|||
})
|
||||
|
||||
successCase := []api.Container{
|
||||
{Name: "abc", Image: "image"},
|
||||
{Name: "123", Image: "image"},
|
||||
{Name: "abc-123", Image: "image"},
|
||||
{Name: "abc", Image: "image", ImagePullPolicy: "IfNotPresent"},
|
||||
{Name: "123", Image: "image", ImagePullPolicy: "IfNotPresent"},
|
||||
{Name: "abc-123", Image: "image", ImagePullPolicy: "IfNotPresent"},
|
||||
{
|
||||
Name: "life-123",
|
||||
Image: "image",
|
||||
|
@ -354,6 +347,7 @@ func TestValidateContainers(t *testing.T) {
|
|||
Exec: &api.ExecAction{Command: []string{"ls", "-l"}},
|
||||
},
|
||||
},
|
||||
ImagePullPolicy: "IfNotPresent",
|
||||
},
|
||||
{
|
||||
Name: "resources-test",
|
||||
|
@ -365,8 +359,9 @@ func TestValidateContainers(t *testing.T) {
|
|||
api.ResourceName("my.org/resource"): resource.MustParse("10m"),
|
||||
},
|
||||
},
|
||||
ImagePullPolicy: "IfNotPresent",
|
||||
},
|
||||
{Name: "abc-1234", Image: "image", Privileged: true},
|
||||
{Name: "abc-1234", Image: "image", Privileged: true, ImagePullPolicy: "IfNotPresent"},
|
||||
}
|
||||
if errs := validateContainers(successCase, volumes); len(errs) != 0 {
|
||||
t.Errorf("expected success: %v", errs)
|
||||
|
@ -467,7 +462,6 @@ func TestValidateContainers(t *testing.T) {
|
|||
|
||||
func TestValidateRestartPolicy(t *testing.T) {
|
||||
successCases := []api.RestartPolicy{
|
||||
{},
|
||||
{Always: &api.RestartPolicyAlways{}},
|
||||
{OnFailure: &api.RestartPolicyOnFailure{}},
|
||||
{Never: &api.RestartPolicyNever{}},
|
||||
|
@ -479,6 +473,7 @@ func TestValidateRestartPolicy(t *testing.T) {
|
|||
}
|
||||
|
||||
errorCases := []api.RestartPolicy{
|
||||
{},
|
||||
{Always: &api.RestartPolicyAlways{}, Never: &api.RestartPolicyNever{}},
|
||||
{Never: &api.RestartPolicyNever{}, OnFailure: &api.RestartPolicyOnFailure{}},
|
||||
}
|
||||
|
@ -487,19 +482,10 @@ func TestValidateRestartPolicy(t *testing.T) {
|
|||
t.Errorf("expected failure for %d", k)
|
||||
}
|
||||
}
|
||||
|
||||
noPolicySpecified := api.RestartPolicy{}
|
||||
errs := validateRestartPolicy(&noPolicySpecified)
|
||||
if len(errs) != 0 {
|
||||
t.Errorf("expected success: %v", errs)
|
||||
}
|
||||
if noPolicySpecified.Always == nil {
|
||||
t.Errorf("expected Always policy specified")
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateDNSPolicy(t *testing.T) {
|
||||
successCases := []api.DNSPolicy{api.DNSClusterFirst, api.DNSDefault, api.DNSPolicy("")}
|
||||
successCases := []api.DNSPolicy{api.DNSClusterFirst, api.DNSDefault, api.DNSPolicy(api.DNSClusterFirst)}
|
||||
for _, policy := range successCases {
|
||||
if errs := validateDNSPolicy(&policy); len(errs) != 0 {
|
||||
t.Errorf("expected success: %v", errs)
|
||||
|
@ -516,9 +502,12 @@ func TestValidateDNSPolicy(t *testing.T) {
|
|||
|
||||
func TestValidateManifest(t *testing.T) {
|
||||
successCases := []api.ContainerManifest{
|
||||
{Version: "v1beta1", ID: "abc"},
|
||||
{Version: "v1beta2", ID: "123"},
|
||||
{Version: "V1BETA1", ID: "abc.123.do-re-mi"},
|
||||
{Version: "v1beta1", ID: "abc", RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst},
|
||||
{Version: "v1beta2", ID: "123", RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst},
|
||||
{Version: "V1BETA1", ID: "abc.123.do-re-mi",
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}}, DNSPolicy: api.DNSClusterFirst},
|
||||
{
|
||||
Version: "v1beta1",
|
||||
ID: "abc",
|
||||
|
@ -537,9 +526,9 @@ func TestValidateManifest(t *testing.T) {
|
|||
},
|
||||
},
|
||||
Ports: []api.Port{
|
||||
{Name: "p1", ContainerPort: 80, HostPort: 8080},
|
||||
{Name: "p2", ContainerPort: 81},
|
||||
{ContainerPort: 82},
|
||||
{Name: "p1", ContainerPort: 80, HostPort: 8080, Protocol: "TCP"},
|
||||
{Name: "p2", ContainerPort: 81, Protocol: "TCP"},
|
||||
{ContainerPort: 82, Protocol: "TCP"},
|
||||
},
|
||||
Env: []api.EnvVar{
|
||||
{Name: "ev1", Value: "val1"},
|
||||
|
@ -550,8 +539,11 @@ func TestValidateManifest(t *testing.T) {
|
|||
{Name: "vol1", MountPath: "/foo"},
|
||||
{Name: "vol1", MountPath: "/bar"},
|
||||
},
|
||||
ImagePullPolicy: "IfNotPresent",
|
||||
},
|
||||
},
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
}
|
||||
for _, manifest := range successCases {
|
||||
|
@ -583,22 +575,23 @@ func TestValidateManifest(t *testing.T) {
|
|||
|
||||
func TestValidatePodSpec(t *testing.T) {
|
||||
successCases := []api.PodSpec{
|
||||
{}, // empty is valid, if not very useful */
|
||||
{ // Populate basic fields, leave defaults for most.
|
||||
Volumes: []api.Volume{{Name: "vol"}},
|
||||
Containers: []api.Container{{Name: "ctr", Image: "image"}},
|
||||
Volumes: []api.Volume{{Name: "vol", Source: api.VolumeSource{EmptyDir: &api.EmptyDir{}}}},
|
||||
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
{ // Populate all fields.
|
||||
Volumes: []api.Volume{
|
||||
{Name: "vol"},
|
||||
{Name: "vol", Source: api.VolumeSource{EmptyDir: &api.EmptyDir{}}},
|
||||
},
|
||||
Containers: []api.Container{{Name: "ctr", Image: "image"}},
|
||||
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
NodeSelector: map[string]string{
|
||||
"key": "value",
|
||||
},
|
||||
Host: "foobar",
|
||||
Host: "foobar",
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
}
|
||||
for i := range successCases {
|
||||
|
@ -623,38 +616,26 @@ func TestValidatePodSpec(t *testing.T) {
|
|||
t.Errorf("expected failure for %q", k)
|
||||
}
|
||||
}
|
||||
|
||||
defaultPod := api.PodSpec{} // all empty fields
|
||||
if errs := ValidatePodSpec(&defaultPod); len(errs) != 0 {
|
||||
t.Errorf("expected success: %v", errs)
|
||||
}
|
||||
if util.AllPtrFieldsNil(defaultPod.RestartPolicy) {
|
||||
t.Errorf("expected a default RestartPolicy")
|
||||
}
|
||||
if defaultPod.DNSPolicy == "" {
|
||||
t.Errorf("expected a default DNSPolicy")
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidatePod(t *testing.T) {
|
||||
successCases := []api.Pod{
|
||||
{ // Mostly empty.
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: "ns"},
|
||||
},
|
||||
{ // Basic fields.
|
||||
ObjectMeta: api.ObjectMeta{Name: "123", Namespace: "ns"},
|
||||
Spec: api.PodSpec{
|
||||
Volumes: []api.Volume{{Name: "vol"}},
|
||||
Containers: []api.Container{{Name: "ctr", Image: "image"}},
|
||||
Volumes: []api.Volume{{Name: "vol", Source: api.VolumeSource{EmptyDir: &api.EmptyDir{}}}},
|
||||
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
{ // Just about everything.
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc.123.do-re-mi", Namespace: "ns"},
|
||||
Spec: api.PodSpec{
|
||||
Volumes: []api.Volume{
|
||||
{Name: "vol"},
|
||||
{Name: "vol", Source: api.VolumeSource{EmptyDir: &api.EmptyDir{}}},
|
||||
},
|
||||
Containers: []api.Container{{Name: "ctr", Image: "image"}},
|
||||
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
NodeSelector: map[string]string{
|
||||
|
@ -896,21 +877,27 @@ func TestValidateBoundPods(t *testing.T) {
|
|||
successCases := []api.BoundPod{
|
||||
{ // Mostly empty.
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc", Namespace: "ns"},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
{ // Basic fields.
|
||||
ObjectMeta: api.ObjectMeta{Name: "123", Namespace: "ns"},
|
||||
Spec: api.PodSpec{
|
||||
Volumes: []api.Volume{{Name: "vol"}},
|
||||
Containers: []api.Container{{Name: "ctr", Image: "image"}},
|
||||
Volumes: []api.Volume{{Name: "vol", Source: api.VolumeSource{EmptyDir: &api.EmptyDir{}}}},
|
||||
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
{ // Just about everything.
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc.123.do-re-mi", Namespace: "ns"},
|
||||
Spec: api.PodSpec{
|
||||
Volumes: []api.Volume{
|
||||
{Name: "vol"},
|
||||
{Name: "vol", Source: api.VolumeSource{EmptyDir: &api.EmptyDir{}}},
|
||||
},
|
||||
Containers: []api.Container{{Name: "ctr", Image: "image"}},
|
||||
Containers: []api.Container{{Name: "ctr", Image: "image", ImagePullPolicy: "IfNotPresent"}},
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
NodeSelector: map[string]string{
|
||||
|
@ -955,20 +942,50 @@ func TestValidateService(t *testing.T) {
|
|||
svc: api.Service{
|
||||
ObjectMeta: api.ObjectMeta{Namespace: api.NamespaceDefault},
|
||||
Spec: api.ServiceSpec{
|
||||
Port: 8675,
|
||||
Selector: map[string]string{"foo": "bar"},
|
||||
Port: 8675,
|
||||
Selector: map[string]string{"foo": "bar"},
|
||||
Protocol: "TCP",
|
||||
SessionAffinity: "None",
|
||||
},
|
||||
},
|
||||
// Should fail because the ID is missing.
|
||||
numErrs: 1,
|
||||
},
|
||||
{
|
||||
name: "missing protocol",
|
||||
svc: api.Service{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc123", Namespace: api.NamespaceDefault},
|
||||
Spec: api.ServiceSpec{
|
||||
Port: 8675,
|
||||
Selector: map[string]string{"foo": "bar"},
|
||||
SessionAffinity: "None",
|
||||
},
|
||||
},
|
||||
// Should fail because protocol is missing.
|
||||
numErrs: 1,
|
||||
},
|
||||
{
|
||||
name: "missing session affinity",
|
||||
svc: api.Service{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc123", Namespace: api.NamespaceDefault},
|
||||
Spec: api.ServiceSpec{
|
||||
Port: 8675,
|
||||
Selector: map[string]string{"foo": "bar"},
|
||||
Protocol: "TCP",
|
||||
},
|
||||
},
|
||||
// Should fail because protocol is missing.
|
||||
numErrs: 1,
|
||||
},
|
||||
{
|
||||
name: "missing namespace",
|
||||
svc: api.Service{
|
||||
ObjectMeta: api.ObjectMeta{Name: "foo"},
|
||||
Spec: api.ServiceSpec{
|
||||
Port: 8675,
|
||||
Selector: map[string]string{"foo": "bar"},
|
||||
Port: 8675,
|
||||
Selector: map[string]string{"foo": "bar"},
|
||||
Protocol: "TCP",
|
||||
SessionAffinity: "None",
|
||||
},
|
||||
},
|
||||
// Should fail because the Namespace is missing.
|
||||
|
@ -979,8 +996,10 @@ func TestValidateService(t *testing.T) {
|
|||
svc: api.Service{
|
||||
ObjectMeta: api.ObjectMeta{Name: "-123abc", Namespace: api.NamespaceDefault},
|
||||
Spec: api.ServiceSpec{
|
||||
Port: 8675,
|
||||
Selector: map[string]string{"foo": "bar"},
|
||||
Port: 8675,
|
||||
Selector: map[string]string{"foo": "bar"},
|
||||
Protocol: "TCP",
|
||||
SessionAffinity: "None",
|
||||
},
|
||||
},
|
||||
// Should fail because the ID is invalid.
|
||||
|
@ -995,8 +1014,10 @@ func TestValidateService(t *testing.T) {
|
|||
Namespace: api.NamespaceDefault,
|
||||
},
|
||||
Spec: api.ServiceSpec{
|
||||
Port: 8675,
|
||||
Selector: map[string]string{"foo": "bar"},
|
||||
Port: 8675,
|
||||
Selector: map[string]string{"foo": "bar"},
|
||||
Protocol: "TCP",
|
||||
SessionAffinity: "None",
|
||||
},
|
||||
},
|
||||
// Should fail because the Base value for generation is invalid
|
||||
|
@ -1011,8 +1032,10 @@ func TestValidateService(t *testing.T) {
|
|||
Namespace: api.NamespaceDefault,
|
||||
},
|
||||
Spec: api.ServiceSpec{
|
||||
Port: 8675,
|
||||
Selector: map[string]string{"foo": "bar"},
|
||||
Port: 8675,
|
||||
Selector: map[string]string{"foo": "bar"},
|
||||
Protocol: "TCP",
|
||||
SessionAffinity: "None",
|
||||
},
|
||||
},
|
||||
// Should fail because the generate name type is invalid.
|
||||
|
@ -1023,7 +1046,9 @@ func TestValidateService(t *testing.T) {
|
|||
svc: api.Service{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc123", Namespace: api.NamespaceDefault},
|
||||
Spec: api.ServiceSpec{
|
||||
Selector: map[string]string{"foo": "bar"},
|
||||
Selector: map[string]string{"foo": "bar"},
|
||||
Protocol: "TCP",
|
||||
SessionAffinity: "None",
|
||||
},
|
||||
},
|
||||
// Should fail because the port number is missing/invalid.
|
||||
|
@ -1034,8 +1059,10 @@ func TestValidateService(t *testing.T) {
|
|||
svc: api.Service{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc123", Namespace: api.NamespaceDefault},
|
||||
Spec: api.ServiceSpec{
|
||||
Port: 66536,
|
||||
Selector: map[string]string{"foo": "bar"},
|
||||
Port: 66536,
|
||||
Selector: map[string]string{"foo": "bar"},
|
||||
Protocol: "TCP",
|
||||
SessionAffinity: "None",
|
||||
},
|
||||
},
|
||||
// Should fail because the port number is invalid.
|
||||
|
@ -1046,9 +1073,10 @@ func TestValidateService(t *testing.T) {
|
|||
svc: api.Service{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc123", Namespace: api.NamespaceDefault},
|
||||
Spec: api.ServiceSpec{
|
||||
Port: 8675,
|
||||
Selector: map[string]string{"foo": "bar"},
|
||||
Protocol: "INVALID",
|
||||
Port: 8675,
|
||||
Selector: map[string]string{"foo": "bar"},
|
||||
Protocol: "INVALID",
|
||||
SessionAffinity: "None",
|
||||
},
|
||||
},
|
||||
// Should fail because the protocol is invalid.
|
||||
|
@ -1059,7 +1087,9 @@ func TestValidateService(t *testing.T) {
|
|||
svc: api.Service{
|
||||
ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: api.NamespaceDefault},
|
||||
Spec: api.ServiceSpec{
|
||||
Port: 8675,
|
||||
Port: 8675,
|
||||
Protocol: "TCP",
|
||||
SessionAffinity: "None",
|
||||
},
|
||||
},
|
||||
// Should be ok because the selector is missing.
|
||||
|
@ -1070,9 +1100,10 @@ func TestValidateService(t *testing.T) {
|
|||
svc: api.Service{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc123", Namespace: api.NamespaceDefault},
|
||||
Spec: api.ServiceSpec{
|
||||
Port: 8675,
|
||||
Selector: map[string]string{"foo": "bar"},
|
||||
Protocol: "TCP",
|
||||
Port: 8675,
|
||||
Selector: map[string]string{"foo": "bar"},
|
||||
Protocol: "TCP",
|
||||
SessionAffinity: "None",
|
||||
},
|
||||
},
|
||||
numErrs: 0,
|
||||
|
@ -1082,9 +1113,10 @@ func TestValidateService(t *testing.T) {
|
|||
svc: api.Service{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc123", Namespace: api.NamespaceDefault},
|
||||
Spec: api.ServiceSpec{
|
||||
Port: 8675,
|
||||
Selector: map[string]string{"foo": "bar"},
|
||||
Protocol: "UDP",
|
||||
Port: 8675,
|
||||
Selector: map[string]string{"foo": "bar"},
|
||||
Protocol: "UDP",
|
||||
SessionAffinity: "None",
|
||||
},
|
||||
},
|
||||
numErrs: 0,
|
||||
|
@ -1094,8 +1126,10 @@ func TestValidateService(t *testing.T) {
|
|||
svc: api.Service{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc123", Namespace: api.NamespaceDefault},
|
||||
Spec: api.ServiceSpec{
|
||||
Port: 8675,
|
||||
Selector: map[string]string{"foo": "bar"},
|
||||
Port: 8675,
|
||||
Selector: map[string]string{"foo": "bar"},
|
||||
Protocol: "UDP",
|
||||
SessionAffinity: "None",
|
||||
},
|
||||
},
|
||||
numErrs: 0,
|
||||
|
@ -1108,13 +1142,15 @@ func TestValidateService(t *testing.T) {
|
|||
Port: 80,
|
||||
CreateExternalLoadBalancer: true,
|
||||
Selector: map[string]string{"foo": "bar"},
|
||||
Protocol: "TCP",
|
||||
SessionAffinity: "None",
|
||||
},
|
||||
},
|
||||
existing: api.ServiceList{
|
||||
Items: []api.Service{
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{Name: "def123", Namespace: api.NamespaceDefault},
|
||||
Spec: api.ServiceSpec{Port: 80, CreateExternalLoadBalancer: true},
|
||||
Spec: api.ServiceSpec{Port: 80, CreateExternalLoadBalancer: true, Protocol: "TCP"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1128,13 +1164,15 @@ func TestValidateService(t *testing.T) {
|
|||
Port: 80,
|
||||
CreateExternalLoadBalancer: true,
|
||||
Selector: map[string]string{"foo": "bar"},
|
||||
Protocol: "TCP",
|
||||
SessionAffinity: "None",
|
||||
},
|
||||
},
|
||||
existing: api.ServiceList{
|
||||
Items: []api.Service{
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{Name: "def123", Namespace: api.NamespaceDefault},
|
||||
Spec: api.ServiceSpec{Port: 80},
|
||||
Spec: api.ServiceSpec{Port: 80, Protocol: "TCP"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1145,15 +1183,17 @@ func TestValidateService(t *testing.T) {
|
|||
svc: api.Service{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc123", Namespace: api.NamespaceDefault},
|
||||
Spec: api.ServiceSpec{
|
||||
Port: 80,
|
||||
Selector: map[string]string{"foo": "bar"},
|
||||
Port: 80,
|
||||
Selector: map[string]string{"foo": "bar"},
|
||||
Protocol: "TCP",
|
||||
SessionAffinity: "None",
|
||||
},
|
||||
},
|
||||
existing: api.ServiceList{
|
||||
Items: []api.Service{
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{Name: "def123", Namespace: api.NamespaceDefault},
|
||||
Spec: api.ServiceSpec{Port: 80, CreateExternalLoadBalancer: true},
|
||||
Spec: api.ServiceSpec{Port: 80, CreateExternalLoadBalancer: true, Protocol: "TCP"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1164,15 +1204,17 @@ func TestValidateService(t *testing.T) {
|
|||
svc: api.Service{
|
||||
ObjectMeta: api.ObjectMeta{Name: "abc123", Namespace: api.NamespaceDefault},
|
||||
Spec: api.ServiceSpec{
|
||||
Port: 80,
|
||||
Selector: map[string]string{"foo": "bar"},
|
||||
Port: 80,
|
||||
Selector: map[string]string{"foo": "bar"},
|
||||
Protocol: "TCP",
|
||||
SessionAffinity: "None",
|
||||
},
|
||||
},
|
||||
existing: api.ServiceList{
|
||||
Items: []api.Service{
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{Name: "def123", Namespace: api.NamespaceDefault},
|
||||
Spec: api.ServiceSpec{Port: 80},
|
||||
Spec: api.ServiceSpec{Port: 80, Protocol: "TCP"},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -1189,7 +1231,9 @@ func TestValidateService(t *testing.T) {
|
|||
},
|
||||
},
|
||||
Spec: api.ServiceSpec{
|
||||
Port: 8675,
|
||||
Port: 8675,
|
||||
Protocol: "TCP",
|
||||
SessionAffinity: "None",
|
||||
},
|
||||
},
|
||||
numErrs: 1,
|
||||
|
@ -1205,7 +1249,9 @@ func TestValidateService(t *testing.T) {
|
|||
},
|
||||
},
|
||||
Spec: api.ServiceSpec{
|
||||
Port: 8675,
|
||||
Port: 8675,
|
||||
Protocol: "TCP",
|
||||
SessionAffinity: "None",
|
||||
},
|
||||
},
|
||||
numErrs: 1,
|
||||
|
@ -1218,8 +1264,10 @@ func TestValidateService(t *testing.T) {
|
|||
Namespace: api.NamespaceDefault,
|
||||
},
|
||||
Spec: api.ServiceSpec{
|
||||
Port: 8675,
|
||||
Selector: map[string]string{"foo": "bar", "NoUppercaseOrSpecialCharsLike=Equals": "bar"},
|
||||
Port: 8675,
|
||||
Selector: map[string]string{"foo": "bar", "NoUppercaseOrSpecialCharsLike=Equals": "bar"},
|
||||
Protocol: "TCP",
|
||||
SessionAffinity: "None",
|
||||
},
|
||||
},
|
||||
numErrs: 1,
|
||||
|
@ -1238,17 +1286,16 @@ func TestValidateService(t *testing.T) {
|
|||
svc := api.Service{
|
||||
ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: api.NamespaceDefault},
|
||||
Spec: api.ServiceSpec{
|
||||
Port: 8675,
|
||||
Selector: map[string]string{"foo": "bar"},
|
||||
Port: 8675,
|
||||
Selector: map[string]string{"foo": "bar"},
|
||||
Protocol: "TCP",
|
||||
SessionAffinity: "None",
|
||||
},
|
||||
}
|
||||
errs := ValidateService(&svc)
|
||||
if len(errs) != 0 {
|
||||
t.Errorf("Unexpected non-zero error list: %#v", errs)
|
||||
}
|
||||
if svc.Spec.Protocol != "TCP" {
|
||||
t.Errorf("Expected default protocol of 'TCP': %#v", errs)
|
||||
}
|
||||
}
|
||||
|
||||
func TestValidateReplicationController(t *testing.T) {
|
||||
|
@ -1258,6 +1305,10 @@ func TestValidateReplicationController(t *testing.T) {
|
|||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: validSelector,
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
}
|
||||
invalidVolumePodTemplate := api.PodTemplate{
|
||||
|
@ -1271,9 +1322,8 @@ func TestValidateReplicationController(t *testing.T) {
|
|||
invalidPodTemplate := api.PodTemplate{
|
||||
Spec: api.PodTemplateSpec{
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicy{
|
||||
Always: &api.RestartPolicyAlways{},
|
||||
},
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: invalidSelector,
|
||||
|
@ -1400,6 +1450,7 @@ func TestValidateReplicationController(t *testing.T) {
|
|||
RestartPolicy: api.RestartPolicy{
|
||||
OnFailure: &api.RestartPolicyOnFailure{},
|
||||
},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: validSelector,
|
||||
|
@ -1419,6 +1470,7 @@ func TestValidateReplicationController(t *testing.T) {
|
|||
RestartPolicy: api.RestartPolicy{
|
||||
Never: &api.RestartPolicyNever{},
|
||||
},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
ObjectMeta: api.ObjectMeta{
|
||||
Labels: validSelector,
|
||||
|
|
|
@ -96,7 +96,7 @@ func (c *testClient) Setup() *testClient {
|
|||
func (c *testClient) Validate(t *testing.T, received runtime.Object, err error) {
|
||||
c.ValidateCommon(t, err)
|
||||
|
||||
if c.Response.Body != nil && !api.Semantic.DeepEqual(c.Response.Body, received) {
|
||||
if c.Response.Body != nil && !api.Semantic.DeepDerivative(c.Response.Body, received) {
|
||||
t.Errorf("bad response for request %#v: expected %#v, got %#v", c.Request, c.Response.Body, received)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,7 +26,7 @@ import (
|
|||
"net/http/httptest"
|
||||
"net/url"
|
||||
"os"
|
||||
"reflect"
|
||||
// "reflect"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
@ -62,7 +62,7 @@ func TestRequestWithErrorWontChange(t *testing.T) {
|
|||
if changed != &r {
|
||||
t.Errorf("returned request should point to the same object")
|
||||
}
|
||||
if !reflect.DeepEqual(&original, changed) {
|
||||
if !api.Semantic.DeepDerivative(changed, &original) {
|
||||
t.Errorf("expected %#v, got %#v", &original, changed)
|
||||
}
|
||||
}
|
||||
|
@ -148,7 +148,7 @@ func TestRequestParseSelectorParam(t *testing.T) {
|
|||
|
||||
func TestRequestParam(t *testing.T) {
|
||||
r := (&Request{}).Param("foo", "a")
|
||||
if !reflect.DeepEqual(map[string]string{"foo": "a"}, r.params) {
|
||||
if !api.Semantic.DeepDerivative(r.params, map[string]string{"foo": "a"}) {
|
||||
t.Errorf("should have set a param: %#v", r)
|
||||
}
|
||||
}
|
||||
|
@ -218,7 +218,7 @@ func TestTransformResponse(t *testing.T) {
|
|||
if hasErr != test.Error {
|
||||
t.Errorf("%d: unexpected error: %t %v", i, test.Error, err)
|
||||
}
|
||||
if !(test.Data == nil && response == nil) && !reflect.DeepEqual(test.Data, response) {
|
||||
if !(test.Data == nil && response == nil) && !api.Semantic.DeepDerivative(test.Data, response) {
|
||||
t.Errorf("%d: unexpected response: %#v %#v", i, test.Data, response)
|
||||
}
|
||||
if test.Created != created {
|
||||
|
@ -491,7 +491,7 @@ func TestDoRequestNewWay(t *testing.T) {
|
|||
}
|
||||
if obj == nil {
|
||||
t.Error("nil obj")
|
||||
} else if !reflect.DeepEqual(obj, expectedObj) {
|
||||
} else if !api.Semantic.DeepDerivative(expectedObj, obj) {
|
||||
t.Errorf("Expected: %#v, got %#v", expectedObj, obj)
|
||||
}
|
||||
fakeHandler.ValidateRequest(t, "/api/v1beta2/foo/bar/baz?labels=name%3Dfoo&timeout=1s", "POST", &reqBody)
|
||||
|
@ -526,7 +526,7 @@ func TestDoRequestNewWayReader(t *testing.T) {
|
|||
}
|
||||
if obj == nil {
|
||||
t.Error("nil obj")
|
||||
} else if !reflect.DeepEqual(obj, expectedObj) {
|
||||
} else if !api.Semantic.DeepDerivative(expectedObj, obj) {
|
||||
t.Errorf("Expected: %#v, got %#v", expectedObj, obj)
|
||||
}
|
||||
tmpStr := string(reqBodyExpected)
|
||||
|
@ -562,7 +562,7 @@ func TestDoRequestNewWayObj(t *testing.T) {
|
|||
}
|
||||
if obj == nil {
|
||||
t.Error("nil obj")
|
||||
} else if !reflect.DeepEqual(obj, expectedObj) {
|
||||
} else if !api.Semantic.DeepDerivative(expectedObj, obj) {
|
||||
t.Errorf("Expected: %#v, got %#v", expectedObj, obj)
|
||||
}
|
||||
tmpStr := string(reqBodyExpected)
|
||||
|
@ -611,7 +611,7 @@ func TestDoRequestNewWayFile(t *testing.T) {
|
|||
}
|
||||
if obj == nil {
|
||||
t.Error("nil obj")
|
||||
} else if !reflect.DeepEqual(obj, expectedObj) {
|
||||
} else if !api.Semantic.DeepDerivative(expectedObj, obj) {
|
||||
t.Errorf("Expected: %#v, got %#v", expectedObj, obj)
|
||||
}
|
||||
if wasCreated {
|
||||
|
@ -653,7 +653,7 @@ func TestWasCreated(t *testing.T) {
|
|||
}
|
||||
if obj == nil {
|
||||
t.Error("nil obj")
|
||||
} else if !reflect.DeepEqual(obj, expectedObj) {
|
||||
} else if !api.Semantic.DeepDerivative(expectedObj, obj) {
|
||||
t.Errorf("Expected: %#v, got %#v", expectedObj, obj)
|
||||
}
|
||||
if !wasCreated {
|
||||
|
@ -790,7 +790,7 @@ func checkAuth(t *testing.T, expect *Config, r *http.Request) {
|
|||
foundAuth, found := authFromReq(r)
|
||||
if !found {
|
||||
t.Errorf("no auth found")
|
||||
} else if e, a := expect, foundAuth; !reflect.DeepEqual(e, a) {
|
||||
} else if e, a := expect, foundAuth; !api.Semantic.DeepDerivative(e, a) {
|
||||
t.Fatalf("Wrong basic auth: wanted %#v, got %#v", e, a)
|
||||
}
|
||||
}
|
||||
|
@ -849,7 +849,7 @@ func TestWatch(t *testing.T) {
|
|||
if e, a := item.t, got.Type; e != a {
|
||||
t.Errorf("Expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := item.obj, got.Object; !reflect.DeepEqual(e, a) {
|
||||
if e, a := item.obj, got.Object; !api.Semantic.DeepDerivative(e, a) {
|
||||
t.Errorf("Expected %v, got %v", e, a)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -239,7 +239,7 @@ func TestCreateReplica(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Errorf("Unexpected error: %#v", err)
|
||||
}
|
||||
if !api.Semantic.DeepEqual(&expectedPod, actualPod) {
|
||||
if !api.Semantic.DeepDerivative(&expectedPod, actualPod) {
|
||||
t.Logf("Body: %s", fakeHandler.RequestBody)
|
||||
t.Errorf("Unexpected mismatch. Expected\n %#v,\n Got:\n %#v", &expectedPod, actualPod)
|
||||
}
|
||||
|
@ -351,7 +351,7 @@ func TestWatchControllers(t *testing.T) {
|
|||
var testControllerSpec api.ReplicationController
|
||||
received := make(chan struct{})
|
||||
manager.syncHandler = func(controllerSpec api.ReplicationController) error {
|
||||
if !api.Semantic.DeepEqual(controllerSpec, testControllerSpec) {
|
||||
if !api.Semantic.DeepDerivative(controllerSpec, testControllerSpec) {
|
||||
t.Errorf("Expected %#v, but got %#v", testControllerSpec, controllerSpec)
|
||||
}
|
||||
close(received)
|
||||
|
|
|
@ -40,7 +40,7 @@ type DebugLogger interface {
|
|||
type Converter struct {
|
||||
// Map from the conversion pair to a function which can
|
||||
// do the conversion.
|
||||
funcs map[typePair]reflect.Value
|
||||
conversionFuncs map[typePair]reflect.Value
|
||||
|
||||
// This is a map from a source field type and name, to a list of destination
|
||||
// field type and name.
|
||||
|
@ -51,20 +51,24 @@ type Converter struct {
|
|||
// source field name and type to look for.
|
||||
structFieldSources map[typeNamePair][]typeNamePair
|
||||
|
||||
// Map from a type to a function which applies defaults.
|
||||
defaultingFuncs map[reflect.Type]reflect.Value
|
||||
|
||||
// If non-nil, will be called to print helpful debugging info. Quite verbose.
|
||||
Debug DebugLogger
|
||||
|
||||
// NameFunc is called to retrieve the name of a type; this name is used for the
|
||||
// nameFunc is called to retrieve the name of a type; this name is used for the
|
||||
// purpose of deciding whether two types match or not (i.e., will we attempt to
|
||||
// do a conversion). The default returns the go type name.
|
||||
NameFunc func(t reflect.Type) string
|
||||
nameFunc func(t reflect.Type) string
|
||||
}
|
||||
|
||||
// NewConverter creates a new Converter object.
|
||||
func NewConverter() *Converter {
|
||||
return &Converter{
|
||||
funcs: map[typePair]reflect.Value{},
|
||||
NameFunc: func(t reflect.Type) string { return t.Name() },
|
||||
conversionFuncs: map[typePair]reflect.Value{},
|
||||
defaultingFuncs: map[reflect.Type]reflect.Value{},
|
||||
nameFunc: func(t reflect.Type) string { return t.Name() },
|
||||
structFieldDests: map[typeNamePair][]typeNamePair{},
|
||||
structFieldSources: map[typeNamePair][]typeNamePair{},
|
||||
}
|
||||
|
@ -210,14 +214,18 @@ func (s *scope) error(message string, args ...interface{}) error {
|
|||
return fmt.Errorf(where+message, args...)
|
||||
}
|
||||
|
||||
// Register registers a conversion func with the Converter. conversionFunc must take
|
||||
// three parameters: a pointer to the input type, a pointer to the output type, and
|
||||
// a conversion.Scope (which should be used if recursive conversion calls are desired).
|
||||
// It must return an error.
|
||||
// RegisterConversionFunc registers a conversion func with the
|
||||
// Converter. conversionFunc must take three parameters: a pointer to the input
|
||||
// type, a pointer to the output type, and a conversion.Scope (which should be
|
||||
// used if recursive conversion calls are desired). It must return an error.
|
||||
//
|
||||
// Example:
|
||||
// c.Register(func(in *Pod, out *v1beta1.Pod, s Scope) error { ... return nil })
|
||||
func (c *Converter) Register(conversionFunc interface{}) error {
|
||||
// c.RegisteConversionFunc(
|
||||
// func(in *Pod, out *v1beta1.Pod, s Scope) error {
|
||||
// // conversion logic...
|
||||
// return nil
|
||||
// })
|
||||
func (c *Converter) RegisterConversionFunc(conversionFunc interface{}) error {
|
||||
fv := reflect.ValueOf(conversionFunc)
|
||||
ft := fv.Type()
|
||||
if ft.Kind() != reflect.Func {
|
||||
|
@ -246,7 +254,7 @@ func (c *Converter) Register(conversionFunc interface{}) error {
|
|||
if ft.Out(0) != errorType {
|
||||
return fmt.Errorf("expected error return, got: %v", ft)
|
||||
}
|
||||
c.funcs[typePair{ft.In(0).Elem(), ft.In(1).Elem()}] = fv
|
||||
c.conversionFuncs[typePair{ft.In(0).Elem(), ft.In(1).Elem()}] = fv
|
||||
return nil
|
||||
}
|
||||
|
||||
|
@ -266,6 +274,33 @@ func (c *Converter) SetStructFieldCopy(srcFieldType interface{}, srcFieldName st
|
|||
return nil
|
||||
}
|
||||
|
||||
// RegisterDefaultingFunc registers a value-defaulting func with the Converter.
|
||||
// defaultingFunc must take one parameters: a pointer to the input type.
|
||||
//
|
||||
// Example:
|
||||
// c.RegisteDefaultingFunc(
|
||||
// func(in *v1beta1.Pod) {
|
||||
// // defaulting logic...
|
||||
// })
|
||||
func (c *Converter) RegisterDefaultingFunc(defaultingFunc interface{}) error {
|
||||
fv := reflect.ValueOf(defaultingFunc)
|
||||
ft := fv.Type()
|
||||
if ft.Kind() != reflect.Func {
|
||||
return fmt.Errorf("expected func, got: %v", ft)
|
||||
}
|
||||
if ft.NumIn() != 1 {
|
||||
return fmt.Errorf("expected one 'in' param, got: %v", ft)
|
||||
}
|
||||
if ft.NumOut() != 0 {
|
||||
return fmt.Errorf("expected zero 'out' params, got: %v", ft)
|
||||
}
|
||||
if ft.In(0).Kind() != reflect.Ptr {
|
||||
return fmt.Errorf("expected pointer arg for 'in' param 0, got: %v", ft)
|
||||
}
|
||||
c.defaultingFuncs[ft.In(0).Elem()] = fv
|
||||
return nil
|
||||
}
|
||||
|
||||
// FieldMatchingFlags contains a list of ways in which struct fields could be
|
||||
// copied. These constants may be | combined.
|
||||
type FieldMatchingFlags int
|
||||
|
@ -379,7 +414,17 @@ func (c *Converter) callCustom(sv, dv, custom reflect.Value, scope *scope) error
|
|||
// one is registered.
|
||||
func (c *Converter) convert(sv, dv reflect.Value, scope *scope) error {
|
||||
dt, st := dv.Type(), sv.Type()
|
||||
if fv, ok := c.funcs[typePair{st, dt}]; ok {
|
||||
// Apply default values.
|
||||
if fv, ok := c.defaultingFuncs[st]; ok {
|
||||
if c.Debug != nil {
|
||||
c.Debug.Logf("Applying defaults for '%v'", st)
|
||||
}
|
||||
args := []reflect.Value{sv.Addr()}
|
||||
fv.Call(args)
|
||||
}
|
||||
|
||||
// Convert sv to dv.
|
||||
if fv, ok := c.conversionFuncs[typePair{st, dt}]; ok {
|
||||
if c.Debug != nil {
|
||||
c.Debug.Logf("Calling custom conversion of '%v' to '%v'", st, dt)
|
||||
}
|
||||
|
@ -398,10 +443,10 @@ func (c *Converter) defaultConvert(sv, dv reflect.Value, scope *scope) error {
|
|||
return scope.error("Cannot set dest. (Tried to deep copy something with unexported fields?)")
|
||||
}
|
||||
|
||||
if !scope.flags.IsSet(AllowDifferentFieldTypeNames) && c.NameFunc(dt) != c.NameFunc(st) {
|
||||
if !scope.flags.IsSet(AllowDifferentFieldTypeNames) && c.nameFunc(dt) != c.nameFunc(st) {
|
||||
return scope.error(
|
||||
"type names don't match (%v, %v), and no conversion 'func (%v, %v) error' registered.",
|
||||
c.NameFunc(st), c.NameFunc(dt), st, dt)
|
||||
c.nameFunc(st), c.nameFunc(dt), st, dt)
|
||||
}
|
||||
|
||||
switch st.Kind() {
|
||||
|
@ -498,6 +543,7 @@ func toKVValue(v reflect.Value) kvValue {
|
|||
case reflect.Struct:
|
||||
return structAdaptor(v)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -36,11 +36,11 @@ func TestConverter_DefaultConvert(t *testing.T) {
|
|||
}
|
||||
c := NewConverter()
|
||||
c.Debug = t
|
||||
c.NameFunc = func(t reflect.Type) string { return "MyType" }
|
||||
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.Register(func(in *A, out *B, s Scope) error {
|
||||
err := c.RegisterConversionFunc(func(in *A, out *B, s Scope) error {
|
||||
if err := s.DefaultConvert(in, out, IgnoreMissingFields); err != nil {
|
||||
return err
|
||||
}
|
||||
|
@ -118,14 +118,14 @@ func TestConverter_CallsRegisteredFunctions(t *testing.T) {
|
|||
type C struct{}
|
||||
c := NewConverter()
|
||||
c.Debug = t
|
||||
err := c.Register(func(in *A, out *B, s Scope) error {
|
||||
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.Register(func(in *B, out *A, s Scope) error {
|
||||
err = c.RegisterConversionFunc(func(in *B, out *A, s Scope) error {
|
||||
out.Foo = in.Bar
|
||||
return s.Convert(&in.Baz, &out.Baz, 0)
|
||||
})
|
||||
|
@ -161,7 +161,7 @@ func TestConverter_CallsRegisteredFunctions(t *testing.T) {
|
|||
t.Errorf("expected %v, got %v", e, a)
|
||||
}
|
||||
|
||||
err = c.Register(func(in *A, out *C, s Scope) error {
|
||||
err = c.RegisterConversionFunc(func(in *A, out *C, s Scope) error {
|
||||
return fmt.Errorf("C can't store an A, silly")
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -184,7 +184,7 @@ func TestConverter_fuzz(t *testing.T) {
|
|||
|
||||
f := fuzz.New().NilChance(.5).NumElements(0, 100)
|
||||
c := NewConverter()
|
||||
c.NameFunc = func(t reflect.Type) string {
|
||||
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",
|
||||
|
@ -224,7 +224,7 @@ func TestConverter_MapElemAddr(t *testing.T) {
|
|||
}
|
||||
c := NewConverter()
|
||||
c.Debug = t
|
||||
err := c.Register(
|
||||
err := c.RegisterConversionFunc(
|
||||
func(in *int, out *string, s Scope) error {
|
||||
*out = fmt.Sprintf("%v", *in)
|
||||
return nil
|
||||
|
@ -233,7 +233,7 @@ func TestConverter_MapElemAddr(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
err = c.Register(
|
||||
err = c.RegisterConversionFunc(
|
||||
func(in *string, out *int, s Scope) error {
|
||||
if str, err := strconv.Atoi(*in); err != nil {
|
||||
return err
|
||||
|
@ -270,7 +270,7 @@ func TestConverter_tags(t *testing.T) {
|
|||
}
|
||||
c := NewConverter()
|
||||
c.Debug = t
|
||||
err := c.Register(
|
||||
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)
|
||||
|
@ -296,7 +296,7 @@ func TestConverter_meta(t *testing.T) {
|
|||
c := NewConverter()
|
||||
c.Debug = t
|
||||
checks := 0
|
||||
err := c.Register(
|
||||
err := c.RegisterConversionFunc(
|
||||
func(in *Foo, out *Bar, s Scope) error {
|
||||
if s.Meta() == nil || s.Meta().SrcVersion != "test" || s.Meta().DestVersion != "passes" {
|
||||
t.Errorf("Meta did not get passed!")
|
||||
|
@ -309,7 +309,7 @@ func TestConverter_meta(t *testing.T) {
|
|||
if err != nil {
|
||||
t.Fatalf("Unexpected error: %v", err)
|
||||
}
|
||||
err = c.Register(
|
||||
err = c.RegisterConversionFunc(
|
||||
func(in *string, out *string, s Scope) error {
|
||||
if s.Meta() == nil || s.Meta().SrcVersion != "test" || s.Meta().DestVersion != "passes" {
|
||||
t.Errorf("Meta did not get passed a second time!")
|
||||
|
|
|
@ -103,27 +103,19 @@ func (s *Scheme) DecodeInto(data []byte, obj interface{}) error {
|
|||
dataVersion = objVersion
|
||||
}
|
||||
|
||||
if objVersion == dataVersion {
|
||||
// Easy case!
|
||||
err = yaml.Unmarshal(data, obj)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
external, err := s.NewObject(dataVersion, dataKind)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// yaml is a superset of json, so we use it to decode here. That way,
|
||||
// we understand both.
|
||||
err = yaml.Unmarshal(data, external)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.converter.Convert(external, obj, 0, s.generateConvertMeta(dataVersion, objVersion))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
external, err := s.NewObject(dataVersion, dataKind)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// yaml is a superset of json, so we use it to decode here. That way,
|
||||
// we understand both.
|
||||
err = yaml.Unmarshal(data, external)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
err = s.converter.Convert(external, obj, 0, s.generateConvertMeta(dataVersion, objVersion))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Version and Kind should be blank in memory.
|
||||
|
|
|
@ -233,3 +233,140 @@ func (e Equalities) DeepEqual(a1, a2 interface{}) bool {
|
|||
}
|
||||
return e.deepValueEqual(v1, v2, make(map[visit]bool), 0)
|
||||
}
|
||||
|
||||
func (e Equalities) deepValueDerive(v1, v2 reflect.Value, visited map[visit]bool, depth int) bool {
|
||||
if !v1.IsValid() || !v2.IsValid() {
|
||||
return v1.IsValid() == v2.IsValid()
|
||||
}
|
||||
if v1.Type() != v2.Type() {
|
||||
return false
|
||||
}
|
||||
if fv, ok := e[v1.Type()]; ok {
|
||||
return fv.Call([]reflect.Value{v1, v2})[0].Bool()
|
||||
}
|
||||
|
||||
hard := func(k reflect.Kind) bool {
|
||||
switch k {
|
||||
case reflect.Array, reflect.Map, reflect.Slice, reflect.Struct:
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if v1.CanAddr() && v2.CanAddr() && hard(v1.Kind()) {
|
||||
addr1 := v1.UnsafeAddr()
|
||||
addr2 := v2.UnsafeAddr()
|
||||
if addr1 > addr2 {
|
||||
// Canonicalize order to reduce number of entries in visited.
|
||||
addr1, addr2 = addr2, addr1
|
||||
}
|
||||
|
||||
// Short circuit if references are identical ...
|
||||
if addr1 == addr2 {
|
||||
return true
|
||||
}
|
||||
|
||||
// ... or already seen
|
||||
typ := v1.Type()
|
||||
v := visit{addr1, addr2, typ}
|
||||
if visited[v] {
|
||||
return true
|
||||
}
|
||||
|
||||
// Remember for later.
|
||||
visited[v] = true
|
||||
}
|
||||
|
||||
switch v1.Kind() {
|
||||
case reflect.Array:
|
||||
for i := 0; i < v1.Len(); i++ {
|
||||
if !e.deepValueDerive(v1.Index(i), v2.Index(i), visited, depth+1) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case reflect.Slice:
|
||||
if (v1.IsNil() || v1.Len() == 0) != (v2.IsNil() || v2.Len() == 0) {
|
||||
return false
|
||||
}
|
||||
if v1.IsNil() || v1.Len() == 0 {
|
||||
return true
|
||||
}
|
||||
if v1.Pointer() == v2.Pointer() {
|
||||
return true
|
||||
}
|
||||
for i := 0; i < v1.Len(); i++ {
|
||||
if !e.deepValueDerive(v1.Index(i), v2.Index(i), visited, depth+1) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case reflect.String:
|
||||
if v1.Len() == 0 {
|
||||
return true
|
||||
}
|
||||
return v1.String() == v2.String()
|
||||
case reflect.Interface:
|
||||
if v1.IsNil() {
|
||||
return true
|
||||
}
|
||||
return e.deepValueDerive(v1.Elem(), v2.Elem(), visited, depth+1)
|
||||
case reflect.Ptr:
|
||||
if v1.IsNil() {
|
||||
return true
|
||||
}
|
||||
return e.deepValueDerive(v1.Elem(), v2.Elem(), visited, depth+1)
|
||||
case reflect.Struct:
|
||||
for i, n := 0, v1.NumField(); i < n; i++ {
|
||||
if !e.deepValueDerive(v1.Field(i), v2.Field(i), visited, depth+1) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case reflect.Map:
|
||||
if (v1.IsNil() || v1.Len() == 0) != (v2.IsNil() || v2.Len() == 0) {
|
||||
return false
|
||||
}
|
||||
if v1.IsNil() || v1.Len() == 0 {
|
||||
return true
|
||||
}
|
||||
if v1.Pointer() == v2.Pointer() {
|
||||
return true
|
||||
}
|
||||
for _, k := range v1.MapKeys() {
|
||||
if !e.deepValueDerive(v1.MapIndex(k), v2.MapIndex(k), visited, depth+1) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
case reflect.Func:
|
||||
if v1.IsNil() && v2.IsNil() {
|
||||
return true
|
||||
}
|
||||
// Can't do better than this:
|
||||
return false
|
||||
default:
|
||||
// Normal equality suffices
|
||||
if v1.CanInterface() && v2.CanInterface() {
|
||||
return v1.Interface() == v2.Interface()
|
||||
}
|
||||
return v1.CanInterface() == v2.CanInterface()
|
||||
}
|
||||
}
|
||||
|
||||
// DeepDerivative is similar to DeepEqual except that unset fields in a1 are
|
||||
// ignored (not compared). This allows we to focus on the fields that matter to
|
||||
// the semantic comparison.
|
||||
//
|
||||
// The unset fields include a nil pointer and an empty string.
|
||||
func (e Equalities) DeepDerivative(a1, a2 interface{}) bool {
|
||||
if a1 == nil {
|
||||
return true
|
||||
}
|
||||
v1 := reflect.ValueOf(a1)
|
||||
v2 := reflect.ValueOf(a2)
|
||||
if v1.Type() != v2.Type() {
|
||||
return false
|
||||
}
|
||||
return e.deepValueDerive(v1, v2, make(map[visit]bool), 0)
|
||||
}
|
||||
|
|
|
@ -63,7 +63,7 @@ func NewScheme() *Scheme {
|
|||
InternalVersion: "",
|
||||
MetaFactory: DefaultMetaFactory,
|
||||
}
|
||||
s.converter.NameFunc = s.nameFunc
|
||||
s.converter.nameFunc = s.nameFunc
|
||||
return s
|
||||
}
|
||||
|
||||
|
@ -194,7 +194,7 @@ func (s *Scheme) NewObject(versionName, kind string) (interface{}, error) {
|
|||
// add conversion functions for things with changed/removed fields.
|
||||
func (s *Scheme) AddConversionFuncs(conversionFuncs ...interface{}) error {
|
||||
for _, f := range conversionFuncs {
|
||||
if err := s.converter.Register(f); err != nil {
|
||||
if err := s.converter.RegisterConversionFunc(f); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
@ -209,6 +209,29 @@ func (s *Scheme) AddStructFieldConversion(srcFieldType interface{}, srcFieldName
|
|||
return s.converter.SetStructFieldCopy(srcFieldType, srcFieldName, destFieldType, destFieldName)
|
||||
}
|
||||
|
||||
// AddDefaultingFuncs adds functions to the list of default-value functions.
|
||||
// Each of the given functions is responsible for applying default values
|
||||
// when converting an instance of a versioned API object into an internal
|
||||
// API object. These functions do not need to handle sub-objects. We deduce
|
||||
// how to call these functions from the types of their two parameters.
|
||||
//
|
||||
// s.AddDefaultingFuncs(
|
||||
// func(obj *v1beta1.Pod) {
|
||||
// if obj.OptionalField == "" {
|
||||
// obj.OptionalField = "DefaultValue"
|
||||
// }
|
||||
// },
|
||||
// )
|
||||
func (s *Scheme) AddDefaultingFuncs(defaultingFuncs ...interface{}) error {
|
||||
for _, f := range defaultingFuncs {
|
||||
err := s.converter.RegisterDefaultingFunc(f)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Convert will attempt to convert in into out. Both must be pointers. For easy
|
||||
// testing of conversion functions. Returns an error if the conversion isn't
|
||||
// possible. You can call this with types that haven't been registered (for example,
|
||||
|
|
|
@ -73,11 +73,17 @@ func TestParsePod(t *testing.T) {
|
|||
ObjectMeta: api.ObjectMeta{Name: "test pod"},
|
||||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{
|
||||
{Name: "my container"},
|
||||
{
|
||||
Name: "my container",
|
||||
ImagePullPolicy: api.PullIfNotPresent,
|
||||
TerminationMessagePath: api.TerminationMessagePathDefault,
|
||||
},
|
||||
},
|
||||
Volumes: []api.Volume{
|
||||
{Name: "volume"},
|
||||
{Name: "volume", Source: api.VolumeSource{EmptyDir: &api.EmptyDir{}}},
|
||||
},
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
}, v1beta1.Codec, testParser)
|
||||
}
|
||||
|
@ -96,6 +102,8 @@ func TestParseService(t *testing.T) {
|
|||
Selector: map[string]string{
|
||||
"area": "staging",
|
||||
},
|
||||
Protocol: "TCP",
|
||||
SessionAffinity: "None",
|
||||
},
|
||||
}, v1beta1.Codec, testParser)
|
||||
}
|
||||
|
@ -109,11 +117,17 @@ func TestParseController(t *testing.T) {
|
|||
Template: &api.PodTemplateSpec{
|
||||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{
|
||||
{Name: "my container"},
|
||||
{
|
||||
Name: "my container",
|
||||
ImagePullPolicy: api.PullIfNotPresent,
|
||||
TerminationMessagePath: api.TerminationMessagePathDefault,
|
||||
},
|
||||
},
|
||||
Volumes: []api.Volume{
|
||||
{Name: "volume"},
|
||||
{Name: "volume", Source: api.VolumeSource{EmptyDir: &api.EmptyDir{}}},
|
||||
},
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
|
|
@ -71,6 +71,10 @@ func TestYAMLPrinterPrint(t *testing.T) {
|
|||
|
||||
obj := &api.Pod{
|
||||
ObjectMeta: api.ObjectMeta{Name: "foo"},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
}
|
||||
buf.Reset()
|
||||
printer.PrintObj(obj, buf)
|
||||
|
@ -95,6 +99,10 @@ func TestIdentityPrinter(t *testing.T) {
|
|||
|
||||
obj := &api.Pod{
|
||||
ObjectMeta: api.ObjectMeta{Name: "foo"},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
}
|
||||
buff.Reset()
|
||||
printer.PrintObj(obj, buff)
|
||||
|
|
|
@ -42,9 +42,17 @@ func testData() (*api.PodList, *api.ServiceList) {
|
|||
Items: []api.Pod{
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "10"},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{Name: "bar", Namespace: "test", ResourceVersion: "11"},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -55,6 +63,10 @@ func testData() (*api.PodList, *api.ServiceList) {
|
|||
Items: []api.Service{
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{Name: "baz", Namespace: "test", ResourceVersion: "12"},
|
||||
Spec: api.ServiceSpec{
|
||||
Protocol: "TCP",
|
||||
SessionAffinity: "None",
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -296,6 +308,10 @@ func watchTestData() ([]api.Pod, []watch.Event) {
|
|||
Namespace: "test",
|
||||
ResourceVersion: "10",
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
}
|
||||
events := []watch.Event{
|
||||
|
@ -307,6 +323,10 @@ func watchTestData() ([]api.Pod, []watch.Event) {
|
|||
Namespace: "test",
|
||||
ResourceVersion: "11",
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -317,6 +337,10 @@ func watchTestData() ([]api.Pod, []watch.Event) {
|
|||
Namespace: "test",
|
||||
ResourceVersion: "12",
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -42,6 +42,12 @@ func TestMerge(t *testing.T) {
|
|||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicy{
|
||||
Always: &api.RestartPolicyAlways{},
|
||||
},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
|
@ -57,6 +63,10 @@ func TestMerge(t *testing.T) {
|
|||
},
|
||||
Spec: api.PodSpec{
|
||||
Host: "bar",
|
||||
RestartPolicy: api.RestartPolicy{
|
||||
Always: &api.RestartPolicyAlways{},
|
||||
},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -74,12 +84,18 @@ func TestMerge(t *testing.T) {
|
|||
Spec: api.PodSpec{
|
||||
Volumes: []api.Volume{
|
||||
{
|
||||
Name: "v1",
|
||||
Name: "v1",
|
||||
Source: api.VolumeSource{EmptyDir: &api.EmptyDir{}},
|
||||
},
|
||||
{
|
||||
Name: "v2",
|
||||
Name: "v2",
|
||||
Source: api.VolumeSource{EmptyDir: &api.EmptyDir{}},
|
||||
},
|
||||
},
|
||||
RestartPolicy: api.RestartPolicy{
|
||||
Always: &api.RestartPolicyAlways{},
|
||||
},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
},
|
||||
|
@ -91,13 +107,13 @@ func TestMerge(t *testing.T) {
|
|||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
for i, test := range tests {
|
||||
err := Merge(test.obj, test.fragment, "Pod")
|
||||
if !test.expectErr {
|
||||
if err != nil {
|
||||
t.Errorf("unexpected error: %v", err)
|
||||
} else if !reflect.DeepEqual(test.obj, test.expected) {
|
||||
t.Errorf("\nexpected:\n%v\nsaw:\n%v", test.expected, test.obj)
|
||||
t.Errorf("\n\ntestcase[%d]\nexpected:\n%v\nsaw:\n%v", i, test.expected, test.obj)
|
||||
}
|
||||
}
|
||||
if test.expectErr && err == nil {
|
||||
|
|
|
@ -88,9 +88,17 @@ func testData() (*api.PodList, *api.ServiceList) {
|
|||
Items: []api.Pod{
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "test", ResourceVersion: "10"},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{Name: "bar", Namespace: "test", ResourceVersion: "11"},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -376,7 +384,7 @@ func TestSelector(t *testing.T) {
|
|||
if err != nil || singular || len(test.Infos) != 3 {
|
||||
t.Fatalf("unexpected response: %v %f %#v", err, singular, test.Infos)
|
||||
}
|
||||
if !reflect.DeepEqual([]runtime.Object{&pods.Items[0], &pods.Items[1], &svc.Items[0]}, test.Objects()) {
|
||||
if !api.Semantic.DeepDerivative([]runtime.Object{&pods.Items[0], &pods.Items[1], &svc.Items[0]}, test.Objects()) {
|
||||
t.Errorf("unexpected visited objects: %#v", test.Objects())
|
||||
}
|
||||
|
||||
|
@ -419,7 +427,7 @@ func TestStream(t *testing.T) {
|
|||
if err != nil || singular || len(test.Infos) != 3 {
|
||||
t.Fatalf("unexpected response: %v %f %#v", err, singular, test.Infos)
|
||||
}
|
||||
if !reflect.DeepEqual([]runtime.Object{&pods.Items[0], &pods.Items[1], &rc.Items[0]}, test.Objects()) {
|
||||
if !api.Semantic.DeepDerivative([]runtime.Object{&pods.Items[0], &pods.Items[1], &rc.Items[0]}, test.Objects()) {
|
||||
t.Errorf("unexpected visited objects: %#v", test.Objects())
|
||||
}
|
||||
}
|
||||
|
@ -436,7 +444,7 @@ func TestYAMLStream(t *testing.T) {
|
|||
if err != nil || singular || len(test.Infos) != 3 {
|
||||
t.Fatalf("unexpected response: %v %f %#v", err, singular, test.Infos)
|
||||
}
|
||||
if !reflect.DeepEqual([]runtime.Object{&pods.Items[0], &pods.Items[1], &rc.Items[0]}, test.Objects()) {
|
||||
if !api.Semantic.DeepDerivative([]runtime.Object{&pods.Items[0], &pods.Items[1], &rc.Items[0]}, test.Objects()) {
|
||||
t.Errorf("unexpected visited objects: %#v", test.Objects())
|
||||
}
|
||||
}
|
||||
|
@ -458,7 +466,7 @@ func TestMultipleObject(t *testing.T) {
|
|||
&svc.Items[0],
|
||||
},
|
||||
}
|
||||
if !reflect.DeepEqual(expected, obj) {
|
||||
if !api.Semantic.DeepDerivative(expected, obj) {
|
||||
t.Errorf("unexpected visited objects: %#v", obj)
|
||||
}
|
||||
}
|
||||
|
@ -612,7 +620,7 @@ func TestLatest(t *testing.T) {
|
|||
if err != nil || singular || len(test.Infos) != 3 {
|
||||
t.Fatalf("unexpected response: %v %f %#v", err, singular, test.Infos)
|
||||
}
|
||||
if !reflect.DeepEqual([]runtime.Object{newPod, newPod2, newSvc}, test.Objects()) {
|
||||
if !api.Semantic.DeepDerivative([]runtime.Object{newPod, newPod2, newSvc}, test.Objects()) {
|
||||
t.Errorf("unexpected visited objects: %#v", test.Objects())
|
||||
}
|
||||
}
|
||||
|
@ -646,7 +654,7 @@ func TestIgnoreStreamErrors(t *testing.T) {
|
|||
t.Fatalf("unexpected response: %v %f %#v", err, singular, test.Infos)
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual([]runtime.Object{&pods.Items[0], &svc.Items[0]}, test.Objects()) {
|
||||
if !api.Semantic.DeepDerivative([]runtime.Object{&pods.Items[0], &svc.Items[0]}, test.Objects()) {
|
||||
t.Errorf("unexpected visited objects: %#v", test.Objects())
|
||||
}
|
||||
}
|
||||
|
|
|
@ -162,11 +162,17 @@ func TestHelperCreate(t *testing.T) {
|
|||
Req: expectPost,
|
||||
},
|
||||
{
|
||||
Modify: true,
|
||||
Object: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "10"}},
|
||||
ExpectObject: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}},
|
||||
Resp: &http.Response{StatusCode: http.StatusOK, Body: objBody(&api.Status{Status: api.StatusSuccess})},
|
||||
Req: expectPost,
|
||||
Modify: true,
|
||||
Object: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "10"}},
|
||||
ExpectObject: &api.Pod{
|
||||
ObjectMeta: api.ObjectMeta{Name: "foo"},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
Resp: &http.Response{StatusCode: http.StatusOK, Body: objBody(&api.Status{Status: api.StatusSuccess})},
|
||||
Req: expectPost,
|
||||
},
|
||||
}
|
||||
for i, test := range tests {
|
||||
|
@ -400,9 +406,14 @@ func TestHelperUpdate(t *testing.T) {
|
|||
Req: expectPut,
|
||||
},
|
||||
{
|
||||
Object: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}},
|
||||
ExpectObject: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "10"}},
|
||||
|
||||
Object: &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}},
|
||||
ExpectObject: &api.Pod{
|
||||
ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "10"},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
Overwrite: true,
|
||||
RespFunc: func(req *http.Request) (*http.Response, error) {
|
||||
if req.Method == "PUT" {
|
||||
|
|
|
@ -177,7 +177,7 @@ func TestNewPodAddedSnapshotAndUpdates(t *testing.T) {
|
|||
|
||||
// container updates are separated as UPDATE
|
||||
pod := podUpdate.Pods[0]
|
||||
pod.Spec.Containers = []api.Container{{Name: "bar", Image: "test"}}
|
||||
pod.Spec.Containers = []api.Container{{Name: "bar", Image: "test", ImagePullPolicy: api.PullIfNotPresent}}
|
||||
channel <- CreatePodUpdate(kubelet.ADD, NoneSource, pod)
|
||||
expectPodUpdate(t, ch, CreatePodUpdate(kubelet.UPDATE, NoneSource, pod))
|
||||
}
|
||||
|
@ -195,7 +195,7 @@ func TestNewPodAddedSnapshot(t *testing.T) {
|
|||
|
||||
// container updates are separated as UPDATE
|
||||
pod := podUpdate.Pods[0]
|
||||
pod.Spec.Containers = []api.Container{{Name: "bar", Image: "test"}}
|
||||
pod.Spec.Containers = []api.Container{{Name: "bar", Image: "test", ImagePullPolicy: api.PullIfNotPresent}}
|
||||
channel <- CreatePodUpdate(kubelet.ADD, NoneSource, pod)
|
||||
expectPodUpdate(t, ch, CreatePodUpdate(kubelet.SET, TestSource, pod))
|
||||
}
|
||||
|
@ -213,7 +213,7 @@ func TestNewPodAddedUpdatedRemoved(t *testing.T) {
|
|||
|
||||
// an kubelet.ADD should be converted to kubelet.UPDATE
|
||||
pod := CreateValidPod("foo", "new", "test")
|
||||
pod.Spec.Containers = []api.Container{{Name: "bar", Image: "test"}}
|
||||
pod.Spec.Containers = []api.Container{{Name: "bar", Image: "test", ImagePullPolicy: api.PullIfNotPresent}}
|
||||
podUpdate = CreatePodUpdate(kubelet.ADD, NoneSource, pod)
|
||||
channel <- podUpdate
|
||||
expectPodUpdate(t, ch, CreatePodUpdate(kubelet.UPDATE, NoneSource, pod))
|
||||
|
@ -236,7 +236,7 @@ func TestNewPodAddedUpdatedSet(t *testing.T) {
|
|||
|
||||
// should be converted to an kubelet.ADD, kubelet.REMOVE, and kubelet.UPDATE
|
||||
pod := CreateValidPod("foo2", "new", "test")
|
||||
pod.Spec.Containers = []api.Container{{Name: "bar", Image: "test"}}
|
||||
pod.Spec.Containers = []api.Container{{Name: "bar", Image: "test", ImagePullPolicy: api.PullIfNotPresent}}
|
||||
podUpdate = CreatePodUpdate(kubelet.SET, NoneSource, pod, CreateValidPod("foo3", "new", ""), CreateValidPod("foo4", "new", "test"))
|
||||
channel <- podUpdate
|
||||
expectPodUpdate(t, ch,
|
||||
|
|
|
@ -62,7 +62,6 @@ func ExampleManifestAndPod(id string) (v1beta1.ContainerManifest, api.BoundPod)
|
|||
{
|
||||
Name: "c" + id,
|
||||
Image: "foo",
|
||||
TerminationMessagePath: "/somepath",
|
||||
},
|
||||
},
|
||||
Volumes: []api.Volume{
|
||||
|
@ -94,7 +93,7 @@ func TestUpdateOnNonExistentFile(t *testing.T) {
|
|||
case got := <-ch:
|
||||
update := got.(kubelet.PodUpdate)
|
||||
expected := CreatePodUpdate(kubelet.SET, kubelet.FileSource)
|
||||
if !api.Semantic.DeepEqual(expected, update) {
|
||||
if !api.Semantic.DeepDerivative(expected, update) {
|
||||
t.Fatalf("Expected %#v, Got %#v", expected, update)
|
||||
}
|
||||
|
||||
|
@ -137,15 +136,7 @@ func TestReadFromFile(t *testing.T) {
|
|||
Namespace: "",
|
||||
SelfLink: "",
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{
|
||||
{
|
||||
Image: "test/image",
|
||||
TerminationMessagePath: "/dev/termination-log",
|
||||
ImagePullPolicy: api.PullAlways,
|
||||
},
|
||||
},
|
||||
},
|
||||
Spec: api.PodSpec{Containers: []api.Container{{Image: "test/image"}}},
|
||||
})
|
||||
|
||||
// There's no way to provide namespace in ContainerManifest, so
|
||||
|
@ -161,7 +152,7 @@ func TestReadFromFile(t *testing.T) {
|
|||
}
|
||||
update.Pods[0].ObjectMeta.SelfLink = ""
|
||||
|
||||
if !api.Semantic.DeepEqual(expected, update) {
|
||||
if !api.Semantic.DeepDerivative(expected, update) {
|
||||
t.Fatalf("Expected %#v, Got %#v", expected, update)
|
||||
}
|
||||
|
||||
|
@ -191,15 +182,7 @@ func TestReadFromFileWithoutID(t *testing.T) {
|
|||
Namespace: "",
|
||||
SelfLink: "",
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{
|
||||
{
|
||||
Image: "test/image",
|
||||
TerminationMessagePath: "/dev/termination-log",
|
||||
ImagePullPolicy: api.PullAlways,
|
||||
},
|
||||
},
|
||||
},
|
||||
Spec: api.PodSpec{Containers: []api.Container{{Image: "test/image"}}},
|
||||
})
|
||||
|
||||
if len(update.Pods[0].ObjectMeta.Name) == 0 {
|
||||
|
@ -209,7 +192,7 @@ func TestReadFromFileWithoutID(t *testing.T) {
|
|||
update.Pods[0].ObjectMeta.Namespace = ""
|
||||
update.Pods[0].ObjectMeta.SelfLink = ""
|
||||
|
||||
if !api.Semantic.DeepEqual(expected, update) {
|
||||
if !api.Semantic.DeepDerivative(expected, update) {
|
||||
t.Fatalf("Expected %#v, Got %#v", expected, update)
|
||||
}
|
||||
|
||||
|
@ -240,21 +223,13 @@ func TestReadV1Beta2FromFile(t *testing.T) {
|
|||
Namespace: "",
|
||||
SelfLink: "",
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{
|
||||
{
|
||||
Image: "test/image",
|
||||
TerminationMessagePath: "/dev/termination-log",
|
||||
ImagePullPolicy: api.PullAlways,
|
||||
},
|
||||
},
|
||||
},
|
||||
Spec: api.PodSpec{Containers: []api.Container{{Image: "test/image"}}},
|
||||
})
|
||||
|
||||
update.Pods[0].ObjectMeta.Namespace = ""
|
||||
update.Pods[0].ObjectMeta.SelfLink = ""
|
||||
|
||||
if !api.Semantic.DeepEqual(expected, update) {
|
||||
if !api.Semantic.DeepDerivative(expected, update) {
|
||||
t.Fatalf("Expected %#v, Got %#v", expected, update)
|
||||
}
|
||||
|
||||
|
@ -315,7 +290,7 @@ func TestExtractFromEmptyDir(t *testing.T) {
|
|||
|
||||
update := (<-ch).(kubelet.PodUpdate)
|
||||
expected := CreatePodUpdate(kubelet.SET, kubelet.FileSource)
|
||||
if !api.Semantic.DeepEqual(expected, update) {
|
||||
if !api.Semantic.DeepDerivative(expected, update) {
|
||||
t.Errorf("Expected %#v, Got %#v", expected, update)
|
||||
}
|
||||
}
|
||||
|
@ -371,7 +346,7 @@ func TestExtractFromDir(t *testing.T) {
|
|||
}
|
||||
sort.Sort(sortedPods(update.Pods))
|
||||
sort.Sort(sortedPods(expected.Pods))
|
||||
if !api.Semantic.DeepEqual(expected, update) {
|
||||
if !api.Semantic.DeepDerivative(expected, update) {
|
||||
t.Fatalf("Expected %#v, Got %#v", expected, update)
|
||||
}
|
||||
for i := range update.Pods {
|
||||
|
|
|
@ -90,8 +90,10 @@ func (m *Master) createMasterServiceIfNeeded(serviceName string, serviceIP net.I
|
|||
Spec: api.ServiceSpec{
|
||||
Port: servicePort,
|
||||
// maintained by this code, not by the pod selector
|
||||
Selector: nil,
|
||||
PortalIP: serviceIP.String(),
|
||||
Selector: nil,
|
||||
PortalIP: serviceIP.String(),
|
||||
Protocol: api.ProtocolTCP,
|
||||
SessionAffinity: api.AffinityTypeNone,
|
||||
},
|
||||
}
|
||||
// Kids, don't do this at home: this is a hack. There's no good way to call the business
|
||||
|
|
|
@ -126,6 +126,10 @@ func TestControllerDecode(t *testing.T) {
|
|||
"name": "nginx",
|
||||
},
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -225,10 +229,13 @@ var validPodTemplate = api.PodTemplate{
|
|||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{
|
||||
{
|
||||
Name: "test",
|
||||
Image: "test_image",
|
||||
Name: "test",
|
||||
Image: "test_image",
|
||||
ImagePullPolicy: api.PullIfNotPresent,
|
||||
},
|
||||
},
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
|
|
@ -17,7 +17,6 @@ limitations under the License.
|
|||
package etcd
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"strconv"
|
||||
"strings"
|
||||
"testing"
|
||||
|
@ -441,6 +440,10 @@ func TestEtcdUpdatePodNotScheduled(t *testing.T) {
|
|||
"foo": "bar",
|
||||
},
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
}
|
||||
err := registry.UpdatePod(ctx, &podIn)
|
||||
if err != nil {
|
||||
|
@ -515,9 +518,13 @@ func TestEtcdUpdatePodScheduled(t *testing.T) {
|
|||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{
|
||||
{
|
||||
Image: "foo:v2",
|
||||
Image: "foo:v2",
|
||||
ImagePullPolicy: api.PullIfNotPresent,
|
||||
TerminationMessagePath: api.TerminationMessagePathDefault,
|
||||
},
|
||||
},
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
Status: api.PodStatus{
|
||||
Host: "machine",
|
||||
|
@ -1338,6 +1345,8 @@ func TestEtcdUpdateService(t *testing.T) {
|
|||
Selector: map[string]string{
|
||||
"baz": "bar",
|
||||
},
|
||||
Protocol: "TCP",
|
||||
SessionAffinity: "None",
|
||||
},
|
||||
}
|
||||
err := registry.UpdateService(ctx, &testService)
|
||||
|
@ -1352,7 +1361,7 @@ func TestEtcdUpdateService(t *testing.T) {
|
|||
// Clear modified indices before the equality test.
|
||||
svc.ResourceVersion = ""
|
||||
testService.ResourceVersion = ""
|
||||
if !reflect.DeepEqual(*svc, testService) {
|
||||
if !api.Semantic.DeepEqual(*svc, testService) {
|
||||
t.Errorf("Unexpected service: got\n %#v\n, wanted\n %#v", svc, testService)
|
||||
}
|
||||
}
|
||||
|
@ -1404,7 +1413,7 @@ func TestEtcdGetEndpoints(t *testing.T) {
|
|||
t.Errorf("unexpected error: %v", err)
|
||||
}
|
||||
|
||||
if e, a := endpoints, got; !reflect.DeepEqual(e, a) {
|
||||
if e, a := endpoints, got; !api.Semantic.DeepEqual(e, a) {
|
||||
t.Errorf("Unexpected endpoints: %#v, expected %#v", e, a)
|
||||
}
|
||||
}
|
||||
|
@ -1433,7 +1442,7 @@ func TestEtcdUpdateEndpoints(t *testing.T) {
|
|||
}
|
||||
var endpointsOut api.Endpoints
|
||||
err = latest.Codec.DecodeInto([]byte(response.Node.Value), &endpointsOut)
|
||||
if !reflect.DeepEqual(endpoints, endpointsOut) {
|
||||
if !api.Semantic.DeepEqual(endpoints, endpointsOut) {
|
||||
t.Errorf("Unexpected endpoints: %#v, expected %#v", endpointsOut, endpoints)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@ package etcd
|
|||
import (
|
||||
"fmt"
|
||||
"path"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
|
@ -146,7 +145,7 @@ func TestEtcdList(t *testing.T) {
|
|||
continue
|
||||
}
|
||||
|
||||
if e, a := item.out, list; !reflect.DeepEqual(e, a) {
|
||||
if e, a := item.out, list; !api.Semantic.DeepDerivative(e, a) {
|
||||
t.Errorf("%v: Expected %#v, got %#v", name, e, a)
|
||||
}
|
||||
}
|
||||
|
@ -209,7 +208,7 @@ func TestEtcdCreate(t *testing.T) {
|
|||
t.Errorf("%v: unexpected error: %v", name, err)
|
||||
}
|
||||
|
||||
if e, a := item.expect, fakeClient.Data[path]; !reflect.DeepEqual(e, a) {
|
||||
if e, a := item.expect, fakeClient.Data[path]; !api.Semantic.DeepDerivative(e, a) {
|
||||
t.Errorf("%v:\n%s", name, util.ObjectDiff(e, a))
|
||||
}
|
||||
}
|
||||
|
@ -284,7 +283,7 @@ func TestEtcdUpdate(t *testing.T) {
|
|||
t.Errorf("%v: unexpected error: %v", name, err)
|
||||
}
|
||||
|
||||
if e, a := item.expect, fakeClient.Data[path]; !reflect.DeepEqual(e, a) {
|
||||
if e, a := item.expect, fakeClient.Data[path]; !api.Semantic.DeepDerivative(e, a) {
|
||||
t.Errorf("%v:\n%s", name, util.ObjectDiff(e, a))
|
||||
}
|
||||
}
|
||||
|
@ -340,7 +339,7 @@ func TestEtcdGet(t *testing.T) {
|
|||
t.Errorf("%v: unexpected error: %v", name, err)
|
||||
}
|
||||
|
||||
if e, a := item.expect, got; !reflect.DeepEqual(e, a) {
|
||||
if e, a := item.expect, got; !api.Semantic.DeepDerivative(e, a) {
|
||||
t.Errorf("%v:\n%s", name, util.ObjectDiff(e, a))
|
||||
}
|
||||
}
|
||||
|
@ -396,7 +395,7 @@ func TestEtcdDelete(t *testing.T) {
|
|||
t.Errorf("%v: unexpected error: %v", name, err)
|
||||
}
|
||||
|
||||
if e, a := item.expect, fakeClient.Data[path]; !reflect.DeepEqual(e, a) {
|
||||
if e, a := item.expect, fakeClient.Data[path]; !api.Semantic.DeepDerivative(e, a) {
|
||||
t.Errorf("%v:\n%s", name, util.ObjectDiff(e, a))
|
||||
}
|
||||
}
|
||||
|
@ -432,7 +431,7 @@ func TestEtcdWatch(t *testing.T) {
|
|||
t.Fatalf("unexpected channel close")
|
||||
}
|
||||
|
||||
if e, a := podA, got.Object; !reflect.DeepEqual(e, a) {
|
||||
if e, a := podA, got.Object; !api.Semantic.DeepDerivative(e, a) {
|
||||
t.Errorf("difference: %s", util.ObjectDiff(e, a))
|
||||
}
|
||||
}
|
||||
|
|
|
@ -88,6 +88,10 @@ func TestCreatePodRegistryError(t *testing.T) {
|
|||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
}
|
||||
ctx := api.NewDefaultContext()
|
||||
ch, err := storage.Create(ctx, pod)
|
||||
|
@ -108,6 +112,10 @@ func TestCreatePodSetsIds(t *testing.T) {
|
|||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
}
|
||||
ctx := api.NewDefaultContext()
|
||||
ch, err := storage.Create(ctx, pod)
|
||||
|
@ -135,6 +143,10 @@ func TestCreatePodSetsUID(t *testing.T) {
|
|||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
}
|
||||
ctx := api.NewDefaultContext()
|
||||
ch, err := storage.Create(ctx, pod)
|
||||
|
@ -346,6 +358,10 @@ func TestPodDecode(t *testing.T) {
|
|||
ObjectMeta: api.ObjectMeta{
|
||||
Name: "foo",
|
||||
},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
}
|
||||
body, err := latest.Codec.Encode(expected)
|
||||
if err != nil {
|
||||
|
@ -447,7 +463,12 @@ func TestCreatePod(t *testing.T) {
|
|||
registry: podRegistry,
|
||||
podCache: &fakeCache{statusToReturn: &api.PodStatus{}},
|
||||
}
|
||||
pod := &api.Pod{}
|
||||
pod := &api.Pod{
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
}
|
||||
pod.Name = "foo"
|
||||
ctx := api.NewDefaultContext()
|
||||
channel, err := storage.Create(ctx, pod)
|
||||
|
@ -470,6 +491,10 @@ func TestCreatePodWithConflictingNamespace(t *testing.T) {
|
|||
storage := REST{}
|
||||
pod := &api.Pod{
|
||||
ObjectMeta: api.ObjectMeta{Name: "test", Namespace: "not-default"},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
}
|
||||
|
||||
ctx := api.NewDefaultContext()
|
||||
|
@ -488,6 +513,10 @@ func TestUpdatePodWithConflictingNamespace(t *testing.T) {
|
|||
storage := REST{}
|
||||
pod := &api.Pod{
|
||||
ObjectMeta: api.ObjectMeta{Name: "test", Namespace: "not-default"},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
}
|
||||
|
||||
ctx := api.NewDefaultContext()
|
||||
|
@ -647,10 +676,13 @@ func TestCreate(t *testing.T) {
|
|||
Spec: api.PodSpec{
|
||||
Containers: []api.Container{
|
||||
{
|
||||
Name: "test1",
|
||||
Image: "foo",
|
||||
Name: "test1",
|
||||
Image: "foo",
|
||||
ImagePullPolicy: api.PullIfNotPresent,
|
||||
},
|
||||
},
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
// invalid
|
||||
|
|
|
@ -49,8 +49,10 @@ func TestServiceRegistryCreate(t *testing.T) {
|
|||
svc := &api.Service{
|
||||
ObjectMeta: api.ObjectMeta{Name: "foo"},
|
||||
Spec: api.ServiceSpec{
|
||||
Port: 6502,
|
||||
Selector: map[string]string{"bar": "baz"},
|
||||
Port: 6502,
|
||||
Selector: map[string]string{"bar": "baz"},
|
||||
Protocol: api.ProtocolTCP,
|
||||
SessionAffinity: api.AffinityTypeNone,
|
||||
},
|
||||
}
|
||||
ctx := api.NewDefaultContext()
|
||||
|
@ -88,14 +90,18 @@ func TestServiceStorageValidatesCreate(t *testing.T) {
|
|||
"empty ID": {
|
||||
ObjectMeta: api.ObjectMeta{Name: ""},
|
||||
Spec: api.ServiceSpec{
|
||||
Port: 6502,
|
||||
Selector: map[string]string{"bar": "baz"},
|
||||
Port: 6502,
|
||||
Selector: map[string]string{"bar": "baz"},
|
||||
Protocol: api.ProtocolTCP,
|
||||
SessionAffinity: api.AffinityTypeNone,
|
||||
},
|
||||
},
|
||||
"empty selector": {
|
||||
ObjectMeta: api.ObjectMeta{Name: "foo"},
|
||||
Spec: api.ServiceSpec{
|
||||
Selector: map[string]string{"bar": "baz"},
|
||||
Selector: map[string]string{"bar": "baz"},
|
||||
Protocol: api.ProtocolTCP,
|
||||
SessionAffinity: api.AffinityTypeNone,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -126,8 +132,10 @@ func TestServiceRegistryUpdate(t *testing.T) {
|
|||
c, err := storage.Update(ctx, &api.Service{
|
||||
ObjectMeta: api.ObjectMeta{Name: "foo"},
|
||||
Spec: api.ServiceSpec{
|
||||
Port: 6502,
|
||||
Selector: map[string]string{"bar": "baz2"},
|
||||
Port: 6502,
|
||||
Selector: map[string]string{"bar": "baz2"},
|
||||
Protocol: api.ProtocolTCP,
|
||||
SessionAffinity: api.AffinityTypeNone,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
|
@ -161,15 +169,19 @@ func TestServiceStorageValidatesUpdate(t *testing.T) {
|
|||
"empty ID": {
|
||||
ObjectMeta: api.ObjectMeta{Name: ""},
|
||||
Spec: api.ServiceSpec{
|
||||
Port: 6502,
|
||||
Selector: map[string]string{"bar": "baz"},
|
||||
Port: 6502,
|
||||
Selector: map[string]string{"bar": "baz"},
|
||||
Protocol: api.ProtocolTCP,
|
||||
SessionAffinity: api.AffinityTypeNone,
|
||||
},
|
||||
},
|
||||
"invalid selector": {
|
||||
ObjectMeta: api.ObjectMeta{Name: "foo"},
|
||||
Spec: api.ServiceSpec{
|
||||
Port: 6502,
|
||||
Selector: map[string]string{"ThisSelectorFailsValidation": "ok"},
|
||||
Port: 6502,
|
||||
Selector: map[string]string{"ThisSelectorFailsValidation": "ok"},
|
||||
Protocol: api.ProtocolTCP,
|
||||
SessionAffinity: api.AffinityTypeNone,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
@ -196,6 +208,8 @@ func TestServiceRegistryExternalService(t *testing.T) {
|
|||
Port: 6502,
|
||||
Selector: map[string]string{"bar": "baz"},
|
||||
CreateExternalLoadBalancer: true,
|
||||
Protocol: api.ProtocolTCP,
|
||||
SessionAffinity: api.AffinityTypeNone,
|
||||
},
|
||||
}
|
||||
c, _ := storage.Create(ctx, svc)
|
||||
|
@ -225,6 +239,8 @@ func TestServiceRegistryExternalServiceError(t *testing.T) {
|
|||
Port: 6502,
|
||||
Selector: map[string]string{"bar": "baz"},
|
||||
CreateExternalLoadBalancer: true,
|
||||
Protocol: api.ProtocolTCP,
|
||||
SessionAffinity: api.AffinityTypeNone,
|
||||
},
|
||||
}
|
||||
ctx := api.NewDefaultContext()
|
||||
|
@ -247,7 +263,9 @@ func TestServiceRegistryDelete(t *testing.T) {
|
|||
svc := &api.Service{
|
||||
ObjectMeta: api.ObjectMeta{Name: "foo"},
|
||||
Spec: api.ServiceSpec{
|
||||
Selector: map[string]string{"bar": "baz"},
|
||||
Selector: map[string]string{"bar": "baz"},
|
||||
Protocol: api.ProtocolTCP,
|
||||
SessionAffinity: api.AffinityTypeNone,
|
||||
},
|
||||
}
|
||||
registry.CreateService(ctx, svc)
|
||||
|
@ -272,6 +290,8 @@ func TestServiceRegistryDeleteExternal(t *testing.T) {
|
|||
Spec: api.ServiceSpec{
|
||||
Selector: map[string]string{"bar": "baz"},
|
||||
CreateExternalLoadBalancer: true,
|
||||
Protocol: api.ProtocolTCP,
|
||||
SessionAffinity: api.AffinityTypeNone,
|
||||
},
|
||||
}
|
||||
registry.CreateService(ctx, svc)
|
||||
|
@ -386,8 +406,10 @@ func TestServiceRegistryIPAllocation(t *testing.T) {
|
|||
svc1 := &api.Service{
|
||||
ObjectMeta: api.ObjectMeta{Name: "foo"},
|
||||
Spec: api.ServiceSpec{
|
||||
Selector: map[string]string{"bar": "baz"},
|
||||
Port: 6502,
|
||||
Selector: map[string]string{"bar": "baz"},
|
||||
Port: 6502,
|
||||
Protocol: api.ProtocolTCP,
|
||||
SessionAffinity: api.AffinityTypeNone,
|
||||
},
|
||||
}
|
||||
ctx := api.NewDefaultContext()
|
||||
|
@ -404,8 +426,10 @@ func TestServiceRegistryIPAllocation(t *testing.T) {
|
|||
svc2 := &api.Service{
|
||||
ObjectMeta: api.ObjectMeta{Name: "bar"},
|
||||
Spec: api.ServiceSpec{
|
||||
Selector: map[string]string{"bar": "baz"},
|
||||
Port: 6502,
|
||||
Selector: map[string]string{"bar": "baz"},
|
||||
Port: 6502,
|
||||
Protocol: api.ProtocolTCP,
|
||||
SessionAffinity: api.AffinityTypeNone,
|
||||
}}
|
||||
ctx = api.NewDefaultContext()
|
||||
c2, _ := rest.Create(ctx, svc2)
|
||||
|
@ -421,9 +445,11 @@ func TestServiceRegistryIPAllocation(t *testing.T) {
|
|||
svc3 := &api.Service{
|
||||
ObjectMeta: api.ObjectMeta{Name: "quux"},
|
||||
Spec: api.ServiceSpec{
|
||||
Selector: map[string]string{"bar": "baz"},
|
||||
PortalIP: "1.2.3.93",
|
||||
Port: 6502,
|
||||
Selector: map[string]string{"bar": "baz"},
|
||||
PortalIP: "1.2.3.93",
|
||||
Port: 6502,
|
||||
Protocol: api.ProtocolTCP,
|
||||
SessionAffinity: api.AffinityTypeNone,
|
||||
},
|
||||
}
|
||||
ctx = api.NewDefaultContext()
|
||||
|
@ -445,8 +471,10 @@ func TestServiceRegistryIPReallocation(t *testing.T) {
|
|||
svc1 := &api.Service{
|
||||
ObjectMeta: api.ObjectMeta{Name: "foo"},
|
||||
Spec: api.ServiceSpec{
|
||||
Selector: map[string]string{"bar": "baz"},
|
||||
Port: 6502,
|
||||
Selector: map[string]string{"bar": "baz"},
|
||||
Port: 6502,
|
||||
Protocol: api.ProtocolTCP,
|
||||
SessionAffinity: api.AffinityTypeNone,
|
||||
},
|
||||
}
|
||||
ctx := api.NewDefaultContext()
|
||||
|
@ -466,8 +494,10 @@ func TestServiceRegistryIPReallocation(t *testing.T) {
|
|||
svc2 := &api.Service{
|
||||
ObjectMeta: api.ObjectMeta{Name: "bar"},
|
||||
Spec: api.ServiceSpec{
|
||||
Selector: map[string]string{"bar": "baz"},
|
||||
Port: 6502,
|
||||
Selector: map[string]string{"bar": "baz"},
|
||||
Port: 6502,
|
||||
Protocol: api.ProtocolTCP,
|
||||
SessionAffinity: api.AffinityTypeNone,
|
||||
},
|
||||
}
|
||||
ctx = api.NewDefaultContext()
|
||||
|
@ -492,8 +522,10 @@ func TestServiceRegistryIPUpdate(t *testing.T) {
|
|||
svc := &api.Service{
|
||||
ObjectMeta: api.ObjectMeta{Name: "foo"},
|
||||
Spec: api.ServiceSpec{
|
||||
Selector: map[string]string{"bar": "baz"},
|
||||
Port: 6502,
|
||||
Selector: map[string]string{"bar": "baz"},
|
||||
Port: 6502,
|
||||
Protocol: api.ProtocolTCP,
|
||||
SessionAffinity: api.AffinityTypeNone,
|
||||
},
|
||||
}
|
||||
ctx := api.NewDefaultContext()
|
||||
|
@ -541,6 +573,8 @@ func TestServiceRegistryIPExternalLoadBalancer(t *testing.T) {
|
|||
Selector: map[string]string{"bar": "baz"},
|
||||
Port: 6502,
|
||||
CreateExternalLoadBalancer: true,
|
||||
Protocol: api.ProtocolTCP,
|
||||
SessionAffinity: api.AffinityTypeNone,
|
||||
},
|
||||
}
|
||||
ctx := api.NewDefaultContext()
|
||||
|
@ -573,8 +607,10 @@ func TestServiceRegistryIPReloadFromStorage(t *testing.T) {
|
|||
svc := &api.Service{
|
||||
ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: api.NamespaceDefault},
|
||||
Spec: api.ServiceSpec{
|
||||
Selector: map[string]string{"bar": "baz"},
|
||||
Port: 6502,
|
||||
Selector: map[string]string{"bar": "baz"},
|
||||
Port: 6502,
|
||||
Protocol: api.ProtocolTCP,
|
||||
SessionAffinity: api.AffinityTypeNone,
|
||||
},
|
||||
}
|
||||
ctx := api.NewDefaultContext()
|
||||
|
@ -583,8 +619,10 @@ func TestServiceRegistryIPReloadFromStorage(t *testing.T) {
|
|||
svc = &api.Service{
|
||||
ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: api.NamespaceDefault},
|
||||
Spec: api.ServiceSpec{
|
||||
Selector: map[string]string{"bar": "baz"},
|
||||
Port: 6502,
|
||||
Selector: map[string]string{"bar": "baz"},
|
||||
Port: 6502,
|
||||
Protocol: api.ProtocolTCP,
|
||||
SessionAffinity: api.AffinityTypeNone,
|
||||
},
|
||||
}
|
||||
c, _ = rest1.Create(ctx, svc)
|
||||
|
@ -597,8 +635,10 @@ func TestServiceRegistryIPReloadFromStorage(t *testing.T) {
|
|||
svc = &api.Service{
|
||||
ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: api.NamespaceDefault},
|
||||
Spec: api.ServiceSpec{
|
||||
Selector: map[string]string{"bar": "baz"},
|
||||
Port: 6502,
|
||||
Selector: map[string]string{"bar": "baz"},
|
||||
Port: 6502,
|
||||
Protocol: api.ProtocolTCP,
|
||||
SessionAffinity: api.AffinityTypeNone,
|
||||
},
|
||||
}
|
||||
c, _ = rest2.Create(ctx, svc)
|
||||
|
@ -658,8 +698,10 @@ func TestCreate(t *testing.T) {
|
|||
// valid
|
||||
&api.Service{
|
||||
Spec: api.ServiceSpec{
|
||||
Selector: map[string]string{"bar": "baz"},
|
||||
Port: 6502,
|
||||
Selector: map[string]string{"bar": "baz"},
|
||||
Port: 6502,
|
||||
Protocol: "TCP",
|
||||
SessionAffinity: "None",
|
||||
},
|
||||
},
|
||||
// invalid
|
||||
|
|
|
@ -265,7 +265,8 @@ func (s *Scheme) Log(l conversion.DebugLogger) {
|
|||
|
||||
// AddConversionFuncs adds a function to the list of conversion functions. The given
|
||||
// function should know how to convert between two API objects. We deduce how to call
|
||||
// it from the types of its two parameters; see the comment for Converter.Register.
|
||||
// it from the types of its two parameters; see the comment for
|
||||
// Converter.RegisterConversionFunction.
|
||||
//
|
||||
// Note that, if you need to copy sub-objects that didn't change, it's safe to call
|
||||
// Convert() inside your conversionFuncs, as long as you don't start a conversion
|
||||
|
@ -287,6 +288,13 @@ func (s *Scheme) AddStructFieldConversion(srcFieldType interface{}, srcFieldName
|
|||
return s.raw.AddStructFieldConversion(srcFieldType, srcFieldName, destFieldType, destFieldName)
|
||||
}
|
||||
|
||||
// AddDefaultingFuncs adds a function to the list of value-defaulting functions.
|
||||
// We deduce how to call it from the types of its two parameters; see the
|
||||
// comment for Converter.RegisterDefaultingFunction.
|
||||
func (s *Scheme) AddDefaultingFuncs(defaultingFuncs ...interface{}) error {
|
||||
return s.raw.AddDefaultingFuncs(defaultingFuncs...)
|
||||
}
|
||||
|
||||
// Convert will attempt to convert in into out. Both must be pointers.
|
||||
// For easy testing of conversion functions. Returns an error if the conversion isn't
|
||||
// possible.
|
||||
|
|
|
@ -26,6 +26,7 @@ import (
|
|||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/meta"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api/testapi"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/conversion"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/runtime"
|
||||
"github.com/coreos/go-etcd/etcd"
|
||||
)
|
||||
|
@ -47,6 +48,12 @@ func init() {
|
|||
scheme.AddKnownTypes("", &TestResource{})
|
||||
scheme.AddKnownTypes("v1beta1", &TestResource{})
|
||||
codec = runtime.CodecFor(scheme, "v1beta1")
|
||||
scheme.AddConversionFuncs(
|
||||
func(in *TestResource, out *TestResource, s conversion.Scope) error {
|
||||
*out = *in
|
||||
return nil
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
func TestIsEtcdNotFound(t *testing.T) {
|
||||
|
@ -94,10 +101,27 @@ func TestExtractToList(t *testing.T) {
|
|||
expect := api.PodList{
|
||||
ListMeta: api.ListMeta{ResourceVersion: "10"},
|
||||
Items: []api.Pod{
|
||||
// We expect items to be sorted by its name.
|
||||
{ObjectMeta: api.ObjectMeta{Name: "bar", ResourceVersion: "2"}},
|
||||
{ObjectMeta: api.ObjectMeta{Name: "baz", ResourceVersion: "3"}},
|
||||
{ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "1"}},
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{Name: "bar", ResourceVersion: "2"},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{Name: "baz", ResourceVersion: "3"},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "1"},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -160,9 +184,27 @@ func TestExtractToListAcrossDirectories(t *testing.T) {
|
|||
ListMeta: api.ListMeta{ResourceVersion: "10"},
|
||||
Items: []api.Pod{
|
||||
// We expect list to be sorted by directory (e.g. namespace) first, then by name.
|
||||
{ObjectMeta: api.ObjectMeta{Name: "baz", ResourceVersion: "1"}},
|
||||
{ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "1"}},
|
||||
{ObjectMeta: api.ObjectMeta{Name: "bar", ResourceVersion: "2"}},
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{Name: "baz", ResourceVersion: "1"},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "1"},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{Name: "bar", ResourceVersion: "2"},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -212,9 +254,27 @@ func TestExtractToListExcludesDirectories(t *testing.T) {
|
|||
expect := api.PodList{
|
||||
ListMeta: api.ListMeta{ResourceVersion: "10"},
|
||||
Items: []api.Pod{
|
||||
{ObjectMeta: api.ObjectMeta{Name: "bar", ResourceVersion: "2"}},
|
||||
{ObjectMeta: api.ObjectMeta{Name: "baz", ResourceVersion: "3"}},
|
||||
{ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "1"}},
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{Name: "bar", ResourceVersion: "2"},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{Name: "baz", ResourceVersion: "3"},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
{
|
||||
ObjectMeta: api.ObjectMeta{Name: "foo", ResourceVersion: "1"},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
|
@ -231,7 +291,13 @@ func TestExtractToListExcludesDirectories(t *testing.T) {
|
|||
|
||||
func TestExtractObj(t *testing.T) {
|
||||
fakeClient := NewFakeEtcdClient(t)
|
||||
expect := api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo"}}
|
||||
expect := api.Pod{
|
||||
ObjectMeta: api.ObjectMeta{Name: "foo"},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
}
|
||||
fakeClient.Set("/some/key", runtime.EncodeOrDie(testapi.Codec(), &expect), 0)
|
||||
helper := EtcdHelper{fakeClient, testapi.Codec(), versioner}
|
||||
var got api.Pod
|
||||
|
|
|
@ -18,7 +18,6 @@ package tools
|
|||
|
||||
import (
|
||||
"fmt"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -123,7 +122,7 @@ func TestWatchInterpretations(t *testing.T) {
|
|||
if e, a := item.expectType, event.Type; e != a {
|
||||
t.Errorf("'%v - %v': expected %v, got %v", name, action, e, a)
|
||||
}
|
||||
if e, a := item.expectObject, event.Object; !reflect.DeepEqual(e, a) {
|
||||
if e, a := item.expectObject, event.Object; !api.Semantic.DeepDerivative(e, a) {
|
||||
t.Errorf("'%v - %v': expected %v, got %v", name, action, e, a)
|
||||
}
|
||||
}
|
||||
|
@ -250,7 +249,7 @@ func TestWatch(t *testing.T) {
|
|||
if e, a := watch.Added, event.Type; e != a {
|
||||
t.Errorf("Expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := pod, event.Object; !reflect.DeepEqual(e, a) {
|
||||
if e, a := pod, event.Object; !api.Semantic.DeepDerivative(e, a) {
|
||||
t.Errorf("Expected %v, got %v", e, a)
|
||||
}
|
||||
|
||||
|
@ -381,7 +380,7 @@ func TestWatchEtcdState(t *testing.T) {
|
|||
t.Errorf("%s: expected type %v, got %v", k, e, a)
|
||||
break
|
||||
}
|
||||
if e, a := testCase.Expected[i].Endpoints, event.Object.(*api.Endpoints).Endpoints; !reflect.DeepEqual(e, a) {
|
||||
if e, a := testCase.Expected[i].Endpoints, event.Object.(*api.Endpoints).Endpoints; !api.Semantic.DeepDerivative(e, a) {
|
||||
t.Errorf("%s: expected type %v, got %v", k, e, a)
|
||||
break
|
||||
}
|
||||
|
@ -456,7 +455,7 @@ func TestWatchFromZeroIndex(t *testing.T) {
|
|||
t.Errorf("%s: expected pod with resource version %v, Got %#v", k, testCase.ExpectedVersion, actualPod)
|
||||
}
|
||||
pod.ResourceVersion = testCase.ExpectedVersion
|
||||
if e, a := pod, event.Object; !reflect.DeepEqual(e, a) {
|
||||
if e, a := pod, event.Object; !api.Semantic.DeepDerivative(e, a) {
|
||||
t.Errorf("%s: expected %v, got %v", k, e, a)
|
||||
}
|
||||
watching.Stop()
|
||||
|
@ -515,7 +514,7 @@ func TestWatchListFromZeroIndex(t *testing.T) {
|
|||
t.Errorf("Expected pod with resource version %d, Got %#v", 1, actualPod)
|
||||
}
|
||||
pod.ResourceVersion = "1"
|
||||
if e, a := pod, event.Object; !reflect.DeepEqual(e, a) {
|
||||
if e, a := pod, event.Object; !api.Semantic.DeepDerivative(e, a) {
|
||||
t.Errorf("Expected %v, got %v", e, a)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,6 @@ package json
|
|||
import (
|
||||
"encoding/json"
|
||||
"io"
|
||||
"reflect"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
|
@ -58,7 +57,7 @@ func TestDecoder(t *testing.T) {
|
|||
if e, a := eventType, action; e != a {
|
||||
t.Errorf("Expected %v, got %v", e, a)
|
||||
}
|
||||
if e, a := expect, got; !reflect.DeepEqual(e, a) {
|
||||
if e, a := expect, got; !api.Semantic.DeepDerivative(e, a) {
|
||||
t.Errorf("Expected %v, got %v", e, a)
|
||||
}
|
||||
t.Logf("Exited read")
|
||||
|
|
|
@ -19,7 +19,6 @@ package json
|
|||
import (
|
||||
"bytes"
|
||||
"io/ioutil"
|
||||
"reflect"
|
||||
"testing"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
|
@ -66,7 +65,7 @@ func TestEncodeDecodeRoundTrip(t *testing.T) {
|
|||
t.Errorf("%d: unexpected error: %v", i, err)
|
||||
continue
|
||||
}
|
||||
if !reflect.DeepEqual(testCase.Object, obj) {
|
||||
if !api.Semantic.DeepDerivative(testCase.Object, obj) {
|
||||
t.Errorf("%d: expected %#v, got %#v", i, testCase.Object, obj)
|
||||
}
|
||||
if event != testCase.Type {
|
||||
|
|
|
@ -195,7 +195,13 @@ func makeURL(suffix string) string {
|
|||
}
|
||||
|
||||
func TestDefaultErrorFunc(t *testing.T) {
|
||||
testPod := &api.Pod{ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "bar"}}
|
||||
testPod := &api.Pod{
|
||||
ObjectMeta: api.ObjectMeta{Name: "foo", Namespace: "bar"},
|
||||
Spec: api.PodSpec{
|
||||
RestartPolicy: api.RestartPolicy{Always: &api.RestartPolicyAlways{}},
|
||||
DNSPolicy: api.DNSClusterFirst,
|
||||
},
|
||||
}
|
||||
handler := util.FakeHandler{
|
||||
StatusCode: 200,
|
||||
ResponseBody: runtime.EncodeOrDie(latest.Codec, testPod),
|
||||
|
|
Loading…
Reference in New Issue