Move unstructured conversion into pkg/runtime

Scheme conversion should support unstructured conversion natively to
allow going from unstructured to typed and back. It is not a higher
level responsibility to do that conversion because the scheme is the
only one who knows what types it supports.
This commit is contained in:
Clayton Coleman 2017-11-13 21:28:57 -05:00
parent 0eb999c26a
commit 557f9ddfe6
No known key found for this signature in database
GPG Key ID: 3D16906B4F1C5CB3
24 changed files with 85 additions and 193 deletions

View File

@ -505,7 +505,6 @@ staging/src/k8s.io/apimachinery/pkg/apis/meta/v1alpha1
staging/src/k8s.io/apimachinery/pkg/apis/testapigroup staging/src/k8s.io/apimachinery/pkg/apis/testapigroup
staging/src/k8s.io/apimachinery/pkg/apis/testapigroup/v1 staging/src/k8s.io/apimachinery/pkg/apis/testapigroup/v1
staging/src/k8s.io/apimachinery/pkg/conversion staging/src/k8s.io/apimachinery/pkg/conversion
staging/src/k8s.io/apimachinery/pkg/conversion/unstructured
staging/src/k8s.io/apimachinery/pkg/labels staging/src/k8s.io/apimachinery/pkg/labels
staging/src/k8s.io/apimachinery/pkg/runtime/schema staging/src/k8s.io/apimachinery/pkg/runtime/schema
staging/src/k8s.io/apimachinery/pkg/runtime/serializer staging/src/k8s.io/apimachinery/pkg/runtime/serializer

View File

