From ff4e1f3592bfd51aea3018ed7e0a8579e5c0eea1 Mon Sep 17 00:00:00 2001 From: Antoine Pelisse Date: Wed, 21 Aug 2019 16:13:53 -0700 Subject: [PATCH] Add new FieldsType to clarify the format of Fields --- .../api/apitesting/roundtrip/compatibility.go | 4 +- .../pkg/api/validation/objectmeta.go | 2 + .../pkg/apis/meta/fuzzer/fuzzer.go | 2 +- .../pkg/apis/meta/v1/fields_proto.go | 88 ------------------- .../apimachinery/pkg/apis/meta/v1/helpers.go | 8 +- .../apimachinery/pkg/apis/meta/v1/types.go | 16 ++-- .../pkg/apis/meta/v1/validation/validation.go | 15 ++++ .../meta/v1/validation/validation_test.go | 52 +++++++++++ .../handlers/fieldmanager/internal/fields.go | 6 +- .../fieldmanager/internal/fields_test.go | 6 +- .../fieldmanager/internal/managedfields.go | 12 ++- .../internal/managedfields_test.go | 22 +++-- .../integration/apiserver/apply/apply_test.go | 13 +-- 13 files changed, 120 insertions(+), 126 deletions(-) delete mode 100644 staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/fields_proto.go diff --git a/staging/src/k8s.io/apimachinery/pkg/api/apitesting/roundtrip/compatibility.go b/staging/src/k8s.io/apimachinery/pkg/api/apitesting/roundtrip/compatibility.go index 33abeba55af..5b5cd3d8158 100644 --- a/staging/src/k8s.io/apimachinery/pkg/api/apitesting/roundtrip/compatibility.go +++ b/staging/src/k8s.io/apimachinery/pkg/api/apitesting/roundtrip/compatibility.go @@ -231,8 +231,8 @@ func CompatibilityTestFuzzer(scheme *runtime.Scheme, fuzzFuncs []interface{}) *f func(f *[]metav1.ManagedFieldsEntry, c fuzz.Continue) { field := metav1.ManagedFieldsEntry{} c.Fuzz(&field) - if field.Fields != nil { - field.Fields.Raw = []byte("{}") + if field.FieldsV1 != nil { + field.FieldsV1.Raw = []byte("{}") } *f = []metav1.ManagedFieldsEntry{field} }, diff --git a/staging/src/k8s.io/apimachinery/pkg/api/validation/objectmeta.go b/staging/src/k8s.io/apimachinery/pkg/api/validation/objectmeta.go index cf668c7c816..90f566b14f5 100644 --- a/staging/src/k8s.io/apimachinery/pkg/api/validation/objectmeta.go +++ b/staging/src/k8s.io/apimachinery/pkg/api/validation/objectmeta.go @@ -184,6 +184,7 @@ func ValidateObjectMetaAccessor(meta metav1.Object, requiresNamespace bool, name allErrs = append(allErrs, ValidateAnnotations(meta.GetAnnotations(), fldPath.Child("annotations"))...) allErrs = append(allErrs, ValidateOwnerReferences(meta.GetOwnerReferences(), fldPath.Child("ownerReferences"))...) allErrs = append(allErrs, ValidateFinalizers(meta.GetFinalizers(), fldPath.Child("finalizers"))...) + allErrs = append(allErrs, v1validation.ValidateManagedFields(meta.GetManagedFields(), fldPath.Child("managedFields"))...) return allErrs } @@ -256,6 +257,7 @@ func ValidateObjectMetaAccessorUpdate(newMeta, oldMeta metav1.Object, fldPath *f allErrs = append(allErrs, v1validation.ValidateLabels(newMeta.GetLabels(), fldPath.Child("labels"))...) allErrs = append(allErrs, ValidateAnnotations(newMeta.GetAnnotations(), fldPath.Child("annotations"))...) allErrs = append(allErrs, ValidateOwnerReferences(newMeta.GetOwnerReferences(), fldPath.Child("ownerReferences"))...) + allErrs = append(allErrs, v1validation.ValidateManagedFields(newMeta.GetManagedFields(), fldPath.Child("managedFields"))...) return allErrs } diff --git a/staging/src/k8s.io/apimachinery/pkg/apis/meta/fuzzer/fuzzer.go b/staging/src/k8s.io/apimachinery/pkg/apis/meta/fuzzer/fuzzer.go index 6b93a138887..68cf673b764 100644 --- a/staging/src/k8s.io/apimachinery/pkg/apis/meta/fuzzer/fuzzer.go +++ b/staging/src/k8s.io/apimachinery/pkg/apis/meta/fuzzer/fuzzer.go @@ -272,7 +272,7 @@ func v1FuzzerFuncs(codecs runtimeserializer.CodecFactory) []interface{} { }, func(j *metav1.ManagedFieldsEntry, c fuzz.Continue) { c.FuzzNoCustom(j) - j.Fields = nil + j.FieldsV1 = nil }, } } diff --git a/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/fields_proto.go b/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/fields_proto.go deleted file mode 100644 index d403e76a41c..00000000000 --- a/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/fields_proto.go +++ /dev/null @@ -1,88 +0,0 @@ -/* -Copyright 2019 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 v1 - -import ( - "encoding/json" -) - -// Fields is declared in types.go - -// ProtoFields is a struct that is equivalent to Fields, but intended for -// protobuf marshalling/unmarshalling. It is generated into a serialization -// that matches Fields. Do not use in Go structs. -type ProtoFields struct { - // Map is the representation used in the alpha version of this API - Map map[string]Fields `json:"-" protobuf:"bytes,1,rep,name=map"` - - // Raw is the underlying serialization of this object. - Raw []byte `json:"-" protobuf:"bytes,2,opt,name=raw"` -} - -// ProtoFields returns the Fields as a new ProtoFields value. -func (m *Fields) ProtoFields() *ProtoFields { - if m == nil { - return &ProtoFields{} - } - return &ProtoFields{ - Raw: m.Raw, - } -} - -// Size implements the protobuf marshalling interface. -func (m *Fields) Size() (n int) { - return m.ProtoFields().Size() -} - -// Unmarshal implements the protobuf marshalling interface. -func (m *Fields) Unmarshal(data []byte) error { - if len(data) == 0 { - return nil - } - p := ProtoFields{} - if err := p.Unmarshal(data); err != nil { - return err - } - if len(p.Map) == 0 { - return json.Unmarshal(p.Raw, &m) - } - b, err := json.Marshal(&p.Map) - if err != nil { - return err - } - return json.Unmarshal(b, &m) -} - -// Marshal implements the protobuf marshaling interface. -func (m *Fields) Marshal() (data []byte, err error) { - return m.ProtoFields().Marshal() -} - -// MarshalTo implements the protobuf marshaling interface. -func (m *Fields) MarshalTo(data []byte) (int, error) { - return m.ProtoFields().MarshalTo(data) -} - -// MarshalToSizedBuffer implements the protobuf reverse marshaling interface. -func (m *Fields) MarshalToSizedBuffer(data []byte) (int, error) { - return m.ProtoFields().MarshalToSizedBuffer(data) -} - -// String implements the protobuf goproto_stringer interface. -func (m *Fields) String() string { - return m.ProtoFields().String() -} diff --git a/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/helpers.go b/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/helpers.go index 843cd3b15bf..ec016fd3c8d 100644 --- a/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/helpers.go +++ b/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/helpers.go @@ -258,7 +258,7 @@ func ResetObjectMetaForStatus(meta, existingMeta Object) { // MarshalJSON implements json.Marshaler // MarshalJSON may get called on pointers or values, so implement MarshalJSON on value. // http://stackoverflow.com/questions/21390979/custom-marshaljson-never-gets-called-in-go -func (f Fields) MarshalJSON() ([]byte, error) { +func (f FieldsV1) MarshalJSON() ([]byte, error) { if f.Raw == nil { return []byte("null"), nil } @@ -266,7 +266,7 @@ func (f Fields) MarshalJSON() ([]byte, error) { } // UnmarshalJSON implements json.Unmarshaler -func (f *Fields) UnmarshalJSON(b []byte) error { +func (f *FieldsV1) UnmarshalJSON(b []byte) error { if f == nil { return errors.New("metav1.Fields: UnmarshalJSON on nil pointer") } @@ -276,5 +276,5 @@ func (f *Fields) UnmarshalJSON(b []byte) error { return nil } -var _ json.Marshaler = Fields{} -var _ json.Unmarshaler = &Fields{} +var _ json.Marshaler = FieldsV1{} +var _ json.Unmarshaler = &FieldsV1{} diff --git a/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/types.go b/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/types.go index 8fe7b47e973..a2a611f2af1 100644 --- a/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/types.go +++ b/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/types.go @@ -1105,9 +1105,12 @@ type ManagedFieldsEntry struct { // Time is timestamp of when these fields were set. It should always be empty if Operation is 'Apply' // +optional Time *Time `json:"time,omitempty" protobuf:"bytes,4,opt,name=time"` - // Fields identifies a set of fields. + // FieldsType is the discriminator for the different fields format and version. + // There is currently only one possible value: "FieldsV1" + FieldsType string `json:"fieldsType,omitempty" protobuf:"bytes,6,opt,name=fieldsType"` + // FieldsV1 holds the first JSON version format as described in the "FieldsV1" type. // +optional - Fields *Fields `json:"fields,omitempty" protobuf:"bytes,5,opt,name=fields,casttype=Fields"` + FieldsV1 *FieldsV1 `json:"fieldsV1,omitempty" protobuf:"bytes,7,opt,name=fieldsV1"` } // ManagedFieldsOperationType is the type of operation which lead to a ManagedFieldsEntry being created. @@ -1118,7 +1121,7 @@ const ( ManagedFieldsOperationUpdate ManagedFieldsOperationType = "Update" ) -// Fields stores a set of fields in a data structure like a Trie. +// FieldsV1 stores a set of fields in a data structure like a Trie, in JSON format. // // Each key is either a '.' representing the field itself, and will always map to an empty set, // or a string representing a sub-field or item. The string will follow one of these four formats: @@ -1129,12 +1132,9 @@ const ( // If a key maps to an empty Fields value, the field that key represents is part of the set. // // The exact format is defined in sigs.k8s.io/structured-merge-diff -// +protobuf.options.marshal=false -// +protobuf.as=ProtoFields -// +protobuf.options.(gogoproto.goproto_stringer)=false -type Fields struct { +type FieldsV1 struct { // Raw is the underlying serialization of this object. - Raw []byte `json:"-" protobuf:"-"` + Raw []byte `json:"-" protobuf:"bytes,1,opt,name=Raw"` } // TODO: Table does not generate to protobuf because of the interface{} - fix protobuf diff --git a/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/validation/validation.go b/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/validation/validation.go index eeb73999f9b..2743793dde2 100644 --- a/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/validation/validation.go +++ b/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/validation/validation.go @@ -169,3 +169,18 @@ func ValidateTableOptions(opts *metav1.TableOptions) field.ErrorList { } return allErrs } + +func ValidateManagedFields(fieldsList []metav1.ManagedFieldsEntry, fldPath *field.Path) field.ErrorList { + var allErrs field.ErrorList + for _, fields := range fieldsList { + switch fields.Operation { + case metav1.ManagedFieldsOperationApply, metav1.ManagedFieldsOperationUpdate: + default: + allErrs = append(allErrs, field.Invalid(fldPath.Child("operation"), fields.Operation, "must be `Apply` or `Update`")) + } + if fields.FieldsType != "FieldsV1" { + allErrs = append(allErrs, field.Invalid(fldPath.Child("fieldsType"), fields.FieldsType, "must be `FieldsV1`")) + } + } + return allErrs +} diff --git a/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/validation/validation_test.go b/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/validation/validation_test.go index 454d52b18bd..30d6289f8ee 100644 --- a/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/validation/validation_test.go +++ b/staging/src/k8s.io/apimachinery/pkg/apis/meta/v1/validation/validation_test.go @@ -241,3 +241,55 @@ func TestValidateFieldManagerInvalid(t *testing.T) { }) } } + +func TestValidateMangedFieldsInvalid(t *testing.T) { + tests := []metav1.ManagedFieldsEntry{ + { + Operation: metav1.ManagedFieldsOperationUpdate, + // FieldsType is missing + }, + { + Operation: metav1.ManagedFieldsOperationUpdate, + FieldsType: "RandomVersion", + }, + { + Operation: "RandomOperation", + FieldsType: "FieldsV1", + }, + { + // Operation is missing + FieldsType: "FieldsV1", + }, + } + + for _, test := range tests { + t.Run(fmt.Sprintf("%#v", test), func(t *testing.T) { + errs := ValidateManagedFields([]metav1.ManagedFieldsEntry{test}, field.NewPath("managedFields")) + if len(errs) == 0 { + t.Errorf("Validation should have failed") + } + }) + } +} + +func TestValidateMangedFieldsValid(t *testing.T) { + tests := []metav1.ManagedFieldsEntry{ + { + Operation: metav1.ManagedFieldsOperationUpdate, + FieldsType: "FieldsV1", + }, + { + Operation: metav1.ManagedFieldsOperationApply, + FieldsType: "FieldsV1", + }, + } + + for _, test := range tests { + t.Run(fmt.Sprintf("%#v", test), func(t *testing.T) { + err := ValidateManagedFields([]metav1.ManagedFieldsEntry{test}, field.NewPath("managedFields")) + if err != nil { + t.Errorf("Validation failed: %v", err) + } + }) + } +} diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/fields.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/fields.go index e59820c619d..5af15ea90dd 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/fields.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/fields.go @@ -26,7 +26,7 @@ import ( // EmptyFields represents a set with no paths // It looks like metav1.Fields{Raw: []byte("{}")} -var EmptyFields metav1.Fields = func() metav1.Fields { +var EmptyFields metav1.FieldsV1 = func() metav1.FieldsV1 { f, err := SetToFields(*fieldpath.NewSet()) if err != nil { panic("should never happen") @@ -35,13 +35,13 @@ var EmptyFields metav1.Fields = func() metav1.Fields { }() // FieldsToSet creates a set paths from an input trie of fields -func FieldsToSet(f metav1.Fields) (s fieldpath.Set, err error) { +func FieldsToSet(f metav1.FieldsV1) (s fieldpath.Set, err error) { err = s.FromJSON(bytes.NewReader(f.Raw)) return s, err } // SetToFields creates a trie of fields from an input set of paths -func SetToFields(s fieldpath.Set) (f metav1.Fields, err error) { +func SetToFields(s fieldpath.Set) (f metav1.FieldsV1, err error) { f.Raw, err = s.ToJSON() return f, err } diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/fields_test.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/fields_test.go index a93a1ffb42a..a11822facc3 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/fields_test.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/fields_test.go @@ -29,7 +29,7 @@ import ( // TestFieldsRoundTrip tests that a fields trie can be round tripped as a path set func TestFieldsRoundTrip(t *testing.T) { - tests := []metav1.Fields{ + tests := []metav1.FieldsV1{ { Raw: []byte(`{"f:metadata":{"f:name":{},".":{}}}`), }, @@ -54,11 +54,11 @@ func TestFieldsRoundTrip(t *testing.T) { // TestFieldsToSetError tests that errors are picked up by FieldsToSet func TestFieldsToSetError(t *testing.T) { tests := []struct { - fields metav1.Fields + fields metav1.FieldsV1 errString string }{ { - fields: metav1.Fields{ + fields: metav1.FieldsV1{ Raw: []byte(`{"k:{invalid json}":{"f:name":{},".":{}}}`), }, errString: "ReadObjectCB", diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/managedfields.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/managedfields.go index c71a47b0005..1f0eb6d3a44 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/managedfields.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/managedfields.go @@ -101,8 +101,11 @@ func decodeManagedFields(encodedManagedFields []metav1.ManagedFieldsEntry) (mana func BuildManagerIdentifier(encodedManager *metav1.ManagedFieldsEntry) (manager string, err error) { encodedManagerCopy := *encodedManager + // Never include fields type in the manager identifier + encodedManagerCopy.FieldsType = "" + // Never include the fields in the manager identifier - encodedManagerCopy.Fields = nil + encodedManagerCopy.FieldsV1 = nil // Never include the time in the manager identifier encodedManagerCopy.Time = nil @@ -124,8 +127,8 @@ func BuildManagerIdentifier(encodedManager *metav1.ManagedFieldsEntry) (manager func decodeVersionedSet(encodedVersionedSet *metav1.ManagedFieldsEntry) (versionedSet fieldpath.VersionedSet, err error) { fields := EmptyFields - if encodedVersionedSet.Fields != nil { - fields = *encodedVersionedSet.Fields + if encodedVersionedSet.FieldsV1 != nil { + fields = *encodedVersionedSet.FieldsV1 } set, err := FieldsToSet(fields) if err != nil { @@ -191,11 +194,12 @@ func encodeManagerVersionedSet(manager string, versionedSet fieldpath.VersionedS if versionedSet.Applied() { encodedVersionedSet.Operation = metav1.ManagedFieldsOperationApply } + encodedVersionedSet.FieldsType = "FieldsV1" fields, err := SetToFields(*versionedSet.Set()) if err != nil { return nil, fmt.Errorf("error encoding set: %v", err) } - encodedVersionedSet.Fields = &fields + encodedVersionedSet.FieldsV1 = &fields return encodedVersionedSet, nil } diff --git a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/managedfields_test.go b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/managedfields_test.go index 703a10ab816..79e6c146491 100644 --- a/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/managedfields_test.go +++ b/staging/src/k8s.io/apiserver/pkg/endpoints/handlers/fieldmanager/internal/managedfields_test.go @@ -32,7 +32,8 @@ import ( func TestRoundTripManagedFields(t *testing.T) { tests := []string{ `- apiVersion: v1 - fields: + fieldsType: FieldsV1 + fieldsV1: v:3: f:alsoPi: {} v:3.1415: @@ -43,7 +44,8 @@ func TestRoundTripManagedFields(t *testing.T) { operation: Update time: "2001-02-03T04:05:06Z" - apiVersion: v1beta1 - fields: + fieldsType: FieldsV1 + fieldsV1: i:5: f:i: {} manager: foo @@ -51,7 +53,8 @@ func TestRoundTripManagedFields(t *testing.T) { time: "2011-12-13T14:15:16Z" `, `- apiVersion: v1 - fields: + fieldsType: FieldsV1 + fieldsV1: f:spec: f:containers: k:{"name":"c"}: @@ -61,7 +64,8 @@ func TestRoundTripManagedFields(t *testing.T) { operation: Apply `, `- apiVersion: v1 - fields: + fieldsType: FieldsV1 + fieldsV1: f:apiVersion: {} f:kind: {} f:metadata: @@ -90,7 +94,8 @@ func TestRoundTripManagedFields(t *testing.T) { operation: Update `, `- apiVersion: v1 - fields: + fieldsType: FieldsV1 + fieldsV1: f:allowVolumeExpansion: {} f:apiVersion: {} f:kind: {} @@ -106,7 +111,8 @@ func TestRoundTripManagedFields(t *testing.T) { operation: Apply `, `- apiVersion: v1 - fields: + fieldsType: FieldsV1 + fieldsV1: f:apiVersion: {} f:kind: {} f:metadata: @@ -163,7 +169,7 @@ func TestBuildManagerIdentifier(t *testing.T) { { managedFieldsEntry: ` apiVersion: v1 -fields: +fieldsV1: f:apiVersion: {} manager: foo operation: Update @@ -174,7 +180,7 @@ time: "2001-02-03T04:05:06Z" { managedFieldsEntry: ` apiVersion: v1 -fields: +fieldsV1: f:apiVersion: {} manager: foo operation: Apply diff --git a/test/integration/apiserver/apply/apply_test.go b/test/integration/apiserver/apply/apply_test.go index 456d410b201..ef9001e72c5 100644 --- a/test/integration/apiserver/apply/apply_test.go +++ b/test/integration/apiserver/apply/apply_test.go @@ -456,7 +456,8 @@ func TestApplyManagedFields(t *testing.T) { "operation": "Apply", "apiVersion": "v1", "time": "` + accessor.GetManagedFields()[0].Time.UTC().Format(time.RFC3339) + `", - "fields": { + "fieldsType": "FieldsV1", + "fieldsV1": { "f:metadata": { "f:labels": { "f:test-label": {} @@ -469,7 +470,8 @@ func TestApplyManagedFields(t *testing.T) { "operation": "Update", "apiVersion": "v1", "time": "` + accessor.GetManagedFields()[1].Time.UTC().Format(time.RFC3339) + `", - "fields": { + "fieldsType": "FieldsV1", + "fieldsV1": { "f:data": { "f:key": {}, "f:new-key": {} @@ -684,7 +686,7 @@ func TestApplyRemoveContainerPort(t *testing.T) { } if len(deployment.Spec.Template.Spec.Containers[0].Ports) > 0 { - t.Fatalf("Expected no container ports but got: %v", deployment.Spec.Template.Spec.Containers[0].Ports) + t.Fatalf("Expected no container ports but got: %v, object: \n%#v", deployment.Spec.Template.Spec.Containers[0].Ports, deployment) } } @@ -804,7 +806,7 @@ func TestApplyConvertsManagedFieldsVersion(t *testing.T) { "manager": "sidecar_controller", "operation": "Apply", "apiVersion": "extensions/v1beta1", - "fields": { + "fieldsV1": { "f:metadata": { "f:labels": { "f:sidecar_version": {} @@ -918,7 +920,8 @@ func TestApplyConvertsManagedFieldsVersion(t *testing.T) { Operation: metav1.ManagedFieldsOperationApply, APIVersion: "apps/v1", Time: actual.Time, - Fields: &metav1.Fields{ + FieldsType: "FieldsV1", + FieldsV1: &metav1.FieldsV1{ Raw: []byte(`{"f:metadata":{"f:labels":{"f:sidecar_version":{}}},"f:spec":{"f:template":{"f:spec":{"f:containers":{"k:{\"name\":\"sidecar\"}":{".":{},"f:image":{},"f:name":{}}}}}}}`), }, }