diff --git a/pkg/api/BUILD b/pkg/api/BUILD index 27b3008b026..396aa330330 100644 --- a/pkg/api/BUILD +++ b/pkg/api/BUILD @@ -84,11 +84,9 @@ go_test( "//pkg/apis/batch/v2alpha1:go_default_library", "//pkg/apis/extensions:go_default_library", "//pkg/apis/extensions/v1beta1:go_default_library", - "//vendor:github.com/davecgh/go-spew/spew", "//vendor:github.com/gogo/protobuf/proto", "//vendor:github.com/golang/protobuf/proto", "//vendor:github.com/google/gofuzz", - "//vendor:github.com/spf13/pflag", "//vendor:github.com/ugorji/go/codec", "//vendor:k8s.io/apimachinery/pkg/api/equality", "//vendor:k8s.io/apimachinery/pkg/api/meta", diff --git a/pkg/api/copy_test.go b/pkg/api/copy_test.go index 15f2916c9d5..9d6ad4feb8c 100644 --- a/pkg/api/copy_test.go +++ b/pkg/api/copy_test.go @@ -33,7 +33,7 @@ import ( ) func TestDeepCopyApiObjects(t *testing.T) { - for i := 0; i < *fuzzIters; i++ { + for i := 0; i < *apitesting.FuzzIters; i++ { for _, version := range []schema.GroupVersion{testapi.Default.InternalGroupVersion(), api.Registry.GroupOrDie(api.GroupName).GroupVersion} { f := apitesting.FuzzerFor(kapitesting.FuzzerFuncs(t, api.Codecs), rand.NewSource(rand.Int63())) for kind := range api.Scheme.KnownTypes(version) { @@ -82,7 +82,7 @@ func doDeepCopyTest(t *testing.T, kind schema.GroupVersionKind, f *fuzz.Fuzzer) } func TestDeepCopySingleType(t *testing.T) { - for i := 0; i < *fuzzIters; i++ { + for i := 0; i < *apitesting.FuzzIters; i++ { for _, version := range []schema.GroupVersion{testapi.Default.InternalGroupVersion(), api.Registry.GroupOrDie(api.GroupName).GroupVersion} { f := apitesting.FuzzerFor(kapitesting.FuzzerFuncs(t, api.Codecs), rand.NewSource(rand.Int63())) doDeepCopyTest(t, version.WithKind("Pod"), f) diff --git a/pkg/api/defaulting_test.go b/pkg/api/defaulting_test.go index c066d5030b0..b3f9e5d95bc 100644 --- a/pkg/api/defaulting_test.go +++ b/pkg/api/defaulting_test.go @@ -24,6 +24,7 @@ import ( "github.com/google/gofuzz" + apitesting "k8s.io/apimachinery/pkg/api/testing" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/schema" @@ -149,7 +150,7 @@ func TestDefaulting(t *testing.T) { iter := 0 changedOnce := false for { - if iter > *fuzzIters { + if iter > *apitesting.FuzzIters { if !expectedChanged || changedOnce { break } diff --git a/pkg/api/serialization_proto_test.go b/pkg/api/serialization_proto_test.go index c48f6324107..523653d16ab 100644 --- a/pkg/api/serialization_proto_test.go +++ b/pkg/api/serialization_proto_test.go @@ -30,29 +30,13 @@ import ( "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/serializer/protobuf" "k8s.io/apimachinery/pkg/util/diff" - "k8s.io/apimachinery/pkg/util/sets" "k8s.io/kubernetes/pkg/api" - "k8s.io/kubernetes/pkg/api/testapi" kapitesting "k8s.io/kubernetes/pkg/api/testing" "k8s.io/kubernetes/pkg/api/v1" _ "k8s.io/kubernetes/pkg/apis/extensions" _ "k8s.io/kubernetes/pkg/apis/extensions/v1beta1" ) -var nonProtobaleAPIGroups = sets.NewString( - "kubeadm.k8s.io", -) - -func init() { - codecsToTest = append(codecsToTest, func(version schema.GroupVersion, item runtime.Object) (runtime.Codec, bool, error) { - if nonProtobaleAPIGroups.Has(version.Group) { - return nil, false, nil - } - s := protobuf.NewSerializer(api.Scheme, api.Scheme, "application/arbitrary.content.type") - return api.Codecs.CodecForVersions(s, s, testapi.ExternalGroupVersions(), nil), true, nil - }) -} - func TestUniversalDeserializer(t *testing.T) { expected := &v1.Pod{ObjectMeta: metav1.ObjectMeta{Name: "test"}} d := api.Codecs.UniversalDeserializer() diff --git a/pkg/api/serialization_test.go b/pkg/api/serialization_test.go index 13b088dfad1..a644dedf01d 100644 --- a/pkg/api/serialization_test.go +++ b/pkg/api/serialization_test.go @@ -20,16 +20,13 @@ import ( "bytes" "encoding/hex" "encoding/json" - "fmt" "io/ioutil" "math/rand" "reflect" "strings" "testing" - "github.com/davecgh/go-spew/spew" "github.com/golang/protobuf/proto" - flag "github.com/spf13/pflag" "github.com/ugorji/go/codec" apiequality "k8s.io/apimachinery/pkg/api/equality" @@ -51,17 +48,6 @@ import ( "k8s.io/kubernetes/pkg/apis/extensions/v1beta1" ) -var fuzzIters = flag.Int("fuzz-iters", 20, "How many fuzzing iterations to do.") - -// codecsToTest is a list of functions that yield the codecs to use to test a -// particular runtime object. -var codecsToTest = []func(version schema.GroupVersion, item runtime.Object) (runtime.Codec, bool, error){ - func(version schema.GroupVersion, item runtime.Object) (runtime.Codec, bool, error) { - c, err := testapi.GetCodecForObject(item) - return c, true, err - }, -} - // fuzzInternalObject fuzzes an arbitrary runtime object using the appropriate // fuzzer registered with the apitesting package. func fuzzInternalObject(t *testing.T, forVersion schema.GroupVersion, item runtime.Object, seed int64) runtime.Object { @@ -152,26 +138,12 @@ func TestSetControllerConversion(t *testing.T) { func TestSpecificKind(t *testing.T) { // Uncomment the following line to enable logging of which conversions // api.scheme.Log(t) + internalGVK := schema.GroupVersionKind{Group: "extensions", Version: runtime.APIVersionInternal, Kind: "DaemonSet"} - kind := "DaemonSet" - for i := 0; i < *fuzzIters; i++ { - doRoundTripTest(testapi.Groups["extensions"], kind, t) - if t.Failed() { - break - } - } -} + seed := rand.Int63() + fuzzer := apitesting.FuzzerFor(kapitesting.FuzzerFuncs(t, api.Codecs), rand.NewSource(seed)) -// TestList applies the round-trip test to the List kind, which may hold -// objects of heterogenous unknown types. -func TestList(t *testing.T) { - kind := "List" - item, err := api.Scheme.New(api.SchemeGroupVersion.WithKind(kind)) - if err != nil { - t.Errorf("Couldn't make a %v? %v", kind, err) - return - } - roundTripSame(t, testapi.Default, item) + apitesting.RoundTripSpecificKind(t, internalGVK, api.Scheme, api.Codecs, fuzzer, nil) } var nonRoundTrippableTypes = sets.NewString( @@ -225,136 +197,19 @@ func TestCommonKindsRegistered(t *testing.T) { } } -var nonInternalRoundTrippableTypes = sets.NewString("List", "ListOptions", "ExportOptions") -var nonRoundTrippableTypesByVersion = map[string][]string{} - // TestRoundTripTypes applies the round-trip test to all round-trippable Kinds // in all of the API groups registered for test in the testapi package. func TestRoundTripTypes(t *testing.T) { - for groupKey, group := range testapi.Groups { - for kind := range group.InternalTypes() { - t.Logf("working on %v in %v", kind, groupKey) - if nonRoundTrippableTypes.Has(kind) { - continue - } - // Try a few times, since runTest uses random values. - for i := 0; i < *fuzzIters; i++ { - doRoundTripTest(group, kind, t) - if t.Failed() { - break - } - } - } - } -} - -func doRoundTripTest(group testapi.TestGroup, kind string, t *testing.T) { - object, err := api.Scheme.New(group.InternalGroupVersion().WithKind(kind)) - if err != nil { - t.Fatalf("Couldn't make a %v? %v", kind, err) - } - if _, err := meta.TypeAccessor(object); err != nil { - t.Fatalf("%q is not a TypeMeta and cannot be tested - add it to nonRoundTrippableTypes: %v", kind, err) - } - if api.Scheme.Recognizes(group.GroupVersion().WithKind(kind)) { - roundTripSame(t, group, object, nonRoundTrippableTypesByVersion[kind]...) - } - if !nonInternalRoundTrippableTypes.Has(kind) && api.Scheme.Recognizes(group.GroupVersion().WithKind(kind)) { - roundTrip(t, group.Codec(), fuzzInternalObject(t, group.InternalGroupVersion(), object, rand.Int63())) - } -} - -// roundTripSame verifies the same source object is tested in all API versions -// yielded by codecsToTest -func roundTripSame(t *testing.T, group testapi.TestGroup, item runtime.Object, except ...string) { - set := sets.NewString(except...) seed := rand.Int63() - fuzzInternalObject(t, group.InternalGroupVersion(), item, seed) + fuzzer := apitesting.FuzzerFor(kapitesting.FuzzerFuncs(t, api.Codecs), rand.NewSource(seed)) - version := *group.GroupVersion() - codecs := []runtime.Codec{} - for _, fn := range codecsToTest { - codec, ok, err := fn(version, item) - if err != nil { - t.Errorf("unable to get codec: %v", err) - return - } - if !ok { - continue - } - codecs = append(codecs, codec) + nonRoundTrippableTypes := map[schema.GroupVersionKind]bool{ + {Group: "componentconfig", Version: runtime.APIVersionInternal, Kind: "KubeletConfiguration"}: true, + {Group: "componentconfig", Version: runtime.APIVersionInternal, Kind: "KubeProxyConfiguration"}: true, + {Group: "componentconfig", Version: runtime.APIVersionInternal, Kind: "KubeSchedulerConfiguration"}: true, } - if !set.Has(version.String()) { - fuzzInternalObject(t, version, item, seed) - for _, codec := range codecs { - roundTrip(t, codec, item) - } - } -} - -// roundTrip applies a single round-trip test to the given runtime object -// using the given codec. The round-trip test ensures that an object can be -// deep-copied and converted from internal -> versioned -> internal without -// loss of data. -func roundTrip(t *testing.T, codec runtime.Codec, item runtime.Object) { - printer := spew.ConfigState{DisableMethods: true} - original := item - - // deep copy the original object - copied, err := api.Scheme.DeepCopy(item) - if err != nil { - panic(fmt.Sprintf("unable to copy: %v", err)) - } - item = copied.(runtime.Object) - name := reflect.TypeOf(item).Elem().Name() - - // encode (serialize) the deep copy using the provided codec - data, err := runtime.Encode(codec, item) - if err != nil { - if runtime.IsNotRegisteredError(err) { - t.Logf("%v: not registered: %v (%s)", name, err, printer.Sprintf("%#v", item)) - } else { - t.Errorf("%v: %v (%s)", name, err, printer.Sprintf("%#v", item)) - } - return - } - - // ensure that the deep copy is equal to the original; neither the deep - // copy or conversion should alter the object - if !apiequality.Semantic.DeepEqual(original, item) { - t.Errorf("0: %v: encode altered the object, diff: %v", name, diff.ObjectReflectDiff(original, item)) - return - } - - // decode (deserialize) the encoded data back into an object - obj2, err := runtime.Decode(codec, data) - if err != nil { - t.Errorf("0: %v: %v\nCodec: %#v\nData: %s\nSource: %#v", name, err, codec, dataAsString(data), printer.Sprintf("%#v", item)) - panic("failed") - } - - // ensure that the object produced from decoding the encoded data is equal - // to the original object - if !apiequality.Semantic.DeepEqual(original, obj2) { - t.Errorf("\n1: %v: diff: %v\nCodec: %#v\nSource:\n\n%#v\n\nEncoded:\n\n%s\n\nFinal:\n\n%#v", name, diff.ObjectReflectDiff(item, obj2), codec, printer.Sprintf("%#v", item), dataAsString(data), printer.Sprintf("%#v", obj2)) - return - } - - // decode the encoded data into a new object (instead of letting the codec - // create a new object) - obj3 := reflect.New(reflect.TypeOf(item).Elem()).Interface().(runtime.Object) - if err := runtime.DecodeInto(codec, data, obj3); err != nil { - t.Errorf("2: %v: %v", name, err) - return - } - - // ensure that the new runtime object is equal to the original after being - // decoded into - if !apiequality.Semantic.DeepEqual(item, obj3) { - t.Errorf("3: %v: diff: %v\nCodec: %#v", name, diff.ObjectReflectDiff(item, obj3), codec) - return - } + apitesting.RoundTripTypes(t, api.Scheme, api.Codecs, fuzzer, nonRoundTrippableTypes) } // TestEncodePtr tests that a pointer to a golang type can be encoded and diff --git a/staging/src/k8s.io/apimachinery/pkg/api/testing/roundtrip.go b/staging/src/k8s.io/apimachinery/pkg/api/testing/roundtrip.go new file mode 100644 index 00000000000..e2a2e6cb9e0 --- /dev/null +++ b/staging/src/k8s.io/apimachinery/pkg/api/testing/roundtrip.go @@ -0,0 +1,246 @@ +/* +Copyright 2017 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 testing + +import ( + "encoding/hex" + "fmt" + "reflect" + "strings" + "testing" + + "github.com/davecgh/go-spew/spew" + "github.com/golang/protobuf/proto" + "github.com/google/gofuzz" + flag "github.com/spf13/pflag" + "k8s.io/apimachinery/pkg/runtime/serializer/protobuf" + + apiequality "k8s.io/apimachinery/pkg/api/equality" + "k8s.io/apimachinery/pkg/api/meta" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + runtimeserializer "k8s.io/apimachinery/pkg/runtime/serializer" + "k8s.io/apimachinery/pkg/util/diff" + "k8s.io/apimachinery/pkg/util/sets" +) + +var FuzzIters = flag.Int("fuzz-iters", 20, "How many fuzzing iterations to do.") + +// nonRoundTrippableTypes are kinds that are effectively reserved across all GroupVersions +// They don't roundtrip +var globalNonRoundTrippableTypes = sets.NewString( + "ExportOptions", + "GetOptions", + // WatchEvent does not include kind and version and can only be deserialized + // implicitly (if the caller expects the specific object). The watch call defines + // the schema by content type, rather than via kind/version included in each + // object. + "WatchEvent", + // ListOptions is now part of the meta group + "ListOptions", + // Delete options is only read in metav1 + "DeleteOptions", +) + +// RoundTripTypes applies the round-trip test to all round-trippable Kinds +// in the scheme. It will skip all the GroupVersionKinds in the skip list. +func RoundTripTypesWithoutProtobuf(t *testing.T, scheme *runtime.Scheme, codecFactory runtimeserializer.CodecFactory, fuzzer *fuzz.Fuzzer, nonRoundTrippableTypes map[schema.GroupVersionKind]bool) { + roundTripTypes(t, scheme, codecFactory, fuzzer, nonRoundTrippableTypes, true) +} + +func RoundTripTypes(t *testing.T, scheme *runtime.Scheme, codecFactory runtimeserializer.CodecFactory, fuzzer *fuzz.Fuzzer, nonRoundTrippableTypes map[schema.GroupVersionKind]bool) { + roundTripTypes(t, scheme, codecFactory, fuzzer, nonRoundTrippableTypes, false) +} + +func roundTripTypes(t *testing.T, scheme *runtime.Scheme, codecFactory runtimeserializer.CodecFactory, fuzzer *fuzz.Fuzzer, nonRoundTrippableTypes map[schema.GroupVersionKind]bool, skipProtobuf bool) { + for _, group := range groupsFromScheme(scheme) { + t.Logf("starting group %q", group) + internalVersion := schema.GroupVersion{Group: group, Version: runtime.APIVersionInternal} + internalKindToGoType := scheme.KnownTypes(internalVersion) + + for kind := range internalKindToGoType { + if globalNonRoundTrippableTypes.Has(kind) { + continue + } + + internalGVK := internalVersion.WithKind(kind) + roundTripSpecificKind(t, internalGVK, scheme, codecFactory, fuzzer, nonRoundTrippableTypes, skipProtobuf) + } + + t.Logf("finished group %q", group) + } +} + +func RoundTripSpecificKindWithoutProtobuf(t *testing.T, internalGVK schema.GroupVersionKind, scheme *runtime.Scheme, codecFactory runtimeserializer.CodecFactory, fuzzer *fuzz.Fuzzer, nonRoundTrippableTypes map[schema.GroupVersionKind]bool) { + roundTripSpecificKind(t, internalGVK, scheme, codecFactory, fuzzer, nonRoundTrippableTypes, true) +} + +func RoundTripSpecificKind(t *testing.T, internalGVK schema.GroupVersionKind, scheme *runtime.Scheme, codecFactory runtimeserializer.CodecFactory, fuzzer *fuzz.Fuzzer, nonRoundTrippableTypes map[schema.GroupVersionKind]bool) { + roundTripSpecificKind(t, internalGVK, scheme, codecFactory, fuzzer, nonRoundTrippableTypes, false) +} + +func roundTripSpecificKind(t *testing.T, internalGVK schema.GroupVersionKind, scheme *runtime.Scheme, codecFactory runtimeserializer.CodecFactory, fuzzer *fuzz.Fuzzer, nonRoundTrippableTypes map[schema.GroupVersionKind]bool, skipProtobuf bool) { + if nonRoundTrippableTypes[internalGVK] { + t.Logf("skipping %v", internalGVK) + return + } + t.Logf("round tripping %v", internalGVK) + + // Try a few times, since runTest uses random values. + for i := 0; i < *FuzzIters; i++ { + roundTripToAllExternalVersions(t, scheme, codecFactory, fuzzer, internalGVK, nonRoundTrippableTypes, skipProtobuf) + if t.Failed() { + break + } + } +} + +// fuzzInternalObject fuzzes an arbitrary runtime object using the appropriate +// fuzzer registered with the apitesting package. +func fuzzInternalObject(t *testing.T, fuzzer *fuzz.Fuzzer, object runtime.Object) runtime.Object { + fuzzer.Fuzz(object) + + j, err := meta.TypeAccessor(object) + if err != nil { + t.Fatalf("Unexpected error %v for %#v", err, object) + } + j.SetKind("") + j.SetAPIVersion("") + + return object +} + +func groupsFromScheme(scheme *runtime.Scheme) []string { + ret := sets.String{} + for gvk := range scheme.AllKnownTypes() { + ret.Insert(gvk.Group) + } + return ret.List() +} + +func roundTripToAllExternalVersions(t *testing.T, scheme *runtime.Scheme, codecFactory runtimeserializer.CodecFactory, fuzzer *fuzz.Fuzzer, internalGVK schema.GroupVersionKind, nonRoundTrippableTypes map[schema.GroupVersionKind]bool, skipProtobuf bool) { + object, err := scheme.New(internalGVK) + if err != nil { + t.Fatalf("Couldn't make a %v? %v", internalGVK, err) + } + if _, err := meta.TypeAccessor(object); err != nil { + t.Fatalf("%q is not a TypeMeta and cannot be tested - add it to nonRoundTrippableInternalTypes: %v", internalGVK, err) + } + + fuzzInternalObject(t, fuzzer, object) + + // find all potential serializations in the scheme. + // TODO fix this up to handle kinds that cross registered with different names. + for externalGVK, externalGoType := range scheme.AllKnownTypes() { + if externalGVK.Version == runtime.APIVersionInternal { + continue + } + if externalGVK.GroupKind() != internalGVK.GroupKind() { + continue + } + if nonRoundTrippableTypes[externalGVK] { + t.Logf("\tskipping %v %v", externalGVK, externalGoType) + continue + } + t.Logf("\tround tripping to %v %v", externalGVK, externalGoType) + + roundTrip(t, scheme, TestCodec(codecFactory, externalGVK.GroupVersion()), object) + + // TODO remove this hack after we're past the intermediate steps + if !skipProtobuf && externalGVK.Group != "kubeadm.k8s.io" { + s := protobuf.NewSerializer(scheme, scheme, "application/arbitrary.content.type") + protobufCodec := codecFactory.CodecForVersions(s, s, externalGVK.GroupVersion(), nil) + roundTrip(t, scheme, protobufCodec, object) + } + } +} + +// roundTrip applies a single round-trip test to the given runtime object +// using the given codec. The round-trip test ensures that an object can be +// deep-copied and converted from internal -> versioned -> internal without +// loss of data. +func roundTrip(t *testing.T, scheme *runtime.Scheme, codec runtime.Codec, object runtime.Object) { + printer := spew.ConfigState{DisableMethods: true} + original := object + + // deep copy the original object + copied, err := scheme.DeepCopy(object) + if err != nil { + panic(fmt.Sprintf("unable to copy: %v", err)) + } + object = copied.(runtime.Object) + name := reflect.TypeOf(object).Elem().Name() + + // encode (serialize) the deep copy using the provided codec + data, err := runtime.Encode(codec, object) + if err != nil { + if runtime.IsNotRegisteredError(err) { + t.Logf("%v: not registered: %v (%s)", name, err, printer.Sprintf("%#v", object)) + } else { + t.Errorf("%v: %v (%s)", name, err, printer.Sprintf("%#v", object)) + } + return + } + + // ensure that the deep copy is equal to the original; neither the deep + // copy or conversion should alter the object + // TODO eliminate this global + if !apiequality.Semantic.DeepEqual(original, object) { + t.Errorf("0: %v: encode altered the object, diff: %v", name, diff.ObjectReflectDiff(original, object)) + return + } + + // decode (deserialize) the encoded data back into an object + obj2, err := runtime.Decode(codec, data) + if err != nil { + t.Errorf("0: %v: %v\nCodec: %#v\nData: %s\nSource: %#v", name, err, codec, dataAsString(data), printer.Sprintf("%#v", object)) + panic("failed") + } + + // ensure that the object produced from decoding the encoded data is equal + // to the original object + if !apiequality.Semantic.DeepEqual(original, obj2) { + t.Errorf("\n1: %v: diff: %v\nCodec: %#v\nSource:\n\n%#v\n\nEncoded:\n\n%s\n\nFinal:\n\n%#v", name, diff.ObjectReflectDiff(object, obj2), codec, printer.Sprintf("%#v", object), dataAsString(data), printer.Sprintf("%#v", obj2)) + return + } + + // decode the encoded data into a new object (instead of letting the codec + // create a new object) + obj3 := reflect.New(reflect.TypeOf(object).Elem()).Interface().(runtime.Object) + if err := runtime.DecodeInto(codec, data, obj3); err != nil { + t.Errorf("2: %v: %v", name, err) + return + } + + // ensure that the new runtime object is equal to the original after being + // decoded into + if !apiequality.Semantic.DeepEqual(object, obj3) { + t.Errorf("3: %v: diff: %v\nCodec: %#v", name, diff.ObjectReflectDiff(object, obj3), codec) + return + } +} + +// dataAsString returns the given byte array as a string; handles detecting +// protocol buffers. +func dataAsString(data []byte) string { + dataString := string(data) + if !strings.HasPrefix(dataString, "{") { + dataString = "\n" + hex.Dump(data) + proto.NewBuffer(make([]byte, 0, 1024)).DebugPrint("decoded object", data) + } + return dataString +} diff --git a/vendor/BUILD b/vendor/BUILD index 748636c7bd0..59e183e654b 100644 --- a/vendor/BUILD +++ b/vendor/BUILD @@ -13837,17 +13837,26 @@ go_library( srcs = [ "k8s.io/apimachinery/pkg/api/testing/codec.go", "k8s.io/apimachinery/pkg/api/testing/fuzzer.go", + "k8s.io/apimachinery/pkg/api/testing/roundtrip.go", ], tags = ["automanaged"], deps = [ + "//vendor:github.com/davecgh/go-spew/spew", + "//vendor:github.com/golang/protobuf/proto", "//vendor:github.com/google/gofuzz", + "//vendor:github.com/spf13/pflag", + "//vendor:k8s.io/apimachinery/pkg/api/equality", + "//vendor:k8s.io/apimachinery/pkg/api/meta", "//vendor:k8s.io/apimachinery/pkg/api/resource", "//vendor:k8s.io/apimachinery/pkg/apis/meta/v1", "//vendor:k8s.io/apimachinery/pkg/runtime", "//vendor:k8s.io/apimachinery/pkg/runtime/schema", "//vendor:k8s.io/apimachinery/pkg/runtime/serializer", + "//vendor:k8s.io/apimachinery/pkg/runtime/serializer/protobuf", "//vendor:k8s.io/apimachinery/pkg/runtime/serializer/recognizer", "//vendor:k8s.io/apimachinery/pkg/types", + "//vendor:k8s.io/apimachinery/pkg/util/diff", + "//vendor:k8s.io/apimachinery/pkg/util/sets", ], )