Skip to content

Commit aaec4e2

Browse files
author
Kubernetes Submit Queue
authored
Merge pull request kubernetes#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 kubernetes#49429 to eliminate the maintenance burden of duplicated RC code. @kubernetes/sig-apps-pr-reviews
2 parents b3a9b80 + c137738 commit aaec4e2

File tree

7 files changed

+203
-2
lines changed

7 files changed

+203
-2
lines changed

hack/.golint_failures

-1
Original file line numberDiff line numberDiff line change
@@ -541,7 +541,6 @@ staging/src/k8s.io/apimachinery/pkg/apimachinery/announced
541541
staging/src/k8s.io/apimachinery/pkg/apimachinery/registered
542542
staging/src/k8s.io/apimachinery/pkg/apis/meta/fuzzer
543543
staging/src/k8s.io/apimachinery/pkg/apis/meta/internalversion
544-
staging/src/k8s.io/apimachinery/pkg/apis/meta/v1
545544
staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured
546545
staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/validation
547546
staging/src/k8s.io/apimachinery/pkg/apis/meta/v1alpha1

pkg/api/v1/BUILD

+5
Original file line numberDiff line numberDiff line change
@@ -46,9 +46,14 @@ go_test(
4646
"//pkg/api:go_default_library",
4747
"//pkg/api/legacyscheme:go_default_library",
4848
"//pkg/api/testapi:go_default_library",
49+
"//pkg/api/testing:go_default_library",
50+
"//pkg/apis/extensions:go_default_library",
51+
"//pkg/util/pointer:go_default_library",
4952
"//vendor/k8s.io/api/core/v1:go_default_library",
53+
"//vendor/k8s.io/api/extensions/v1beta1:go_default_library",
5054
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
5155
"//vendor/k8s.io/apimachinery/pkg/api/resource:go_default_library",
56+
"//vendor/k8s.io/apimachinery/pkg/api/testing/fuzzer:go_default_library",
5257
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
5358
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
5459
"//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library",

pkg/api/v1/conversion.go

+20
Original file line numberDiff line numberDiff line change
@@ -235,7 +235,9 @@ func Convert_v1_ReplicationController_to_extensions_ReplicaSet(in *v1.Replicatio
235235

236236
func Convert_v1_ReplicationControllerSpec_to_extensions_ReplicaSetSpec(in *v1.ReplicationControllerSpec, out *extensions.ReplicaSetSpec, s conversion.Scope) error {
237237
out.Replicas = *in.Replicas
238+
out.MinReadySeconds = in.MinReadySeconds
238239
if in.Selector != nil {
240+
out.Selector = new(metav1.LabelSelector)
239241
metav1.Convert_map_to_unversioned_LabelSelector(&in.Selector, out.Selector, s)
240242
}
241243
if in.Template != nil {
@@ -252,6 +254,15 @@ func Convert_v1_ReplicationControllerStatus_to_extensions_ReplicaSetStatus(in *v
252254
out.ReadyReplicas = in.ReadyReplicas
253255
out.AvailableReplicas = in.AvailableReplicas
254256
out.ObservedGeneration = in.ObservedGeneration
257+
for _, cond := range in.Conditions {
258+
out.Conditions = append(out.Conditions, extensions.ReplicaSetCondition{
259+
Type: extensions.ReplicaSetConditionType(cond.Type),
260+
Status: api.ConditionStatus(cond.Status),
261+
LastTransitionTime: cond.LastTransitionTime,
262+
Reason: cond.Reason,
263+
Message: cond.Message,
264+
})
265+
}
255266
return nil
256267
}
257268

@@ -294,6 +305,15 @@ func Convert_extensions_ReplicaSetStatus_to_v1_ReplicationControllerStatus(in *e
294305
out.ReadyReplicas = in.ReadyReplicas
295306
out.AvailableReplicas = in.AvailableReplicas
296307
out.ObservedGeneration = in.ObservedGeneration
308+
for _, cond := range in.Conditions {
309+
out.Conditions = append(out.Conditions, v1.ReplicationControllerCondition{
310+
Type: v1.ReplicationControllerConditionType(cond.Type),
311+
Status: v1.ConditionStatus(cond.Status),
312+
LastTransitionTime: cond.LastTransitionTime,
313+
Reason: cond.Reason,
314+
Message: cond.Message,
315+
})
316+
}
297317
return nil
298318
}
299319

pkg/api/v1/conversion_test.go

+119
Original file line numberDiff line numberDiff line change
@@ -17,20 +17,27 @@ limitations under the License.
1717
package v1_test
1818

1919
import (
20+
"encoding/json"
21+
"math/rand"
2022
"net/url"
2123
"reflect"
2224
"testing"
2325
"time"
2426

2527
"k8s.io/api/core/v1"
28+
extensionsv1beta1 "k8s.io/api/extensions/v1beta1"
2629
apiequality "k8s.io/apimachinery/pkg/api/equality"
2730
"k8s.io/apimachinery/pkg/api/resource"
31+
"k8s.io/apimachinery/pkg/api/testing/fuzzer"
2832
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2933
"k8s.io/apimachinery/pkg/runtime"
3034
"k8s.io/apimachinery/pkg/util/diff"
3135
"k8s.io/kubernetes/pkg/api"
3236
"k8s.io/kubernetes/pkg/api/legacyscheme"
37+
kapitesting "k8s.io/kubernetes/pkg/api/testing"
3338
k8s_api_v1 "k8s.io/kubernetes/pkg/api/v1"
39+
"k8s.io/kubernetes/pkg/apis/extensions"
40+
utilpointer "k8s.io/kubernetes/pkg/util/pointer"
3441

3542
// enforce that all types are installed
3643
_ "k8s.io/kubernetes/pkg/api/testapi"
@@ -226,3 +233,115 @@ func TestResourceListConversion(t *testing.T) {
226233
}
227234
}
228235
}
236+
237+
func TestReplicationControllerConversion(t *testing.T) {
238+
// If we start with a RC, we should always have round-trip fidelity.
239+
inputs := []*v1.ReplicationController{
240+
{
241+
ObjectMeta: metav1.ObjectMeta{
242+
Name: "name",
243+
Namespace: "namespace",
244+
},
245+
Spec: v1.ReplicationControllerSpec{
246+
Replicas: utilpointer.Int32Ptr(1),
247+
MinReadySeconds: 32,
248+
Selector: map[string]string{"foo": "bar", "bar": "foo"},
249+
Template: &v1.PodTemplateSpec{
250+
ObjectMeta: metav1.ObjectMeta{
251+
Labels: map[string]string{"foo": "bar", "bar": "foo"},
252+
},
253+
Spec: v1.PodSpec{
254+
Containers: []v1.Container{
255+
{
256+
Name: "container",
257+
Image: "image",
258+
},
259+
},
260+
},
261+
},
262+
},
263+
Status: v1.ReplicationControllerStatus{
264+
Replicas: 1,
265+
FullyLabeledReplicas: 2,
266+
ReadyReplicas: 3,
267+
AvailableReplicas: 4,
268+
ObservedGeneration: 5,
269+
Conditions: []v1.ReplicationControllerCondition{
270+
{
271+
Type: v1.ReplicationControllerReplicaFailure,
272+
Status: v1.ConditionTrue,
273+
LastTransitionTime: metav1.NewTime(time.Unix(123456789, 0)),
274+
Reason: "Reason",
275+
Message: "Message",
276+
},
277+
},
278+
},
279+
},
280+
}
281+
282+
// Add some fuzzed RCs.
283+
apiObjectFuzzer := fuzzer.FuzzerFor(kapitesting.FuzzerFuncs, rand.NewSource(152), legacyscheme.Codecs)
284+
for i := 0; i < 100; i++ {
285+
rc := &v1.ReplicationController{}
286+
apiObjectFuzzer.Fuzz(rc)
287+
// Sometimes the fuzzer decides to leave Spec.Template nil.
288+
// We can't support that because Spec.Template is not a pointer in RS,
289+
// so it will round-trip as non-nil but empty.
290+
if rc.Spec.Template == nil {
291+
rc.Spec.Template = &v1.PodTemplateSpec{}
292+
}
293+
// Sometimes the fuzzer decides to insert an empty label key.
294+
// This doesn't round-trip properly because it's invalid.
295+
if rc.Spec.Selector != nil {
296+
delete(rc.Spec.Selector, "")
297+
}
298+
inputs = append(inputs, rc)
299+
}
300+
301+
// Round-trip the input RCs before converting to RS.
302+
for i := range inputs {
303+
inputs[i] = roundTrip(t, inputs[i]).(*v1.ReplicationController)
304+
}
305+
306+
for _, in := range inputs {
307+
rs := &extensions.ReplicaSet{}
308+
// Use in.DeepCopy() to avoid sharing pointers with `in`.
309+
if err := k8s_api_v1.Convert_v1_ReplicationController_to_extensions_ReplicaSet(in.DeepCopy(), rs, nil); err != nil {
310+
t.Errorf("can't convert RC to RS: %v", err)
311+
continue
312+
}
313+
// Round-trip RS before converting back to RC.
314+
rs = roundTripRS(t, rs)
315+
out := &v1.ReplicationController{}
316+
if err := k8s_api_v1.Convert_extensions_ReplicaSet_to_v1_ReplicationController(rs, out, nil); err != nil {
317+
t.Errorf("can't convert RS to RC: %v", err)
318+
continue
319+
}
320+
if !apiequality.Semantic.DeepEqual(in, out) {
321+
instr, _ := json.MarshalIndent(in, "", " ")
322+
outstr, _ := json.MarshalIndent(out, "", " ")
323+
t.Errorf("RC-RS conversion round-trip failed:\nin:\n%s\nout:\n%s", instr, outstr)
324+
}
325+
}
326+
}
327+
328+
func roundTripRS(t *testing.T, rs *extensions.ReplicaSet) *extensions.ReplicaSet {
329+
codec := legacyscheme.Codecs.LegacyCodec(extensionsv1beta1.SchemeGroupVersion)
330+
data, err := runtime.Encode(codec, rs)
331+
if err != nil {
332+
t.Errorf("%v\n %#v", err, rs)
333+
return nil
334+
}
335+
obj2, err := runtime.Decode(codec, data)
336+
if err != nil {
337+
t.Errorf("%v\nData: %s\nSource: %#v", err, string(data), rs)
338+
return nil
339+
}
340+
obj3 := &extensions.ReplicaSet{}
341+
err = legacyscheme.Scheme.Convert(obj2, obj3, nil)
342+
if err != nil {
343+
t.Errorf("%v\nSource: %#v", err, obj2)
344+
return nil
345+
}
346+
return obj3
347+
}

staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/BUILD

+10
Original file line numberDiff line numberDiff line change
@@ -93,3 +93,13 @@ filegroup(
9393
srcs = ["generated.proto"],
9494
visibility = ["//visibility:public"],
9595
)
96+
97+
go_test(
98+
name = "go_default_xtest",
99+
srcs = ["conversion_test.go"],
100+
importpath = "k8s.io/apimachinery/pkg/apis/meta/v1_test",
101+
deps = [
102+
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
103+
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
104+
],
105+
)

staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/conversion.go

-1
Original file line numberDiff line numberDiff line change
@@ -252,7 +252,6 @@ func Convert_map_to_unversioned_LabelSelector(in *map[string]string, out *LabelS
252252
if in == nil {
253253
return nil
254254
}
255-
out = new(LabelSelector)
256255
for labelKey, labelValue := range *in {
257256
AddLabelToSelector(out, labelKey, labelValue)
258257
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/*
2+
Copyright 2017 The Kubernetes Authors.
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
*/
16+
17+
package v1_test
18+
19+
import (
20+
"testing"
21+
22+
apiequality "k8s.io/apimachinery/pkg/api/equality"
23+
"k8s.io/apimachinery/pkg/apis/meta/v1"
24+
)
25+
26+
func TestMapToLabelSelectorRoundTrip(t *testing.T) {
27+
// We should be able to round-trip a map-only selector through LabelSelector.
28+
inputs := []map[string]string{
29+
nil,
30+
{},
31+
{"one": "foo"},
32+
{"one": "foo", "two": "bar"},
33+
}
34+
for _, in := range inputs {
35+
ls := &v1.LabelSelector{}
36+
if err := v1.Convert_map_to_unversioned_LabelSelector(&in, ls, nil); err != nil {
37+
t.Errorf("Convert_map_to_unversioned_LabelSelector(%#v): %v", in, err)
38+
continue
39+
}
40+
out := map[string]string{}
41+
if err := v1.Convert_unversioned_LabelSelector_to_map(ls, &out, nil); err != nil {
42+
t.Errorf("Convert_unversioned_LabelSelector_to_map(%#v): %v", ls, err)
43+
continue
44+
}
45+
if !apiequality.Semantic.DeepEqual(in, out) {
46+
t.Errorf("map-selector conversion round-trip failed: got %v; want %v", out, in)
47+
}
48+
}
49+
}

0 commit comments

Comments
 (0)