Skip to content

Commit 92b66c5

Browse files
authored
Merge pull request kubernetes#75151 from jennybuckley/fix-conversion-bug
Fix apply conversion bug
2 parents faaec7f + 2fdc7e1 commit 92b66c5

File tree

4 files changed

+129
-11
lines changed

4 files changed

+129
-11
lines changed

staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/fieldmanager.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ func NewCRDFieldManager(objectConverter runtime.ObjectConvertor, objectDefaulter
7272
groupVersion: gv,
7373
hubVersion: hub,
7474
updater: merge.Updater{
75-
Converter: internal.NewVersionConverter(internal.DeducedTypeConverter{}, objectConverter, hub),
75+
Converter: internal.NewCRDVersionConverter(internal.DeducedTypeConverter{}, objectConverter, hub),
7676
},
7777
}
7878
}

staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/versionconverter.go

Lines changed: 23 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ limitations under the License.
1717
package internal
1818

1919
import (
20-
"fmt"
21-
2220
"k8s.io/apimachinery/pkg/runtime"
2321
"k8s.io/apimachinery/pkg/runtime/schema"
2422
"sigs.k8s.io/structured-merge-diff/fieldpath"
@@ -31,7 +29,7 @@ import (
3129
type versionConverter struct {
3230
typeConverter TypeConverter
3331
objectConvertor runtime.ObjectConvertor
34-
hubVersion schema.GroupVersion
32+
hubGetter func(from schema.GroupVersion) schema.GroupVersion
3533
}
3634

3735
var _ merge.Converter = &versionConverter{}
@@ -41,7 +39,23 @@ func NewVersionConverter(t TypeConverter, o runtime.ObjectConvertor, h schema.Gr
4139
return &versionConverter{
4240
typeConverter: t,
4341
objectConvertor: o,
44-
hubVersion: h,
42+
hubGetter: func(from schema.GroupVersion) schema.GroupVersion {
43+
return schema.GroupVersion{
44+
Group: from.Group,
45+
Version: h.Version,
46+
}
47+
},
48+
}
49+
}
50+
51+
// NewCRDVersionConverter builds a VersionConverter for CRDs from a TypeConverter and an ObjectConvertor.
52+
func NewCRDVersionConverter(t TypeConverter, o runtime.ObjectConvertor, h schema.GroupVersion) merge.Converter {
53+
return &versionConverter{
54+
typeConverter: t,
55+
objectConvertor: o,
56+
hubGetter: func(from schema.GroupVersion) schema.GroupVersion {
57+
return h
58+
},
4559
}
4660
}
4761

@@ -60,22 +74,21 @@ func (v *versionConverter) Convert(object typed.TypedValue, version fieldpath.AP
6074
}
6175

6276
// If attempting to convert to the same version as we already have, just return it.
63-
if objectToConvert.GetObjectKind().GroupVersionKind().GroupVersion() == groupVersion {
77+
fromVersion := objectToConvert.GetObjectKind().GroupVersionKind().GroupVersion()
78+
if fromVersion == groupVersion {
6479
return object, nil
6580
}
6681

6782
// Convert to internal
68-
internalObject, err := v.objectConvertor.ConvertToVersion(objectToConvert, v.hubVersion)
83+
internalObject, err := v.objectConvertor.ConvertToVersion(objectToConvert, v.hubGetter(fromVersion))
6984
if err != nil {
70-
return object, fmt.Errorf("failed to convert object (%v to %v): %v",
71-
objectToConvert.GetObjectKind().GroupVersionKind(), v.hubVersion, err)
85+
return object, err
7286
}
7387

7488
// Convert the object into the target version
7589
convertedObject, err := v.objectConvertor.ConvertToVersion(internalObject, groupVersion)
7690
if err != nil {
77-
return object, fmt.Errorf("failed to convert object (%v to %v): %v",
78-
internalObject.GetObjectKind().GroupVersionKind(), groupVersion, err)
91+
return object, err
7992
}
8093

8194
// Convert the object back to a smd typed value and return it.

test/integration/apiserver/apply/BUILD

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ go_test(
1515
"//pkg/master:go_default_library",
1616
"//staging/src/k8s.io/apimachinery/pkg/api/errors:go_default_library",
1717
"//staging/src/k8s.io/apimachinery/pkg/api/meta:go_default_library",
18+
"//staging/src/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
1819
"//staging/src/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
1920
"//staging/src/k8s.io/apimachinery/pkg/types:go_default_library",
2021
"//staging/src/k8s.io/apiserver/pkg/features:go_default_library",

test/integration/apiserver/apply/apply_test.go

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import (
2424

2525
"k8s.io/apimachinery/pkg/api/errors"
2626
"k8s.io/apimachinery/pkg/api/meta"
27+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
2728
"k8s.io/apimachinery/pkg/runtime/schema"
2829
"k8s.io/apimachinery/pkg/types"
2930
genericfeatures "k8s.io/apiserver/pkg/features"
@@ -438,3 +439,106 @@ func TestApplyRequiresFieldManager(t *testing.T) {
438439
t.Fatalf("Apply failed to create with fieldManager: %v", err)
439440
}
440441
}
442+
443+
// TestApplyRemoveContainerPort removes a container port from a deployment
444+
func TestApplyRemoveContainerPort(t *testing.T) {
445+
defer utilfeaturetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, genericfeatures.ServerSideApply, true)()
446+
447+
_, client, closeFn := setup(t)
448+
defer closeFn()
449+
450+
obj := []byte(`{
451+
"apiVersion": "extensions/v1beta1",
452+
"kind": "Deployment",
453+
"metadata": {
454+
"name": "deployment",
455+
"labels": {"app": "nginx"}
456+
},
457+
"spec": {
458+
"replicas": 3,
459+
"selector": {
460+
"matchLabels": {
461+
"app": "nginx"
462+
}
463+
},
464+
"template": {
465+
"metadata": {
466+
"labels": {
467+
"app": "nginx"
468+
}
469+
},
470+
"spec": {
471+
"containers": [{
472+
"name": "nginx",
473+
"image": "nginx:latest",
474+
"ports": [{
475+
"containerPort": 80,
476+
"protocol": "TCP"
477+
}]
478+
}]
479+
}
480+
}
481+
}
482+
}`)
483+
484+
_, err := client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
485+
AbsPath("/apis/extensions/v1beta1").
486+
Namespace("default").
487+
Resource("deployments").
488+
Name("deployment").
489+
Param("fieldManager", "apply_test").
490+
Body(obj).Do().Get()
491+
if err != nil {
492+
t.Fatalf("Failed to create object using Apply patch: %v", err)
493+
}
494+
495+
obj = []byte(`{
496+
"apiVersion": "apps/v1",
497+
"kind": "Deployment",
498+
"metadata": {
499+
"name": "deployment",
500+
"labels": {"app": "nginx"}
501+
},
502+
"spec": {
503+
"replicas": 3,
504+
"selector": {
505+
"matchLabels": {
506+
"app": "nginx"
507+
}
508+
},
509+
"template": {
510+
"metadata": {
511+
"labels": {
512+
"app": "nginx"
513+
}
514+
},
515+
"spec": {
516+
"containers": [{
517+
"name": "nginx",
518+
"image": "nginx:latest"
519+
}]
520+
}
521+
}
522+
}
523+
}`)
524+
525+
_, err = client.CoreV1().RESTClient().Patch(types.ApplyPatchType).
526+
AbsPath("/apis/apps/v1").
527+
Namespace("default").
528+
Resource("deployments").
529+
Name("deployment").
530+
Param("fieldManager", "apply_test").
531+
Body(obj).Do().Get()
532+
if err != nil {
533+
t.Fatalf("Failed to remove container port using Apply patch: %v", err)
534+
}
535+
536+
deployment, err := client.AppsV1().Deployments("default").Get("deployment", metav1.GetOptions{})
537+
if err != nil {
538+
t.Fatalf("Failed to retrieve object: %v", err)
539+
}
540+
541+
if len(deployment.Spec.Template.Spec.Containers[0].Ports) > 0 {
542+
t.Fatalf("Expected no container ports but got: %v", deployment.Spec.Template.Spec.Containers[0].Ports)
543+
}
544+
}

0 commit comments

Comments
 (0)