diff --git a/hack/.golint_failures b/hack/.golint_failures index dd074d20ef6..2bc450c9b66 100644 --- a/hack/.golint_failures +++ b/hack/.golint_failures @@ -541,7 +541,6 @@ staging/src/k8s.io/apimachinery/pkg/apimachinery/announced staging/src/k8s.io/apimachinery/pkg/apimachinery/registered staging/src/k8s.io/apimachinery/pkg/apis/meta/fuzzer staging/src/k8s.io/apimachinery/pkg/apis/meta/internalversion -staging/src/k8s.io/apimachinery/pkg/apis/meta/v1 staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/validation staging/src/k8s.io/apimachinery/pkg/apis/meta/v1alpha1 diff --git a/pkg/api/v1/BUILD b/pkg/api/v1/BUILD index 116b0997ec6..8b3b00eb994 100644 --- a/pkg/api/v1/BUILD +++ b/pkg/api/v1/BUILD @@ -46,9 +46,14 @@ go_test( "//pkg/api:go_default_library", "//pkg/api/legacyscheme:go_default_library", "//pkg/api/testapi:go_default_library", + "//pkg/api/testing:go_default_library", + "//pkg/apis/extensions:go_default_library", + "//pkg/util/pointer:go_default_library", "//vendor/k8s.io/api/core/v1:go_default_library", + "//vendor/k8s.io/api/extensions/v1beta1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/api/testing/fuzzer:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library", diff --git a/pkg/api/v1/conversion.go b/pkg/api/v1/conversion.go index 3076db9ccd7..c5b962a70c5 100644 --- a/pkg/api/v1/conversion.go +++ b/pkg/api/v1/conversion.go @@ -235,7 +235,9 @@ func Convert_v1_ReplicationController_to_extensions_ReplicaSet(in *v1.Replicatio func Convert_v1_ReplicationControllerSpec_to_extensions_ReplicaSetSpec(in *v1.ReplicationControllerSpec, out *extensions.ReplicaSetSpec, s conversion.Scope) error { out.Replicas = *in.Replicas + out.MinReadySeconds = in.MinReadySeconds if in.Selector != nil { + out.Selector = new(metav1.LabelSelector) metav1.Convert_map_to_unversioned_LabelSelector(&in.Selector, out.Selector, s) } if in.Template != nil { @@ -252,6 +254,15 @@ func Convert_v1_ReplicationControllerStatus_to_extensions_ReplicaSetStatus(in *v out.ReadyReplicas = in.ReadyReplicas out.AvailableReplicas = in.AvailableReplicas out.ObservedGeneration = in.ObservedGeneration + for _, cond := range in.Conditions { + out.Conditions = append(out.Conditions, extensions.ReplicaSetCondition{ + Type: extensions.ReplicaSetConditionType(cond.Type), + Status: api.ConditionStatus(cond.Status), + LastTransitionTime: cond.LastTransitionTime, + Reason: cond.Reason, + Message: cond.Message, + }) + } return nil } @@ -294,6 +305,15 @@ func Convert_extensions_ReplicaSetStatus_to_v1_ReplicationControllerStatus(in *e out.ReadyReplicas = in.ReadyReplicas out.AvailableReplicas = in.AvailableReplicas out.ObservedGeneration = in.ObservedGeneration + for _, cond := range in.Conditions { + out.Conditions = append(out.Conditions, v1.ReplicationControllerCondition{ + Type: v1.ReplicationControllerConditionType(cond.Type), + Status: v1.ConditionStatus(cond.Status), + LastTransitionTime: cond.LastTransitionTime, + Reason: cond.Reason, + Message: cond.Message, + }) + } return nil } diff --git a/pkg/api/v1/conversion_test.go b/pkg/api/v1/conversion_test.go index c86aa045806..a8022c280f9 100644 --- a/pkg/api/v1/conversion_test.go +++ b/pkg/api/v1/conversion_test.go @@ -17,20 +17,27 @@ limitations under the License. package v1_test import ( + "encoding/json" + "math/rand" "net/url" "reflect" "testing" "time" "k8s.io/api/core/v1" + extensionsv1beta1 "k8s.io/api/extensions/v1beta1" apiequality "k8s.io/apimachinery/pkg/api/equality" "k8s.io/apimachinery/pkg/api/resource" + "k8s.io/apimachinery/pkg/api/testing/fuzzer" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/util/diff" "k8s.io/kubernetes/pkg/api" "k8s.io/kubernetes/pkg/api/legacyscheme" + kapitesting "k8s.io/kubernetes/pkg/api/testing" k8s_api_v1 "k8s.io/kubernetes/pkg/api/v1" + "k8s.io/kubernetes/pkg/apis/extensions" + utilpointer "k8s.io/kubernetes/pkg/util/pointer" // enforce that all types are installed _ "k8s.io/kubernetes/pkg/api/testapi" @@ -226,3 +233,115 @@ func TestResourceListConversion(t *testing.T) { } } } + +func TestReplicationControllerConversion(t *testing.T) { + // If we start with a RC, we should always have round-trip fidelity. + inputs := []*v1.ReplicationController{ + { + ObjectMeta: metav1.ObjectMeta{ + Name: "name", + Namespace: "namespace", + }, + Spec: v1.ReplicationControllerSpec{ + Replicas: utilpointer.Int32Ptr(1), + MinReadySeconds: 32, + Selector: map[string]string{"foo": "bar", "bar": "foo"}, + Template: &v1.PodTemplateSpec{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"foo": "bar", "bar": "foo"}, + }, + Spec: v1.PodSpec{ + Containers: []v1.Container{ + { + Name: "container", + Image: "image", + }, + }, + }, + }, + }, + Status: v1.ReplicationControllerStatus{ + Replicas: 1, + FullyLabeledReplicas: 2, + ReadyReplicas: 3, + AvailableReplicas: 4, + ObservedGeneration: 5, + Conditions: []v1.ReplicationControllerCondition{ + { + Type: v1.ReplicationControllerReplicaFailure, + Status: v1.ConditionTrue, + LastTransitionTime: metav1.NewTime(time.Unix(123456789, 0)), + Reason: "Reason", + Message: "Message", + }, + }, + }, + }, + } + + // Add some fuzzed RCs. + apiObjectFuzzer := fuzzer.FuzzerFor(kapitesting.FuzzerFuncs, rand.NewSource(152), legacyscheme.Codecs) + for i := 0; i < 100; i++ { + rc := &v1.ReplicationController{} + apiObjectFuzzer.Fuzz(rc) + // Sometimes the fuzzer decides to leave Spec.Template nil. + // We can't support that because Spec.Template is not a pointer in RS, + // so it will round-trip as non-nil but empty. + if rc.Spec.Template == nil { + rc.Spec.Template = &v1.PodTemplateSpec{} + } + // Sometimes the fuzzer decides to insert an empty label key. + // This doesn't round-trip properly because it's invalid. + if rc.Spec.Selector != nil { + delete(rc.Spec.Selector, "") + } + inputs = append(inputs, rc) + } + + // Round-trip the input RCs before converting to RS. + for i := range inputs { + inputs[i] = roundTrip(t, inputs[i]).(*v1.ReplicationController) + } + + for _, in := range inputs { + rs := &extensions.ReplicaSet{} + // Use in.DeepCopy() to avoid sharing pointers with `in`. + if err := k8s_api_v1.Convert_v1_ReplicationController_to_extensions_ReplicaSet(in.DeepCopy(), rs, nil); err != nil { + t.Errorf("can't convert RC to RS: %v", err) + continue + } + // Round-trip RS before converting back to RC. + rs = roundTripRS(t, rs) + out := &v1.ReplicationController{} + if err := k8s_api_v1.Convert_extensions_ReplicaSet_to_v1_ReplicationController(rs, out, nil); err != nil { + t.Errorf("can't convert RS to RC: %v", err) + continue + } + if !apiequality.Semantic.DeepEqual(in, out) { + instr, _ := json.MarshalIndent(in, "", " ") + outstr, _ := json.MarshalIndent(out, "", " ") + t.Errorf("RC-RS conversion round-trip failed:\nin:\n%s\nout:\n%s", instr, outstr) + } + } +} + +func roundTripRS(t *testing.T, rs *extensions.ReplicaSet) *extensions.ReplicaSet { + codec := legacyscheme.Codecs.LegacyCodec(extensionsv1beta1.SchemeGroupVersion) + data, err := runtime.Encode(codec, rs) + if err != nil { + t.Errorf("%v\n %#v", err, rs) + return nil + } + obj2, err := runtime.Decode(codec, data) + if err != nil { + t.Errorf("%v\nData: %s\nSource: %#v", err, string(data), rs) + return nil + } + obj3 := &extensions.ReplicaSet{} + err = legacyscheme.Scheme.Convert(obj2, obj3, nil) + if err != nil { + t.Errorf("%v\nSource: %#v", err, obj2) + return nil + } + return obj3 +} diff --git a/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/BUILD b/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/BUILD index 7354f5e2fa1..4a96c3f948c 100644 --- a/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/BUILD +++ b/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/BUILD @@ -93,3 +93,13 @@ filegroup( srcs = ["generated.proto"], visibility = ["//visibility:public"], ) + +go_test( + name = "go_default_xtest", + srcs = ["conversion_test.go"], + importpath = "k8s.io/apimachinery/pkg/apis/meta/v1_test", + deps = [ + "//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library", + "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", + ], +) diff --git a/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/conversion.go b/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/conversion.go index 7049c9a33ba..a96f38ee21e 100644 --- a/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/conversion.go +++ b/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/conversion.go @@ -252,7 +252,6 @@ func Convert_map_to_unversioned_LabelSelector(in *map[string]string, out *LabelS if in == nil { return nil } - out = new(LabelSelector) for labelKey, labelValue := range *in { AddLabelToSelector(out, labelKey, labelValue) } diff --git a/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/conversion_test.go b/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/conversion_test.go new file mode 100644 index 00000000000..bc591584ef5 --- /dev/null +++ b/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/conversion_test.go @@ -0,0 +1,49 @@ +/* +Copyright 2017 The Kubernetes Authors. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1_test + +import ( + "testing" + + apiequality "k8s.io/apimachinery/pkg/api/equality" + "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestMapToLabelSelectorRoundTrip(t *testing.T) { + // We should be able to round-trip a map-only selector through LabelSelector. + inputs := []map[string]string{ + nil, + {}, + {"one": "foo"}, + {"one": "foo", "two": "bar"}, + } + for _, in := range inputs { + ls := &v1.LabelSelector{} + if err := v1.Convert_map_to_unversioned_LabelSelector(&in, ls, nil); err != nil { + t.Errorf("Convert_map_to_unversioned_LabelSelector(%#v): %v", in, err) + continue + } + out := map[string]string{} + if err := v1.Convert_unversioned_LabelSelector_to_map(ls, &out, nil); err != nil { + t.Errorf("Convert_unversioned_LabelSelector_to_map(%#v): %v", ls, err) + continue + } + if !apiequality.Semantic.DeepEqual(in, out) { + t.Errorf("map-selector conversion round-trip failed: got %v; want %v", out, in) + } + } +}