@ -97,7 +97,6 @@ go_test(
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/conversion:go_default_library", "//vendor/k8s.io/apimachinery/pkg/conversion:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/conversion/unstructured:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer/protobuf:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/serializer/protobuf:go_default_library",

View File

@ -29,7 +29,6 @@ import (
"k8s.io/apimachinery/pkg/api/testing/fuzzer" "k8s.io/apimachinery/pkg/api/testing/fuzzer"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
metaunstruct "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" metaunstruct "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/conversion/unstructured"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/diff" "k8s.io/apimachinery/pkg/util/diff"
"k8s.io/apimachinery/pkg/util/json" "k8s.io/apimachinery/pkg/util/json"
@ -99,14 +98,14 @@ func doRoundTrip(t *testing.T, group testapi.TestGroup, kind string) {
return return
} }
newUnstr, err := unstructured.DefaultConverter.ToUnstructured(item) newUnstr, err := runtime.NewTestUnstructuredConverter(apiequality.Semantic).ToUnstructured(item)
if err != nil { if err != nil {
t.Errorf("ToUnstructured failed: %v", err) t.Errorf("ToUnstructured failed: %v", err)
return return
} }
newObj := reflect.New(reflect.TypeOf(item).Elem()).Interface().(runtime.Object) newObj := reflect.New(reflect.TypeOf(item).Elem()).Interface().(runtime.Object)
err = unstructured.DefaultConverter.FromUnstructured(newUnstr, newObj) err = runtime.DefaultUnstructuredConverter.FromUnstructured(newUnstr, newObj)
if err != nil { if err != nil {
t.Errorf("FromUnstructured failed: %v", err) t.Errorf("FromUnstructured failed: %v", err)
return return
@ -146,7 +145,7 @@ func TestRoundTripWithEmptyCreationTimestamp(t *testing.T) {
} }
t.Logf("Testing: %v in %v", kind, groupKey) t.Logf("Testing: %v in %v", kind, groupKey)
unstrBody, err := unstructured.DefaultConverter.ToUnstructured(item) unstrBody, err := runtime.DefaultUnstructuredConverter.ToUnstructured(item)
if err != nil { if err != nil {
t.Fatalf("ToUnstructured failed: %v", err) t.Fatalf("ToUnstructured failed: %v", err)
} }
@ -163,7 +162,7 @@ func TestRoundTripWithEmptyCreationTimestamp(t *testing.T) {
// attempt to re-convert unstructured object - conversion should not fail // attempt to re-convert unstructured object - conversion should not fail
// based on empty metadata fields, such as creationTimestamp // based on empty metadata fields, such as creationTimestamp
newObj := reflect.New(reflect.TypeOf(item).Elem()).Interface().(runtime.Object) newObj := reflect.New(reflect.TypeOf(item).Elem()).Interface().(runtime.Object)
err = unstructured.DefaultConverter.FromUnstructured(unstructObj.Object, newObj) err = runtime.NewTestUnstructuredConverter(apiequality.Semantic).FromUnstructured(unstructObj.Object, newObj)
if err != nil { if err != nil {
t.Fatalf("FromUnstructured failed: %v", err) t.Fatalf("FromUnstructured failed: %v", err)
} }
@ -176,12 +175,12 @@ func BenchmarkToFromUnstructured(b *testing.B) {
size := len(items) size := len(items)
b.ResetTimer() b.ResetTimer()
for i := 0; i < b.N; i++ { for i := 0; i < b.N; i++ {
unstr, err := unstructured.DefaultConverter.ToUnstructured(&items[i%size]) unstr, err := runtime.NewTestUnstructuredConverter(apiequality.Semantic).ToUnstructured(&items[i%size])
if err != nil { if err != nil {
b.Fatalf("unexpected error: %v", err) b.Fatalf("unexpected error: %v", err)
} }
obj := v1.Pod{} obj := v1.Pod{}
if err := unstructured.DefaultConverter.FromUnstructured(unstr, &obj); err != nil { if err := runtime.NewTestUnstructuredConverter(apiequality.Semantic).FromUnstructured(unstr, &obj); err != nil {
b.Fatalf("unexpected error: %v", err) b.Fatalf("unexpected error: %v", err)
} }
} }

View File

@ -182,6 +182,10 @@
"ImportPath": "k8s.io/apimachinery/pkg/util/intstr", "ImportPath": "k8s.io/apimachinery/pkg/util/intstr",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" "Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}, },
{
"ImportPath": "k8s.io/apimachinery/pkg/util/json",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/net", "ImportPath": "k8s.io/apimachinery/pkg/util/net",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" "Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

View File

@ -702,10 +702,6 @@
"ImportPath": "k8s.io/apimachinery/pkg/conversion/queryparams", "ImportPath": "k8s.io/apimachinery/pkg/conversion/queryparams",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" "Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}, },
{
"ImportPath": "k8s.io/apimachinery/pkg/conversion/unstructured",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{ {
"ImportPath": "k8s.io/apimachinery/pkg/fields", "ImportPath": "k8s.io/apimachinery/pkg/fields",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" "Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

View File

@ -32,7 +32,6 @@ go_library(
importpath = "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured", importpath = "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured",
deps = [ deps = [
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/conversion/unstructured:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/types:go_default_library", "//vendor/k8s.io/apimachinery/pkg/types:go_default_library",

View File

@ -24,7 +24,6 @@ import (
"strings" "strings"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/conversion/unstructured"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
@ -39,7 +38,7 @@ func NestedFieldCopy(obj map[string]interface{}, fields ...string) (interface{},
if !ok { if !ok {
return nil, false return nil, false
} }
return unstructured.DeepCopyJSONValue(val), true return runtime.DeepCopyJSONValue(val), true
} }
func nestedFieldNoCopy(obj map[string]interface{}, fields ...string) (interface{}, bool) { func nestedFieldNoCopy(obj map[string]interface{}, fields ...string) (interface{}, bool) {
@ -131,7 +130,7 @@ func NestedSlice(obj map[string]interface{}, fields ...string) ([]interface{}, b
return nil, false return nil, false
} }
if _, ok := val.([]interface{}); ok { if _, ok := val.([]interface{}); ok {
return unstructured.DeepCopyJSONValue(val).([]interface{}), true return runtime.DeepCopyJSONValue(val).([]interface{}), true
} }
return nil, false return nil, false
} }
@ -165,7 +164,7 @@ func NestedMap(obj map[string]interface{}, fields ...string) (map[string]interfa
return nil, false return nil, false
} }
if m, ok := val.(map[string]interface{}); ok { if m, ok := val.(map[string]interface{}); ok {
return unstructured.DeepCopyJSON(m), true return runtime.DeepCopyJSON(m), true
} }
return nil, false return nil, false
} }
@ -173,7 +172,7 @@ func NestedMap(obj map[string]interface{}, fields ...string) (map[string]interfa
// SetNestedField sets the value of a nested field to a deep copy of the value provided. // SetNestedField sets the value of a nested field to a deep copy of the value provided.
// Returns false if value cannot be set because one of the nesting levels is not a map[string]interface{}. // Returns false if value cannot be set because one of the nesting levels is not a map[string]interface{}.
func SetNestedField(obj map[string]interface{}, value interface{}, fields ...string) bool { func SetNestedField(obj map[string]interface{}, value interface{}, fields ...string) bool {
return setNestedFieldNoCopy(obj, unstructured.DeepCopyJSONValue(value), fields...) return setNestedFieldNoCopy(obj, runtime.DeepCopyJSONValue(value), fields...)
} }
func setNestedFieldNoCopy(obj map[string]interface{}, value interface{}, fields ...string) bool { func setNestedFieldNoCopy(obj map[string]interface{}, value interface{}, fields ...string) bool {
@ -288,8 +287,6 @@ func setOwnerReference(src metav1.OwnerReference) map[string]interface{} {
return ret return ret
} }
var converter = unstructured.NewConverter(false)
// UnstructuredJSONScheme is capable of converting JSON data into the Unstructured // UnstructuredJSONScheme is capable of converting JSON data into the Unstructured
// type, which can be used for generic access to objects without a predefined scheme. // type, which can be used for generic access to objects without a predefined scheme.
// TODO: move into serializer/json. // TODO: move into serializer/json.

View File

@ -21,7 +21,6 @@ import (
"fmt" "fmt"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/conversion/unstructured"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
@ -112,7 +111,7 @@ func (in *Unstructured) DeepCopy() *Unstructured {
} }
out := new(Unstructured) out := new(Unstructured)
*out = *in *out = *in
out.Object = unstructured.DeepCopyJSON(in.Object) out.Object = runtime.DeepCopyJSON(in.Object)
return out return out
} }
@ -348,7 +347,7 @@ func (u *Unstructured) GetInitializers() *metav1.Initializers {
return nil return nil
} }
out := &metav1.Initializers{} out := &metav1.Initializers{}
if err := converter.FromUnstructured(obj, out); err != nil { if err := runtime.DefaultUnstructuredConverter.FromUnstructured(obj, out); err != nil {
utilruntime.HandleError(fmt.Errorf("unable to retrieve initializers for object: %v", err)) utilruntime.HandleError(fmt.Errorf("unable to retrieve initializers for object: %v", err))
} }
return out return out
@ -359,7 +358,7 @@ func (u *Unstructured) SetInitializers(initializers *metav1.Initializers) {
RemoveNestedField(u.Object, "metadata", "initializers") RemoveNestedField(u.Object, "metadata", "initializers")
return return
} }
out, err := converter.ToUnstructured(initializers) out, err := runtime.DefaultUnstructuredConverter.ToUnstructured(initializers)
if err != nil { if err != nil {
utilruntime.HandleError(fmt.Errorf("unable to retrieve initializers for object: %v", err)) utilruntime.HandleError(fmt.Errorf("unable to retrieve initializers for object: %v", err))
} }

View File

@ -20,7 +20,6 @@ import (
"bytes" "bytes"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/conversion/unstructured"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
) )
@ -80,7 +79,7 @@ func (u *UnstructuredList) DeepCopy() *UnstructuredList {
} }
out := new(UnstructuredList) out := new(UnstructuredList)
*out = *u *out = *u
out.Object = unstructured.DeepCopyJSON(u.Object) out.Object = runtime.DeepCopyJSON(u.Object)
out.Items = make([]Unstructured, len(u.Items)) out.Items = make([]Unstructured, len(u.Items))
for i := range u.Items { for i := range u.Items {
u.Items[i].DeepCopyInto(&out.Items[i]) u.Items[i].DeepCopyInto(&out.Items[i])

View File

@ -45,7 +45,6 @@ filegroup(
srcs = [ srcs = [
":package-srcs", ":package-srcs",
"//staging/src/k8s.io/apimachinery/pkg/conversion/queryparams:all-srcs", "//staging/src/k8s.io/apimachinery/pkg/conversion/queryparams:all-srcs",
"//staging/src/k8s.io/apimachinery/pkg/conversion/unstructured:all-srcs",
], ],
tags = ["automanaged"], tags = ["automanaged"],
) )

View File

@ -1,39 +0,0 @@
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
)
go_library(
name = "go_default_library",
srcs = [
"converter.go",
"doc.go",
],
importpath = "k8s.io/apimachinery/pkg/conversion/unstructured",
deps = [
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/equality:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/json:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [
":package-srcs",
"//staging/src/k8s.io/apimachinery/pkg/conversion/unstructured/testing:all-srcs",
],
tags = ["automanaged"],
)

View File

@ -1,19 +0,0 @@
/*
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 unstructured provides conversion from runtime objects
// to map[string]interface{} representation.
package unstructured // import "k8s.io/apimachinery/pkg/conversion/unstructured"

View File

@ -1,29 +0,0 @@
load("@io_bazel_rules_go//go:def.bzl", "go_test")
go_test(
name = "go_default_test",
srcs = ["converter_test.go"],
importpath = "k8s.io/apimachinery/pkg/conversion/unstructured/testing",
deps = [
"//vendor/github.com/stretchr/testify/assert:go_default_library",
"//vendor/github.com/stretchr/testify/require:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/conversion/unstructured:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/json:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
visibility = ["//visibility:public"],
)

View File

@ -19,6 +19,7 @@ go_library(
"codec.go", "codec.go",
"codec_check.go", "codec_check.go",
"conversion.go", "conversion.go",
"converter.go",
"doc.go", "doc.go",
"embedded.go", "embedded.go",
"error.go", "error.go",
@ -37,10 +38,13 @@ go_library(
importpath = "k8s.io/apimachinery/pkg/runtime", importpath = "k8s.io/apimachinery/pkg/runtime",
deps = [ deps = [
"//vendor/github.com/gogo/protobuf/proto:go_default_library", "//vendor/github.com/gogo/protobuf/proto:go_default_library",
"//vendor/github.com/golang/glog:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/conversion:go_default_library", "//vendor/k8s.io/apimachinery/pkg/conversion:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/conversion/queryparams:go_default_library", "//vendor/k8s.io/apimachinery/pkg/conversion/queryparams:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/errors:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/json:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/runtime:go_default_library",
], ],
) )
@ -48,6 +52,7 @@ go_test(
name = "go_default_xtest", name = "go_default_xtest",
srcs = [ srcs = [
"conversion_test.go", "conversion_test.go",
"converter_test.go",
"embedded_test.go", "embedded_test.go",
"extension_test.go", "extension_test.go",
"scheme_test.go", "scheme_test.go",
@ -56,13 +61,17 @@ go_test(
deps = [ deps = [
"//vendor/github.com/google/gofuzz:go_default_library", "//vendor/github.com/google/gofuzz:go_default_library",
"//vendor/github.com/spf13/pflag:go_default_library", "//vendor/github.com/spf13/pflag:go_default_library",
"//vendor/github.com/stretchr/testify/assert:go_default_library",
"//vendor/github.com/stretchr/testify/require:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library", "//vendor/k8s.io/apimachinery/pkg/api/meta:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1/unstructured:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/conversion:go_default_library", "//vendor/k8s.io/apimachinery/pkg/conversion:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/serializer:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/testing:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/testing:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library", "//vendor/k8s.io/apimachinery/pkg/util/diff:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/util/json:go_default_library",
], ],
) )

View File

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
package unstructured package runtime
import ( import (
"bytes" "bytes"
@ -27,19 +27,18 @@ import (
"strings" "strings"
"sync" "sync"
"sync/atomic" "sync/atomic"
"time"
apiequality "k8s.io/apimachinery/pkg/api/equality" "k8s.io/apimachinery/pkg/conversion"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/diff"
"k8s.io/apimachinery/pkg/util/json" "k8s.io/apimachinery/pkg/util/json"
utilruntime "k8s.io/apimachinery/pkg/util/runtime" utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"github.com/golang/glog" "github.com/golang/glog"
) )
// Converter is an interface for converting between interface{} // UnstructuredConverter is an interface for converting between interface{}
// and map[string]interface representation. // and map[string]interface representation.
type Converter interface { type UnstructuredConverter interface {
ToUnstructured(obj interface{}) (map[string]interface{}, error) ToUnstructured(obj interface{}) (map[string]interface{}, error)
FromUnstructured(u map[string]interface{}, obj interface{}) error FromUnstructured(u map[string]interface{}, obj interface{}) error
} }
@ -78,7 +77,16 @@ var (
float64Type = reflect.TypeOf(float64(0)) float64Type = reflect.TypeOf(float64(0))
boolType = reflect.TypeOf(bool(false)) boolType = reflect.TypeOf(bool(false))
fieldCache = newFieldsCache() fieldCache = newFieldsCache()
DefaultConverter = NewConverter(parseBool(os.Getenv("KUBE_PATCH_CONVERSION_DETECTOR")))
// DefaultUnstructuredConverter performs unstructured to Go typed object conversions.
DefaultUnstructuredConverter = &unstructuredConverter{
mismatchDetection: parseBool(os.Getenv("KUBE_PATCH_CONVERSION_DETECTOR")),
comparison: conversion.EqualitiesOrDie(
func(a, b time.Time) bool {
return a.UTC() == b.UTC()
},
),
}
) )
func parseBool(key string) bool { func parseBool(key string) bool {
@ -92,24 +100,30 @@ func parseBool(key string) bool {
return value return value
} }
// ConverterImpl knows how to convert between interface{} and // unstructuredConverter knows how to convert between interface{} and
// Unstructured in both ways. // Unstructured in both ways.
type converterImpl struct { type unstructuredConverter struct {
// If true, we will be additionally running conversion via json // If true, we will be additionally running conversion via json
// to ensure that the result is true. // to ensure that the result is true.
// This is supposed to be set only in tests. // This is supposed to be set only in tests.
mismatchDetection bool mismatchDetection bool
// comparison is the default test logic used to compare
comparison conversion.Equalities
} }
func NewConverter(mismatchDetection bool) Converter { // NewTestUnstructuredConverter creates an UnstructuredConverter that accepts JSON typed maps and translates them
return &converterImpl{ // to Go types via reflection. It performs mismatch detection automatically and is intended for use by external
mismatchDetection: mismatchDetection, // test tools. Use DefaultUnstructuredConverter if you do not explicitly need mismatch detection.
func NewTestUnstructuredConverter(comparison conversion.Equalities) UnstructuredConverter {
return &unstructuredConverter{
mismatchDetection: true,
comparison: comparison,
} }
} }
// FromUnstructured converts an object from map[string]interface{} representation into a concrete type. // FromUnstructured converts an object from map[string]interface{} representation into a concrete type.
// It uses encoding/json/Unmarshaler if object implements it or reflection if not. // It uses encoding/json/Unmarshaler if object implements it or reflection if not.
func (c *converterImpl) FromUnstructured(u map[string]interface{}, obj interface{}) error { func (c *unstructuredConverter) FromUnstructured(u map[string]interface{}, obj interface{}) error {
t := reflect.TypeOf(obj) t := reflect.TypeOf(obj)
value := reflect.ValueOf(obj) value := reflect.ValueOf(obj)
if t.Kind() != reflect.Ptr || value.IsNil() { if t.Kind() != reflect.Ptr || value.IsNil() {
@ -122,8 +136,8 @@ func (c *converterImpl) FromUnstructured(u map[string]interface{}, obj interface
if (err != nil) != (newErr != nil) { if (err != nil) != (newErr != nil) {
glog.Fatalf("FromUnstructured unexpected error for %v: error: %v", u, err) glog.Fatalf("FromUnstructured unexpected error for %v: error: %v", u, err)
} }
if err == nil && !apiequality.Semantic.DeepEqual(obj, newObj) { if err == nil && !c.comparison.DeepEqual(obj, newObj) {
glog.Fatalf("FromUnstructured mismatch for %#v, diff: %v", obj, diff.ObjectReflectDiff(obj, newObj)) glog.Fatalf("FromUnstructured mismatch\nobj1: %#v\nobj2: %#v", obj, newObj)
} }
} }
return err return err
@ -393,10 +407,10 @@ func interfaceFromUnstructured(sv, dv reflect.Value) error {
// ToUnstructured converts an object into map[string]interface{} representation. // ToUnstructured converts an object into map[string]interface{} representation.
// It uses encoding/json/Marshaler if object implements it or reflection if not. // It uses encoding/json/Marshaler if object implements it or reflection if not.
func (c *converterImpl) ToUnstructured(obj interface{}) (map[string]interface{}, error) { func (c *unstructuredConverter) ToUnstructured(obj interface{}) (map[string]interface{}, error) {
var u map[string]interface{} var u map[string]interface{}
var err error var err error
if unstr, ok := obj.(runtime.Unstructured); ok { if unstr, ok := obj.(Unstructured); ok {
u = DeepCopyJSON(unstr.UnstructuredContent()) u = DeepCopyJSON(unstr.UnstructuredContent())
} else { } else {
t := reflect.TypeOf(obj) t := reflect.TypeOf(obj)
@ -413,8 +427,8 @@ func (c *converterImpl) ToUnstructured(obj interface{}) (map[string]interface{},
if (err != nil) != (newErr != nil) { if (err != nil) != (newErr != nil) {
glog.Fatalf("ToUnstructured unexpected error for %v: error: %v; newErr: %v", obj, err, newErr) glog.Fatalf("ToUnstructured unexpected error for %v: error: %v; newErr: %v", obj, err, newErr)
} }
if err == nil && !apiequality.Semantic.DeepEqual(u, newUnstr) { if err == nil && !c.comparison.DeepEqual(u, newUnstr) {
glog.Fatalf("ToUnstructured mismatch for %#v, diff: %v", u, diff.ObjectReflectDiff(u, newUnstr)) glog.Fatalf("ToUnstructured mismatch\nobj1: %#v\nobj2: %#v", u, newUnstr)
} }
} }
if err != nil { if err != nil {

View File

@ -18,7 +18,7 @@ limitations under the License.
// Unstructured type depends on unstructured converter package but we want to test how the converter handles // Unstructured type depends on unstructured converter package but we want to test how the converter handles
// the Unstructured type so we need to import both. // the Unstructured type so we need to import both.
package testing package runtime_test
import ( import (
encodingjson "encoding/json" encodingjson "encoding/json"
@ -26,9 +26,11 @@ import (
"reflect" "reflect"
"strconv" "strconv"
"testing" "testing"
"time"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
conversionunstructured "k8s.io/apimachinery/pkg/conversion/unstructured" "k8s.io/apimachinery/pkg/conversion"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/util/diff" "k8s.io/apimachinery/pkg/util/diff"
"k8s.io/apimachinery/pkg/util/json" "k8s.io/apimachinery/pkg/util/json"
@ -36,6 +38,12 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
var simpleEquality = conversion.EqualitiesOrDie(
func(a, b time.Time) bool {
return a.UTC() == b.UTC()
},
)
// Definte a number of test types. // Definte a number of test types.
type A struct { type A struct {
A int `json:"aa,omitempty"` A int `json:"aa,omitempty"`
@ -137,14 +145,15 @@ func doRoundTrip(t *testing.T, item interface{}) {
return return
} }
newUnstr, err := conversionunstructured.DefaultConverter.ToUnstructured(item) // TODO: should be using mismatch detection but fails due to another error
newUnstr, err := runtime.DefaultUnstructuredConverter.ToUnstructured(item)
if err != nil { if err != nil {
t.Errorf("ToUnstructured failed: %v", err) t.Errorf("ToUnstructured failed: %v", err)
return return
} }
newObj := reflect.New(reflect.TypeOf(item).Elem()).Interface() newObj := reflect.New(reflect.TypeOf(item).Elem()).Interface()
err = conversionunstructured.DefaultConverter.FromUnstructured(newUnstr, newObj) err = runtime.NewTestUnstructuredConverter(simpleEquality).FromUnstructured(newUnstr, newObj)
if err != nil { if err != nil {
t.Errorf("FromUnstructured failed: %v", err) t.Errorf("FromUnstructured failed: %v", err)
return return
@ -239,10 +248,9 @@ func TestRoundTrip(t *testing.T) {
} }
for i := range testCases { for i := range testCases {
doRoundTrip(t, testCases[i].obj) t.Run(fmt.Sprintf("%d", i), func(t *testing.T) {
if t.Failed() { doRoundTrip(t, testCases[i].obj)
break })
}
} }
} }
@ -265,7 +273,7 @@ func doUnrecognized(t *testing.T, jsonData string, item interface{}, expectedErr
return return
} }
newObj := reflect.New(reflect.TypeOf(item).Elem()).Interface() newObj := reflect.New(reflect.TypeOf(item).Elem()).Interface()
err = conversionunstructured.DefaultConverter.FromUnstructured(unstr, newObj) err = runtime.NewTestUnstructuredConverter(simpleEquality).FromUnstructured(unstr, newObj)
if (err != nil) != (expectedErr != nil) { if (err != nil) != (expectedErr != nil) {
t.Errorf("Unexpected error in FromUnstructured: %v, expected: %v", err, expectedErr) t.Errorf("Unexpected error in FromUnstructured: %v, expected: %v", err, expectedErr)
} }
@ -479,7 +487,7 @@ func TestDeepCopyJSON(t *testing.T) {
"f": true, "f": true,
"g": encodingjson.Number("123"), "g": encodingjson.Number("123"),
} }
deepCopy := conversionunstructured.DeepCopyJSON(src) deepCopy := runtime.DeepCopyJSON(src)
assert.Equal(t, src, deepCopy) assert.Equal(t, src, deepCopy)
} }
@ -487,7 +495,7 @@ func TestFloatIntConversion(t *testing.T) {
unstr := map[string]interface{}{"fd": float64(3)} unstr := map[string]interface{}{"fd": float64(3)}
var obj F var obj F
if err := conversionunstructured.DefaultConverter.FromUnstructured(unstr, &obj); err != nil { if err := runtime.NewTestUnstructuredConverter(simpleEquality).FromUnstructured(unstr, &obj); err != nil {
t.Errorf("Unexpected error in FromUnstructured: %v", err) t.Errorf("Unexpected error in FromUnstructured: %v", err)
} }
@ -525,7 +533,7 @@ func TestCustomToUnstructured(t *testing.T) {
tc := tc tc := tc
t.Run(tc.Data, func(t *testing.T) { t.Run(tc.Data, func(t *testing.T) {
t.Parallel() t.Parallel()
result, err := conversionunstructured.DefaultConverter.ToUnstructured(&G{ result, err := runtime.NewTestUnstructuredConverter(simpleEquality).ToUnstructured(&G{
CustomValue1: CustomValue{data: []byte(tc.Data)}, CustomValue1: CustomValue{data: []byte(tc.Data)},
CustomValue2: &CustomValue{data: []byte(tc.Data)}, CustomValue2: &CustomValue{data: []byte(tc.Data)},
CustomPointer1: CustomPointer{data: []byte(tc.Data)}, CustomPointer1: CustomPointer{data: []byte(tc.Data)},
@ -550,7 +558,7 @@ func TestCustomToUnstructuredTopLevel(t *testing.T) {
obj := obj obj := obj
t.Run(strconv.Itoa(i), func(t *testing.T) { t.Run(strconv.Itoa(i), func(t *testing.T) {
t.Parallel() t.Parallel()
result, err := conversionunstructured.DefaultConverter.ToUnstructured(obj) result, err := runtime.NewTestUnstructuredConverter(simpleEquality).ToUnstructured(obj)
require.NoError(t, err) require.NoError(t, err)
assert.Equal(t, expected, result) assert.Equal(t, expected, result)
}) })

View File

@ -954,10 +954,6 @@
"ImportPath": "k8s.io/apimachinery/pkg/conversion/queryparams", "ImportPath": "k8s.io/apimachinery/pkg/conversion/queryparams",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" "Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}, },
{
"ImportPath": "k8s.io/apimachinery/pkg/conversion/unstructured",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{ {
"ImportPath": "k8s.io/apimachinery/pkg/fields", "ImportPath": "k8s.io/apimachinery/pkg/fields",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" "Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

View File

@ -60,7 +60,6 @@ go_library(
"//vendor/k8s.io/apimachinery/pkg/apis/meta/internalversion:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/internalversion:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/apis/meta/v1alpha1:go_default_library", "//vendor/k8s.io/apimachinery/pkg/apis/meta/v1alpha1:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/conversion/unstructured:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/fields:go_default_library", "//vendor/k8s.io/apimachinery/pkg/fields:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime:go_default_library",
"//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library", "//vendor/k8s.io/apimachinery/pkg/runtime/schema:go_default_library",

View File

@ -27,7 +27,6 @@ import (
"k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/api/meta" "k8s.io/apimachinery/pkg/api/meta"
"k8s.io/apimachinery/pkg/conversion/unstructured"
"k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/types" "k8s.io/apimachinery/pkg/types"
@ -232,7 +231,7 @@ func patchResource(
return nil, err return nil, err
} }
// Capture the original object map and patch for possible retries. // Capture the original object map and patch for possible retries.
originalMap, err := unstructured.DefaultConverter.ToUnstructured(currentVersionedObject) originalMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(currentVersionedObject)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -278,7 +277,7 @@ func patchResource(
if err != nil { if err != nil {
return nil, err return nil, err
} }
currentObjMap, err := unstructured.DefaultConverter.ToUnstructured(currentVersionedObject) currentObjMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(currentVersionedObject)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -425,7 +424,7 @@ func strategicPatchObject(
objToUpdate runtime.Object, objToUpdate runtime.Object,
versionedObj runtime.Object, versionedObj runtime.Object,
) error { ) error {
originalObjMap, err := unstructured.DefaultConverter.ToUnstructured(originalObject) originalObjMap, err := runtime.DefaultUnstructuredConverter.ToUnstructured(originalObject)
if err != nil { if err != nil {
return err return err
} }
@ -458,7 +457,7 @@ func applyPatchToObject(
} }
// Rather than serialize the patched map to JSON, then decode it to an object, we go directly from a map to an object // Rather than serialize the patched map to JSON, then decode it to an object, we go directly from a map to an object
if err := unstructured.DefaultConverter.FromUnstructured(patchedObjMap, objToUpdate); err != nil { if err := runtime.DefaultUnstructuredConverter.FromUnstructured(patchedObjMap, objToUpdate); err != nil {
return err return err
} }
// Decoding from JSON to a versioned object would apply defaults, so we do the same here // Decoding from JSON to a versioned object would apply defaults, so we do the same here

View File

@ -546,10 +546,6 @@
"ImportPath": "k8s.io/apimachinery/pkg/conversion/queryparams", "ImportPath": "k8s.io/apimachinery/pkg/conversion/queryparams",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" "Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}, },
{
"ImportPath": "k8s.io/apimachinery/pkg/conversion/unstructured",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{ {
"ImportPath": "k8s.io/apimachinery/pkg/fields", "ImportPath": "k8s.io/apimachinery/pkg/fields",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" "Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

View File

@ -666,10 +666,6 @@
"ImportPath": "k8s.io/apimachinery/pkg/conversion/queryparams", "ImportPath": "k8s.io/apimachinery/pkg/conversion/queryparams",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" "Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}, },
{
"ImportPath": "k8s.io/apimachinery/pkg/conversion/unstructured",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{ {
"ImportPath": "k8s.io/apimachinery/pkg/fields", "ImportPath": "k8s.io/apimachinery/pkg/fields",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" "Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

View File

@ -14,10 +14,6 @@
"ImportPath": "github.com/PuerkitoBio/urlesc", "ImportPath": "github.com/PuerkitoBio/urlesc",
"Rev": "5bd2802263f21d8788851d5305584c82a5c75d7e" "Rev": "5bd2802263f21d8788851d5305584c82a5c75d7e"
}, },
{
"ImportPath": "github.com/davecgh/go-spew/spew",
"Rev": "782f4967f2dc4564575ca782fe2d04090b5faca8"
},
{ {
"ImportPath": "github.com/emicklei/go-restful", "ImportPath": "github.com/emicklei/go-restful",
"Rev": "ff4f55a206334ef123e4f79bbf348980da81ca46" "Rev": "ff4f55a206334ef123e4f79bbf348980da81ca46"
@ -306,10 +302,6 @@
"ImportPath": "k8s.io/api/storage/v1beta1", "ImportPath": "k8s.io/api/storage/v1beta1",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" "Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}, },
{
"ImportPath": "k8s.io/apimachinery/pkg/api/equality",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{ {
"ImportPath": "k8s.io/apimachinery/pkg/api/errors", "ImportPath": "k8s.io/apimachinery/pkg/api/errors",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" "Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
@ -354,10 +346,6 @@
"ImportPath": "k8s.io/apimachinery/pkg/conversion/queryparams", "ImportPath": "k8s.io/apimachinery/pkg/conversion/queryparams",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" "Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}, },
{
"ImportPath": "k8s.io/apimachinery/pkg/conversion/unstructured",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{ {
"ImportPath": "k8s.io/apimachinery/pkg/fields", "ImportPath": "k8s.io/apimachinery/pkg/fields",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" "Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
@ -410,10 +398,6 @@
"ImportPath": "k8s.io/apimachinery/pkg/util/clock", "ImportPath": "k8s.io/apimachinery/pkg/util/clock",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" "Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}, },
{
"ImportPath": "k8s.io/apimachinery/pkg/util/diff",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{ {
"ImportPath": "k8s.io/apimachinery/pkg/util/errors", "ImportPath": "k8s.io/apimachinery/pkg/util/errors",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" "Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

View File

@ -666,10 +666,6 @@
"ImportPath": "k8s.io/apimachinery/pkg/conversion/queryparams", "ImportPath": "k8s.io/apimachinery/pkg/conversion/queryparams",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" "Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}, },
{
"ImportPath": "k8s.io/apimachinery/pkg/conversion/unstructured",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{ {
"ImportPath": "k8s.io/apimachinery/pkg/fields", "ImportPath": "k8s.io/apimachinery/pkg/fields",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" "Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"

View File

@ -342,10 +342,6 @@
"ImportPath": "k8s.io/api/storage/v1beta1", "ImportPath": "k8s.io/api/storage/v1beta1",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" "Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}, },
{
"ImportPath": "k8s.io/apimachinery/pkg/api/equality",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{ {
"ImportPath": "k8s.io/apimachinery/pkg/api/errors", "ImportPath": "k8s.io/apimachinery/pkg/api/errors",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" "Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
@ -382,10 +378,6 @@
"ImportPath": "k8s.io/apimachinery/pkg/conversion/queryparams", "ImportPath": "k8s.io/apimachinery/pkg/conversion/queryparams",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" "Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
}, },
{
"ImportPath": "k8s.io/apimachinery/pkg/conversion/unstructured",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
},
{ {
"ImportPath": "k8s.io/apimachinery/pkg/fields", "ImportPath": "k8s.io/apimachinery/pkg/fields",
"Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx" "Rev": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"