From 84d29fc8fa4e174dd0e17f20fa4b37668e3ea1a5 Mon Sep 17 00:00:00 2001 From: "Dr. Stefan Schimanski" Date: Tue, 27 Aug 2019 08:07:57 +0200 Subject: [PATCH] apiextensions: set 'metadata.generation: 1' during read from etcd if not set --- .../pkg/apiserver/customresource_handler.go | 6 ++++++ .../schema/objectmeta/coerce_test.go | 9 +++++++- .../test/integration/objectmeta_test.go | 21 +++++++++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go index 842c4cc7cf4..d72dc1014c5 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/customresource_handler.go @@ -1020,6 +1020,7 @@ func (t crdConversionRESTOptionsGetter) GetRESTOptions(resource schema.GroupReso d := schemaCoercingDecoder{delegate: ret.StorageConfig.Codec, validator: unstructuredSchemaCoercer{ // drop invalid fields while decoding old CRs (before we haven't had any ObjectMeta validation) dropInvalidMetadata: true, + repairGeneration: true, structuralSchemas: t.structuralSchemas, structuralSchemaGK: t.structuralSchemaGK, preserveUnknownFields: t.preserveUnknownFields, @@ -1120,6 +1121,7 @@ func (v schemaCoercingConverter) ConvertFieldLabel(gvk schema.GroupVersionKind, // - generic pruning of unknown fields following a structural schema. type unstructuredSchemaCoercer struct { dropInvalidMetadata bool + repairGeneration bool structuralSchemas map[string]*structuralschema.Structural structuralSchemaGK schema.GroupKind @@ -1154,6 +1156,10 @@ func (v *unstructuredSchemaCoercer) apply(u *unstructured.Unstructured) error { if err := schemaobjectmeta.Coerce(nil, u.Object, v.structuralSchemas[gv.Version], false, v.dropInvalidMetadata); err != nil { return err } + // fixup missing generation in very old CRs + if v.repairGeneration && objectMeta.Generation == 0 { + objectMeta.Generation = 1 + } } // restore meta fields, starting clean diff --git a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/objectmeta/coerce_test.go b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/objectmeta/coerce_test.go index fcbb9fde0f6..8f844752d43 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/objectmeta/coerce_test.go +++ b/staging/src/k8s.io/apiextensions-apiserver/pkg/apiserver/schema/objectmeta/coerce_test.go @@ -167,6 +167,7 @@ func TestGetObjectMetaNils(t *testing.T) { "apiVersion": "v1", "metadata": map[string]interface{}{ "generateName": nil, + "generation": nil, "labels": map[string]interface{}{ "foo": nil, }, @@ -179,7 +180,10 @@ func TestGetObjectMetaNils(t *testing.T) { t.Fatal(err) } if o.GenerateName != "" { - t.Errorf("expected null json value to be read as \"\" string, but got: %q", o.GenerateName) + t.Errorf("expected null json generateName value to be read as \"\" string, but got: %q", o.GenerateName) + } + if o.Generation != 0 { + t.Errorf("expected null json generation value to be read as zero, but got: %q", o.Generation) } if got, expected := o.Labels, map[string]string{"foo": ""}; !reflect.DeepEqual(got, expected) { t.Errorf("unexpected labels, expected=%#v, got=%#v", expected, got) @@ -198,6 +202,9 @@ func TestGetObjectMetaNils(t *testing.T) { if got, expected := o.GenerateName, pod.ObjectMeta.GenerateName; got != expected { t.Errorf("expected generatedName to be %q, got %q", expected, got) } + if got, expected := o.Generation, pod.ObjectMeta.Generation; got != expected { + t.Errorf("expected generation to be %q, got %q", expected, got) + } if got, expected := o.Labels, pod.ObjectMeta.Labels; !reflect.DeepEqual(got, expected) { t.Errorf("expected labels to be %v, got %v", expected, got) } diff --git a/staging/src/k8s.io/apiextensions-apiserver/test/integration/objectmeta_test.go b/staging/src/k8s.io/apiextensions-apiserver/test/integration/objectmeta_test.go index 63577889deb..1ef3bd788a6 100644 --- a/staging/src/k8s.io/apiextensions-apiserver/test/integration/objectmeta_test.go +++ b/staging/src/k8s.io/apiextensions-apiserver/test/integration/objectmeta_test.go @@ -62,6 +62,7 @@ func TestPostInvalidObjectMeta(t *testing.T) { obj := fixtures.NewNoxuInstance("default", "foo") unstructured.SetNestedField(obj.UnstructuredContent(), int64(42), "metadata", "unknown") + unstructured.SetNestedField(obj.UnstructuredContent(), nil, "metadata", "generation") unstructured.SetNestedField(obj.UnstructuredContent(), map[string]interface{}{"foo": int64(42), "bar": "abc"}, "metadata", "labels") _, err = instantiateCustomResource(t, obj, noxuResourceClient, noxuDefinition) if err == nil { @@ -86,6 +87,14 @@ func TestPostInvalidObjectMeta(t *testing.T) { } else if found { t.Errorf("unexpected metadata.unknown=%#v: expected this to be pruned", unknown) } + + if generation, found, err := unstructured.NestedInt64(obj.UnstructuredContent(), "metadata", "generation"); err != nil { + t.Errorf("unexpected error getting metadata.generation: %v", err) + } else if !found { + t.Errorf("expected metadata.generation=1: got: %d", generation) + } else if generation != 1 { + t.Errorf("unexpected metadata.generation=%d: expected this to be set to 1", generation) + } } func TestInvalidObjectMetaInStorage(t *testing.T) { @@ -150,6 +159,8 @@ func TestInvalidObjectMetaInStorage(t *testing.T) { original := fixtures.NewNoxuInstance("default", "foo") unstructured.SetNestedField(original.UnstructuredContent(), int64(42), "metadata", "unknown") + unstructured.SetNestedField(original.UnstructuredContent(), nil, "metadata", "generation") + unstructured.SetNestedField(original.UnstructuredContent(), map[string]interface{}{"foo": int64(42), "bar": "abc"}, "metadata", "annotations") unstructured.SetNestedField(original.UnstructuredContent(), map[string]interface{}{"invalid": "x y"}, "metadata", "labels") unstructured.SetNestedField(original.UnstructuredContent(), int64(42), "embedded", "metadata", "unknown") @@ -193,6 +204,16 @@ func TestInvalidObjectMetaInStorage(t *testing.T) { t.Errorf("Unexpected to find embedded.metadata.unknown=%#v", unknown) } + t.Logf("Checking that metadata.generation=1") + + if generation, found, err := unstructured.NestedInt64(obj.UnstructuredContent(), "metadata", "generation"); err != nil { + t.Errorf("unexpected error getting metadata.generation: %v", err) + } else if !found { + t.Errorf("expected metadata.generation=1: got: %d", generation) + } else if generation != 1 { + t.Errorf("unexpected metadata.generation=%d: expected this to be set to 1", generation) + } + t.Logf("Checking that ObjectMeta is pruned from wrongly-typed annotations") if annotations, found, err := unstructured.NestedStringMap(obj.UnstructuredContent(), "metadata", "annotations"); err != nil {