mirror of
				https://github.com/optim-enterprises-bv/kubernetes.git
				synced 2025-10-31 18:28:13 +00:00 
			
		
		
		
	Merge pull request #53164 from enisoc/rc-rs-conversion
Automatic merge from submit-queue. If you want to cherry-pick this change to another branch, please follow the instructions <a href="https://github.com/kubernetes/community/blob/master/contributors/devel/cherry-picks.md">here</a>. Fix RC/RS conversion This fixes some round-trip information loss when representing an RC as an RS. I want to use these conversions in #49429 to eliminate the maintenance burden of duplicated RC code. @kubernetes/sig-apps-pr-reviews
This commit is contained in:
		| @@ -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 | ||||
|   | ||||
| @@ -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", | ||||
|   | ||||
| @@ -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 | ||||
| } | ||||
|  | ||||
|   | ||||
| @@ -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 | ||||
| } | ||||
|   | ||||
| @@ -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", | ||||
|     ], | ||||
| ) | ||||
|   | ||||
| @@ -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) | ||||
| 	} | ||||
|   | ||||
| @@ -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) | ||||
| 		} | ||||
| 	} | ||||
| } | ||||
		Reference in New Issue
	
	Block a user
	 Kubernetes Submit Queue
					Kubernetes Submit Queue