mirror of
https://github.com/k3s-io/kubernetes.git
synced 2025-07-22 11:21:47 +00:00
Factor out API defaulting from validation logic
Currently, the validation logic validates fields in an object and supply default values wherever applies. This change factors out defaulting to a set of defaulting callback functions for decoding (see #1502 for more discussion). * This change is based on pull request 2587. * Most defaulting has been migrated to defaults.go where the defaulting functions are added. * validation_test.go and converter_test.go have been adapted to not testing the default values. * Fixed all tests with that create invalid objects with the absence of defaulting logic.
This commit is contained in:
parent
1ddb68d8d7
commit
4a72addaeb
@ -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
|
||||
}
|
||||
|
@ -17,6 +17,8 @@ limitations under the License.
|
||||
package v1beta1
|
||||
|
||||
import (
|
||||
"strings"
|
||||
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
|
||||
"github.com/GoogleCloudPlatform/kubernetes/pkg/util"
|
||||
)
|
||||
@ -24,8 +26,8 @@ import (
|
||||
func init() {
|
||||
api.Scheme.AddDefaultingFuncs(
|
||||
func(obj *Volume) {
|
||||
if obj.Source == nil || util.AllPtrFieldsNil(obj.Source) {
|
||||
obj.Source = &VolumeSource{
|
||||
if util.AllPtrFieldsNil(&obj.Source) {
|
||||
obj.Source = VolumeSource{
|
||||
EmptyDir: &EmptyDir{},
|
||||
}
|
||||
}
|
||||
@ -36,13 +38,18 @@ func init() {
|
||||
}
|
||||
},
|
||||
func(obj *Container) {
|
||||
// TODO: delete helper functions that touch this
|
||||
if obj.ImagePullPolicy == "" {
|
||||
obj.ImagePullPolicy = PullIfNotPresent
|
||||
// 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 == "" {
|
||||
// TODO: fix other code that sets this
|
||||
obj.TerminationMessagePath = api.TerminationMessagePathDefault
|
||||
obj.TerminationMessagePath = TerminationMessagePathDefault
|
||||
}
|
||||
},
|
||||
func(obj *RestartPolicy) {
|
||||
@ -54,8 +61,19 @@ func init() {
|
||||
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
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// TODO: remove redundant code in validation and conversion
|
||||
|
65
pkg/api/v1beta1/defaults_test.go
Normal file
65
pkg/api/v1beta1/defaults_test.go
Normal file
@ -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
|
||||
|
79
pkg/api/v1beta2/defaults.go
Normal file
79
pkg/api/v1beta2/defaults.go
Normal file
@ -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 {
|
||||
|
74
pkg/api/v1beta3/defaults.go
Normal file
74
pkg/api/v1beta3/defaults.go
Normal file
@ -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)
|
||||
|
@ -68,7 +68,6 @@ func NewConverter() *Converter {
|
||||
return &Converter{
|
||||
conversionFuncs: map[typePair]reflect.Value{},
|
||||
defaultingFuncs: map[reflect.Type]reflect.Value{},
|
||||
funcs: map[typePair]reflect.Value{},
|
||||
nameFunc: func(t reflect.Type) string { return t.Name() },
|
||||
structFieldDests: map[typeNamePair][]typeNamePair{},
|
||||
structFieldSources: map[typeNamePair][]typeNamePair{},
|
||||
@ -221,7 +220,7 @@ func (s *scope) error(message string, args ...interface{}) error {
|
||||
// used if recursive conversion calls are desired). It must return an error.
|
||||
//
|
||||
// Example:
|
||||
// c.RegisteConversionFuncr(
|
||||
// c.RegisteConversionFunc(
|
||||
// func(in *Pod, out *v1beta1.Pod, s Scope) error {
|
||||
// // conversion logic...
|
||||
// return nil
|
||||
@ -279,7 +278,7 @@ func (c *Converter) SetStructFieldCopy(srcFieldType interface{}, srcFieldName st
|
||||
// defaultingFunc must take one parameters: a pointer to the input type.
|
||||
//
|
||||
// Example:
|
||||
// c.RegisteDefaultingFuncr(
|
||||
// c.RegisteDefaultingFunc(
|
||||
// func(in *v1beta1.Pod) {
|
||||
// // defaulting logic...
|
||||
// })
|
||||
@ -415,7 +414,6 @@ 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()
|
||||
|
||||
// Apply default values.
|
||||
if fv, ok := c.defaultingFuncs[st]; ok {
|
||||
if c.Debug != 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
|
||||
}
|
||||
@ -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
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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()
|
||||
@ -91,14 +93,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,
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -129,8 +135,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 {
|
||||
@ -164,15 +172,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,
|
||||
},
|
||||
},
|
||||
}
|
||||
@ -199,6 +211,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)
|
||||
@ -228,6 +242,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()
|
||||
@ -250,7 +266,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)
|
||||
@ -275,6 +293,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)
|
||||
@ -389,8 +409,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()
|
||||
@ -407,8 +429,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)
|
||||
@ -424,9 +448,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()
|
||||
@ -448,8 +474,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()
|
||||
@ -469,8 +497,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()
|
||||
@ -495,8 +525,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()
|
||||
@ -551,6 +583,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()
|
||||
@ -586,8 +620,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()
|
||||
@ -596,8 +632,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)
|
||||
@ -610,8 +648,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)
|
||||
@ -671,8 +711,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
|
||||
|
@ -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
Block a user