apiextensions: set 'metadata.generation: 1' during read from etcd if not set

This commit is contained in:
Dr. Stefan Schimanski 2019-08-27 08:07:57 +02:00
parent 533daf6624
commit 84d29fc8fa
3 changed files with 35 additions and 1 deletions

View File

@ -1020,6 +1020,7 @@ func (t crdConversionRESTOptionsGetter) GetRESTOptions(resource schema.GroupReso
d := schemaCoercingDecoder{delegate: ret.StorageConfig.Codec, validator: unstructuredSchemaCoercer{ d := schemaCoercingDecoder{delegate: ret.StorageConfig.Codec, validator: unstructuredSchemaCoercer{
// drop invalid fields while decoding old CRs (before we haven't had any ObjectMeta validation) // drop invalid fields while decoding old CRs (before we haven't had any ObjectMeta validation)
dropInvalidMetadata: true, dropInvalidMetadata: true,
repairGeneration: true,
structuralSchemas: t.structuralSchemas, structuralSchemas: t.structuralSchemas,
structuralSchemaGK: t.structuralSchemaGK, structuralSchemaGK: t.structuralSchemaGK,
preserveUnknownFields: t.preserveUnknownFields, preserveUnknownFields: t.preserveUnknownFields,
@ -1120,6 +1121,7 @@ func (v schemaCoercingConverter) ConvertFieldLabel(gvk schema.GroupVersionKind,
// - generic pruning of unknown fields following a structural schema. // - generic pruning of unknown fields following a structural schema.
type unstructuredSchemaCoercer struct { type unstructuredSchemaCoercer struct {
dropInvalidMetadata bool dropInvalidMetadata bool
repairGeneration bool
structuralSchemas map[string]*structuralschema.Structural structuralSchemas map[string]*structuralschema.Structural
structuralSchemaGK schema.GroupKind 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 { if err := schemaobjectmeta.Coerce(nil, u.Object, v.structuralSchemas[gv.Version], false, v.dropInvalidMetadata); err != nil {
return err return err
} }
// fixup missing generation in very old CRs
if v.repairGeneration && objectMeta.Generation == 0 {
objectMeta.Generation = 1
}
} }
// restore meta fields, starting clean // restore meta fields, starting clean

View File

@ -167,6 +167,7 @@ func TestGetObjectMetaNils(t *testing.T) {
"apiVersion": "v1", "apiVersion": "v1",
"metadata": map[string]interface{}{ "metadata": map[string]interface{}{
"generateName": nil, "generateName": nil,
"generation": nil,
"labels": map[string]interface{}{ "labels": map[string]interface{}{
"foo": nil, "foo": nil,
}, },
@ -179,7 +180,10 @@ func TestGetObjectMetaNils(t *testing.T) {
t.Fatal(err) t.Fatal(err)
} }
if o.GenerateName != "" { 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) { if got, expected := o.Labels, map[string]string{"foo": ""}; !reflect.DeepEqual(got, expected) {
t.Errorf("unexpected labels, expected=%#v, got=%#v", expected, got) 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 { if got, expected := o.GenerateName, pod.ObjectMeta.GenerateName; got != expected {
t.Errorf("expected generatedName to be %q, got %q", expected, got) 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) { if got, expected := o.Labels, pod.ObjectMeta.Labels; !reflect.DeepEqual(got, expected) {
t.Errorf("expected labels to be %v, got %v", expected, got) t.Errorf("expected labels to be %v, got %v", expected, got)
} }

View File

@ -62,6 +62,7 @@ func TestPostInvalidObjectMeta(t *testing.T) {
obj := fixtures.NewNoxuInstance("default", "foo") obj := fixtures.NewNoxuInstance("default", "foo")
unstructured.SetNestedField(obj.UnstructuredContent(), int64(42), "metadata", "unknown") 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") unstructured.SetNestedField(obj.UnstructuredContent(), map[string]interface{}{"foo": int64(42), "bar": "abc"}, "metadata", "labels")
_, err = instantiateCustomResource(t, obj, noxuResourceClient, noxuDefinition) _, err = instantiateCustomResource(t, obj, noxuResourceClient, noxuDefinition)
if err == nil { if err == nil {
@ -86,6 +87,14 @@ func TestPostInvalidObjectMeta(t *testing.T) {
} else if found { } else if found {
t.Errorf("unexpected metadata.unknown=%#v: expected this to be pruned", unknown) 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) { func TestInvalidObjectMetaInStorage(t *testing.T) {
@ -150,6 +159,8 @@ func TestInvalidObjectMetaInStorage(t *testing.T) {
original := fixtures.NewNoxuInstance("default", "foo") original := fixtures.NewNoxuInstance("default", "foo")
unstructured.SetNestedField(original.UnstructuredContent(), int64(42), "metadata", "unknown") 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{}{"foo": int64(42), "bar": "abc"}, "metadata", "annotations")
unstructured.SetNestedField(original.UnstructuredContent(), map[string]interface{}{"invalid": "x y"}, "metadata", "labels") unstructured.SetNestedField(original.UnstructuredContent(), map[string]interface{}{"invalid": "x y"}, "metadata", "labels")
unstructured.SetNestedField(original.UnstructuredContent(), int64(42), "embedded", "metadata", "unknown") 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.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") t.Logf("Checking that ObjectMeta is pruned from wrongly-typed annotations")
if annotations, found, err := unstructured.NestedStringMap(obj.UnstructuredContent(), "metadata", "annotations"); err != nil { if annotations, found, err := unstructured.NestedStringMap(obj.UnstructuredContent(), "metadata", "annotations"); err != nil {