diff --git a/Godeps/Godeps.json b/Godeps/Godeps.json index d2a49cccfb3..a058d3158c6 100644 --- a/Godeps/Godeps.json +++ b/Godeps/Godeps.json @@ -4025,6 +4025,10 @@ "ImportPath": "sigs.k8s.io/structured-merge-diff/fieldpath", "Rev": "01332b709372d8d83b4d7653b30a1961680a47d5" }, + { + "ImportPath": "sigs.k8s.io/structured-merge-diff/merge", + "Rev": "01332b709372d8d83b4d7653b30a1961680a47d5" + }, { "ImportPath": "sigs.k8s.io/structured-merge-diff/schema", "Rev": "01332b709372d8d83b4d7653b30a1961680a47d5" diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/apply/versionconverter.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/apply/versionconverter.go new file mode 100644 index 00000000000..72e83bfce2a --- /dev/null +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/apply/versionconverter.go @@ -0,0 +1,67 @@ +/* +Copyright 2018 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 apply + +import ( + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + + "sigs.k8s.io/structured-merge-diff/fieldpath" + "sigs.k8s.io/structured-merge-diff/merge" + "sigs.k8s.io/structured-merge-diff/typed" +) + +// versionConverter is an implementation of +// sigs.k8s.io/structured-merge-diff/merge.Converter +type versionConverter struct { + typeConverter TypeConverter + objectConvertor runtime.ObjectConvertor +} + +var _ merge.Converter = &versionConverter{} + +// NewVersionConverter builds a VersionConverter from a TypeConverter and an ObjectConvertor. +func NewVersionConverter(t TypeConverter, o runtime.ObjectConvertor) merge.Converter { + return &versionConverter{ + typeConverter: t, + objectConvertor: o, + } +} + +// Convert implements sigs.k8s.io/structured-merge-diff/merge.Converter +func (v *versionConverter) Convert(object typed.TypedValue, version fieldpath.APIVersion) (typed.TypedValue, error) { + // Convert the smd typed value to a kubernetes object. + objectToConvert, err := v.typeConverter.TypedToObject(object) + if err != nil { + return object, err + } + + // Parse the target groupVersion. + groupVersion, err := schema.ParseGroupVersion(string(version)) + if err != nil { + return object, err + } + + // Convert the object into the target version + convertedObject, err := v.objectConvertor.ConvertToVersion(objectToConvert, groupVersion) + if err != nil { + return object, err + } + + // Convert the object back to a smd typed value and return it. + return v.typeConverter.ObjectToTyped(convertedObject) +} diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/apply/versionconverter_test.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/apply/versionconverter_test.go new file mode 100644 index 00000000000..0d5a92e711f --- /dev/null +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/apply/versionconverter_test.go @@ -0,0 +1,115 @@ +/* +Copyright 2018 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 apply + +import ( + "fmt" + "path/filepath" + "reflect" + "testing" + + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + + "k8s.io/kube-openapi/pkg/util/proto" + prototesting "k8s.io/kube-openapi/pkg/util/proto/testing" + "sigs.k8s.io/structured-merge-diff/fieldpath" +) + +var fakeSchema = prototesting.Fake{ + Path: filepath.Join( + "..", "..", "..", "..", "..", "..", "..", "..", + "api", "openapi-spec", "swagger.json"), +} + +// TestVersionConverter tests the version converter +func TestVersionConverter(t *testing.T) { + d, err := fakeSchema.OpenAPISchema() + if err != nil { + t.Fatalf("Failed to parse OpenAPI schema: %v", err) + } + m, err := proto.NewOpenAPIData(d) + if err != nil { + t.Fatalf("Failed to build OpenAPI models: %v", err) + } + tc, err := NewTypeConverter(m) + if err != nil { + t.Fatalf("Failed to build TypeConverter: %v", err) + } + oc := fakeObjectConvertor{ + gvkForVersion("v1beta1"): objForGroupVersion("apps/v1beta1"), + gvkForVersion("v1"): objForGroupVersion("apps/v1"), + } + vc := NewVersionConverter(tc, oc) + + input, err := tc.ObjectToTyped(objForGroupVersion("apps/v1beta1")) + if err != nil { + t.Fatalf("error creating converting input object to a typed value: %v", err) + } + expected := objForGroupVersion("apps/v1") + output, err := vc.Convert(input, fieldpath.APIVersion("apps/v1")) + if err != nil { + t.Fatalf("expected err to be nil but got %v", err) + } + actual, err := tc.TypedToObject(output) + if err != nil { + t.Fatalf("error converting output typed value to an object %v", err) + } + + if !reflect.DeepEqual(expected, actual) { + t.Fatalf("expected to get %v but got %v", expected, actual) + } +} + +func gvkForVersion(v string) schema.GroupVersionKind { + return schema.GroupVersionKind{ + Group: "apps", + Version: v, + Kind: "Deployment", + } +} + +func objForGroupVersion(gv string) runtime.Object { + return &unstructured.Unstructured{ + Object: map[string]interface{}{ + "apiVersion": gv, + "kind": "Deployment", + }, + } +} + +type fakeObjectConvertor map[schema.GroupVersionKind]runtime.Object + +var _ runtime.ObjectConvertor = fakeObjectConvertor{} + +func (c fakeObjectConvertor) ConvertToVersion(_ runtime.Object, gv runtime.GroupVersioner) (runtime.Object, error) { + allKinds := make([]schema.GroupVersionKind, 0) + for kind := range c { + allKinds = append(allKinds, kind) + } + gvk, _ := gv.KindForGroupVersionKinds(allKinds) + return c[gvk], nil +} + +func (fakeObjectConvertor) Convert(_, _, _ interface{}) error { + return fmt.Errorf("function not implemented") +} + +func (fakeObjectConvertor) ConvertFieldLabel(_ schema.GroupVersionKind, _, _ string) (string, string, error) { + return "", "", fmt.Errorf("function not implemented") +}