k8s.io/client-go@v0.31.1/testing/fixture_test.go (about)

     1  /*
     2  Copyright 2015 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package testing
    18  
    19  import (
    20  	"fmt"
    21  	"math/rand"
    22  	"sigs.k8s.io/structured-merge-diff/v4/typed"
    23  	"strconv"
    24  	"sync"
    25  	"testing"
    26  
    27  	"github.com/stretchr/testify/assert"
    28  
    29  	v1 "k8s.io/api/core/v1"
    30  	"k8s.io/apimachinery/pkg/api/errors"
    31  	"k8s.io/apimachinery/pkg/api/meta"
    32  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    33  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    34  	runtime "k8s.io/apimachinery/pkg/runtime"
    35  	"k8s.io/apimachinery/pkg/runtime/schema"
    36  	serializer "k8s.io/apimachinery/pkg/runtime/serializer"
    37  	"k8s.io/apimachinery/pkg/types"
    38  	"k8s.io/apimachinery/pkg/util/managedfields"
    39  	"k8s.io/apimachinery/pkg/watch"
    40  	"k8s.io/utils/ptr"
    41  )
    42  
    43  func getArbitraryResource(s schema.GroupVersionResource, name, namespace string) *unstructured.Unstructured {
    44  	return &unstructured.Unstructured{
    45  		Object: map[string]interface{}{
    46  			"kind":       s.Resource,
    47  			"apiVersion": s.Version,
    48  			"metadata": map[string]interface{}{
    49  				"name":            name,
    50  				"namespace":       namespace,
    51  				"generateName":    "test_generateName",
    52  				"uid":             "test_uid",
    53  				"resourceVersion": "test_resourceVersion",
    54  			},
    55  			"data": strconv.Itoa(rand.Int()),
    56  		},
    57  	}
    58  }
    59  
    60  func TestWatchCallNonNamespace(t *testing.T) {
    61  	testResource := schema.GroupVersionResource{Group: "", Version: "test_version", Resource: "test_kind"}
    62  	testObj := getArbitraryResource(testResource, "test_name", "test_namespace")
    63  	accessor, err := meta.Accessor(testObj)
    64  	if err != nil {
    65  		t.Fatalf("unexpected error: %v", err)
    66  	}
    67  	ns := accessor.GetNamespace()
    68  	scheme := runtime.NewScheme()
    69  	codecs := serializer.NewCodecFactory(scheme)
    70  	o := NewObjectTracker(scheme, codecs.UniversalDecoder())
    71  	watch, err := o.Watch(testResource, ns)
    72  	if err != nil {
    73  		t.Fatalf("test resource watch failed in %s: %v ", ns, err)
    74  	}
    75  	go func() {
    76  		err := o.Create(testResource, testObj, ns)
    77  		if err != nil {
    78  			t.Errorf("test resource creation failed: %v", err)
    79  		}
    80  	}()
    81  	out := <-watch.ResultChan()
    82  	assert.Equal(t, testObj, out.Object, "watched object mismatch")
    83  }
    84  
    85  func TestWatchCallAllNamespace(t *testing.T) {
    86  	testResource := schema.GroupVersionResource{Group: "", Version: "test_version", Resource: "test_kind"}
    87  	testObj := getArbitraryResource(testResource, "test_name", "test_namespace")
    88  	accessor, err := meta.Accessor(testObj)
    89  	if err != nil {
    90  		t.Fatalf("unexpected error: %v", err)
    91  	}
    92  	ns := accessor.GetNamespace()
    93  	scheme := runtime.NewScheme()
    94  	codecs := serializer.NewCodecFactory(scheme)
    95  	o := NewObjectTracker(scheme, codecs.UniversalDecoder())
    96  	w, err := o.Watch(testResource, "test_namespace")
    97  	if err != nil {
    98  		t.Fatalf("test resource watch failed in test_namespace: %v", err)
    99  	}
   100  	wAll, err := o.Watch(testResource, "")
   101  	if err != nil {
   102  		t.Fatalf("test resource watch failed in all namespaces: %v", err)
   103  	}
   104  	go func() {
   105  		err := o.Create(testResource, testObj, ns)
   106  		assert.NoError(t, err, "test resource creation failed")
   107  	}()
   108  	out := <-w.ResultChan()
   109  	outAll := <-wAll.ResultChan()
   110  	assert.Equal(t, watch.Added, out.Type, "watch event mismatch")
   111  	assert.Equal(t, watch.Added, outAll.Type, "watch event mismatch")
   112  	assert.Equal(t, testObj, out.Object, "watched created object mismatch")
   113  	assert.Equal(t, testObj, outAll.Object, "watched created object mismatch")
   114  	go func() {
   115  		err := o.Update(testResource, testObj, ns)
   116  		assert.NoError(t, err, "test resource updating failed")
   117  	}()
   118  	out = <-w.ResultChan()
   119  	outAll = <-wAll.ResultChan()
   120  	assert.Equal(t, watch.Modified, out.Type, "watch event mismatch")
   121  	assert.Equal(t, watch.Modified, outAll.Type, "watch event mismatch")
   122  	assert.Equal(t, testObj, out.Object, "watched updated object mismatch")
   123  	assert.Equal(t, testObj, outAll.Object, "watched updated object mismatch")
   124  	go func() {
   125  		err := o.Delete(testResource, "test_namespace", "test_name")
   126  		assert.NoError(t, err, "test resource deletion failed")
   127  	}()
   128  	out = <-w.ResultChan()
   129  	outAll = <-wAll.ResultChan()
   130  	assert.Equal(t, watch.Deleted, out.Type, "watch event mismatch")
   131  	assert.Equal(t, watch.Deleted, outAll.Type, "watch event mismatch")
   132  	assert.Equal(t, testObj, out.Object, "watched deleted object mismatch")
   133  	assert.Equal(t, testObj, outAll.Object, "watched deleted object mismatch")
   134  }
   135  
   136  func TestWatchCallMultipleInvocation(t *testing.T) {
   137  	cases := []struct {
   138  		name string
   139  		op   watch.EventType
   140  		ns   string
   141  	}{
   142  		{
   143  			"foo",
   144  			watch.Added,
   145  			"test_namespace",
   146  		},
   147  		{
   148  			"bar",
   149  			watch.Added,
   150  			"test_namespace",
   151  		},
   152  		{
   153  			"baz",
   154  			watch.Added,
   155  			"",
   156  		},
   157  		{
   158  			"bar",
   159  			watch.Modified,
   160  			"test_namespace",
   161  		},
   162  		{
   163  			"baz",
   164  			watch.Modified,
   165  			"",
   166  		},
   167  		{
   168  			"foo",
   169  			watch.Deleted,
   170  			"test_namespace",
   171  		},
   172  		{
   173  			"bar",
   174  			watch.Deleted,
   175  			"test_namespace",
   176  		},
   177  		{
   178  			"baz",
   179  			watch.Deleted,
   180  			"",
   181  		},
   182  	}
   183  
   184  	scheme := runtime.NewScheme()
   185  	codecs := serializer.NewCodecFactory(scheme)
   186  	testResource := schema.GroupVersionResource{Group: "", Version: "test_version", Resource: "test_kind"}
   187  
   188  	o := NewObjectTracker(scheme, codecs.UniversalDecoder())
   189  	watchNamespaces := []string{
   190  		"",
   191  		"",
   192  		"test_namespace",
   193  		"test_namespace",
   194  	}
   195  	var wg sync.WaitGroup
   196  	wg.Add(len(watchNamespaces))
   197  	for idx, watchNamespace := range watchNamespaces {
   198  		i := idx
   199  		watchNamespace := watchNamespace
   200  		w, err := o.Watch(testResource, watchNamespace)
   201  		if err != nil {
   202  			t.Fatalf("test resource watch failed in %s: %v", watchNamespace, err)
   203  		}
   204  		go func() {
   205  			assert.NoError(t, err, "watch invocation failed")
   206  			for _, c := range cases {
   207  				if watchNamespace == "" || c.ns == watchNamespace {
   208  					fmt.Printf("%#v %#v\n", c, i)
   209  					event := <-w.ResultChan()
   210  					accessor, err := meta.Accessor(event.Object)
   211  					if err != nil {
   212  						t.Errorf("unexpected error: %v", err)
   213  						break
   214  					}
   215  					assert.Equal(t, c.op, event.Type, "watch event mismatched")
   216  					assert.Equal(t, c.name, accessor.GetName(), "watched object mismatch")
   217  					assert.Equal(t, c.ns, accessor.GetNamespace(), "watched object mismatch")
   218  				}
   219  			}
   220  			wg.Done()
   221  		}()
   222  	}
   223  	for _, c := range cases {
   224  		switch c.op {
   225  		case watch.Added:
   226  			obj := getArbitraryResource(testResource, c.name, c.ns)
   227  			o.Create(testResource, obj, c.ns)
   228  		case watch.Modified:
   229  			obj := getArbitraryResource(testResource, c.name, c.ns)
   230  			o.Update(testResource, obj, c.ns)
   231  		case watch.Deleted:
   232  			o.Delete(testResource, c.ns, c.name)
   233  		}
   234  	}
   235  	wg.Wait()
   236  }
   237  
   238  func TestWatchAddAfterStop(t *testing.T) {
   239  	testResource := schema.GroupVersionResource{Group: "", Version: "test_version", Resource: "test_kind"}
   240  	testObj := getArbitraryResource(testResource, "test_name", "test_namespace")
   241  	accessor, err := meta.Accessor(testObj)
   242  	if err != nil {
   243  		t.Fatalf("unexpected error: %v", err)
   244  	}
   245  
   246  	ns := accessor.GetNamespace()
   247  	scheme := runtime.NewScheme()
   248  	codecs := serializer.NewCodecFactory(scheme)
   249  	o := NewObjectTracker(scheme, codecs.UniversalDecoder())
   250  	watch, err := o.Watch(testResource, ns)
   251  	if err != nil {
   252  		t.Errorf("watch creation failed: %v", err)
   253  	}
   254  
   255  	// When the watch is stopped it should ignore later events without panicking.
   256  	defer func() {
   257  		if r := recover(); r != nil {
   258  			t.Errorf("Watch panicked when it should have ignored create after stop: %v", r)
   259  		}
   260  	}()
   261  
   262  	watch.Stop()
   263  	err = o.Create(testResource, testObj, ns)
   264  	if err != nil {
   265  		t.Errorf("test resource creation failed: %v", err)
   266  	}
   267  }
   268  
   269  func TestPatchWithMissingObject(t *testing.T) {
   270  	nodesResource := schema.GroupVersionResource{Group: "", Version: "v1", Resource: "nodes"}
   271  
   272  	scheme := runtime.NewScheme()
   273  	codecs := serializer.NewCodecFactory(scheme)
   274  	o := NewObjectTracker(scheme, codecs.UniversalDecoder())
   275  	reaction := ObjectReaction(o)
   276  	action := NewRootPatchSubresourceAction(nodesResource, "node-1", types.StrategicMergePatchType, []byte(`{}`))
   277  	handled, node, err := reaction(action)
   278  	assert.True(t, handled)
   279  	assert.Nil(t, node)
   280  	assert.EqualError(t, err, `nodes "node-1" not found`)
   281  }
   282  
   283  func TestApplyCreate(t *testing.T) {
   284  	cmResource := schema.GroupVersionResource{Group: "", Version: "v1", Resource: "configMaps"}
   285  	scheme := runtime.NewScheme()
   286  	scheme.AddKnownTypes(cmResource.GroupVersion(), &v1.ConfigMap{})
   287  	codecs := serializer.NewCodecFactory(scheme)
   288  	o := NewFieldManagedObjectTracker(scheme, codecs.UniversalDecoder(), configMapTypeConverter(scheme))
   289  
   290  	reaction := ObjectReaction(o)
   291  	patch := []byte(`{"apiVersion": "v1", "kind": "ConfigMap", "metadata": {"name": "cm-1"}, "data": {"k": "v"}}`)
   292  	action := NewPatchActionWithOptions(cmResource, "default", "cm-1", types.ApplyPatchType, patch,
   293  		metav1.PatchOptions{FieldManager: "test-manager"})
   294  	handled, configMap, err := reaction(action)
   295  	assert.True(t, handled)
   296  	if err != nil {
   297  		t.Errorf("Failed to create a resource with apply: %v", err)
   298  	}
   299  	cm := configMap.(*v1.ConfigMap)
   300  	assert.Equal(t, cm.Data, map[string]string{"k": "v"})
   301  }
   302  
   303  func TestApplyUpdateMultipleFieldManagers(t *testing.T) {
   304  	cmResource := schema.GroupVersionResource{Group: "", Version: "v1", Resource: "configMaps"}
   305  	scheme := runtime.NewScheme()
   306  	scheme.AddKnownTypes(cmResource.GroupVersion(), &v1.ConfigMap{})
   307  	codecs := serializer.NewCodecFactory(scheme)
   308  	o := NewFieldManagedObjectTracker(scheme, codecs.UniversalDecoder(), configMapTypeConverter(scheme))
   309  
   310  	reaction := ObjectReaction(o)
   311  	action := NewCreateAction(cmResource, "default", &v1.ConfigMap{
   312  		TypeMeta: metav1.TypeMeta{
   313  			APIVersion: "v1",
   314  			Kind:       "ConfigMap",
   315  		},
   316  		ObjectMeta: metav1.ObjectMeta{
   317  			Name: "cm-1",
   318  		},
   319  		Data: map[string]string{
   320  			"k0": "v0",
   321  		},
   322  	})
   323  	handled, _, err := reaction(action)
   324  	assert.True(t, handled)
   325  	if err != nil {
   326  		t.Errorf("Failed to create resource: %v", err)
   327  	}
   328  
   329  	// Apply with test-manager-1
   330  	// Expect data to be shared with initial create
   331  	patch := []byte(`{"apiVersion": "v1", "kind": "ConfigMap", "metadata": {"name": "cm-1"}, "data": {"k1": "v1"}}`)
   332  	applyAction := NewPatchActionWithOptions(cmResource, "default", "cm-1", types.ApplyPatchType, patch,
   333  		metav1.PatchOptions{FieldManager: "test-manager-1"})
   334  	handled, configMap, err := reaction(applyAction)
   335  	assert.True(t, handled)
   336  	if err != nil {
   337  		t.Errorf("Failed to apply resource: %v", err)
   338  	}
   339  	cm := configMap.(*v1.ConfigMap)
   340  	assert.Equal(t, map[string]string{"k0": "v0", "k1": "v1"}, cm.Data)
   341  
   342  	// Apply conflicting with test-manager-2, expect apply to fail
   343  	patch = []byte(`{"apiVersion": "v1", "kind": "ConfigMap", "metadata": {"name": "cm-1"}, "data": {"k1": "xyz"}}`)
   344  	applyAction = NewPatchActionWithOptions(cmResource, "default", "cm-1", types.ApplyPatchType, patch,
   345  		metav1.PatchOptions{FieldManager: "test-manager-2"})
   346  	handled, _, err = reaction(applyAction)
   347  	assert.True(t, handled)
   348  	if assert.Error(t, err) {
   349  		assert.Equal(t, "Apply failed with 1 conflict: conflict with \"test-manager-1\": .data.k1", err.Error())
   350  	}
   351  
   352  	// Apply with test-manager-2
   353  	// Expect data to be shared with initial create and test-manager-1
   354  	patch = []byte(`{"apiVersion": "v1", "kind": "ConfigMap", "metadata": {"name": "cm-1"}, "data": {"k2": "v2"}}`)
   355  	applyAction = NewPatchActionWithOptions(cmResource, "default", "cm-1", types.ApplyPatchType, patch,
   356  		metav1.PatchOptions{FieldManager: "test-manager-2"})
   357  	handled, configMap, err = reaction(applyAction)
   358  	assert.True(t, handled)
   359  	if err != nil {
   360  		t.Errorf("Failed to apply resource: %v", err)
   361  	}
   362  	cm = configMap.(*v1.ConfigMap)
   363  	assert.Equal(t, map[string]string{"k0": "v0", "k1": "v1", "k2": "v2"}, cm.Data)
   364  
   365  	// Apply with test-manager-1
   366  	// Expect owned data to be updated
   367  	patch = []byte(`{"apiVersion": "v1", "kind": "ConfigMap", "metadata": {"name": "cm-1"}, "data": {"k1": "v101"}}`)
   368  	applyAction = NewPatchActionWithOptions(cmResource, "default", "cm-1", types.ApplyPatchType, patch,
   369  		metav1.PatchOptions{FieldManager: "test-manager-1"})
   370  	handled, configMap, err = reaction(applyAction)
   371  	assert.True(t, handled)
   372  	if err != nil {
   373  		t.Errorf("Failed to apply resource: %v", err)
   374  	}
   375  	cm = configMap.(*v1.ConfigMap)
   376  	assert.Equal(t, map[string]string{"k0": "v0", "k1": "v101", "k2": "v2"}, cm.Data)
   377  
   378  	// Force apply with test-manager-2
   379  	// Expect data owned by test-manager-1 to be updated, expect data already owned but not in apply configuration to be removed
   380  	patch = []byte(`{"apiVersion": "v1", "kind": "ConfigMap", "metadata": {"name": "cm-1"}, "data": {"k1": "v202"}}`)
   381  	applyAction = NewPatchActionWithOptions(cmResource, "default", "cm-1", types.ApplyPatchType, patch,
   382  		metav1.PatchOptions{FieldManager: "test-manager-2", Force: ptr.To(true)})
   383  	handled, configMap, err = reaction(applyAction)
   384  	assert.True(t, handled)
   385  	if err != nil {
   386  		t.Errorf("Failed to apply resource: %v", err)
   387  	}
   388  	cm = configMap.(*v1.ConfigMap)
   389  	assert.Equal(t, map[string]string{"k0": "v0", "k1": "v202"}, cm.Data)
   390  
   391  	// Update with test-manager-1 to perform a force update of the entire resource
   392  	reaction = ObjectReaction(o)
   393  	updateAction := NewUpdateActionWithOptions(cmResource, "default", &v1.ConfigMap{
   394  		TypeMeta: metav1.TypeMeta{
   395  			APIVersion: "v1",
   396  			Kind:       "ConfigMap",
   397  		},
   398  		ObjectMeta: metav1.ObjectMeta{
   399  			Name: "cm-1",
   400  		},
   401  		Data: map[string]string{
   402  			"k99": "v99",
   403  		},
   404  	}, metav1.UpdateOptions{FieldManager: "test-manager-1"})
   405  	handled, configMap, err = reaction(updateAction)
   406  	assert.True(t, handled)
   407  	if err != nil {
   408  		t.Errorf("Failed to apply resource: %v", err)
   409  	}
   410  	typedCm := configMap.(*v1.ConfigMap)
   411  	assert.Equal(t, map[string]string{"k99": "v99"}, typedCm.Data)
   412  }
   413  
   414  func TestGetWithExactMatch(t *testing.T) {
   415  	scheme := runtime.NewScheme()
   416  	codecs := serializer.NewCodecFactory(scheme)
   417  
   418  	constructObject := func(s schema.GroupVersionResource, name, namespace string) (*unstructured.Unstructured, schema.GroupVersionResource) {
   419  		obj := getArbitraryResource(s, name, namespace)
   420  		gvks, _, err := scheme.ObjectKinds(obj)
   421  		assert.NoError(t, err)
   422  		gvr, _ := meta.UnsafeGuessKindToResource(gvks[0])
   423  		return obj, gvr
   424  	}
   425  
   426  	var err error
   427  	// Object with empty namespace
   428  	o := NewObjectTracker(scheme, codecs.UniversalDecoder())
   429  	nodeResource := schema.GroupVersionResource{Group: "", Version: "v1", Resource: "node"}
   430  	node, gvr := constructObject(nodeResource, "node", "")
   431  
   432  	assert.Nil(t, o.Add(node))
   433  
   434  	// Exact match
   435  	_, err = o.Get(gvr, "", "node")
   436  	assert.NoError(t, err)
   437  
   438  	// Unexpected namespace provided
   439  	_, err = o.Get(gvr, "ns", "node")
   440  	assert.Error(t, err)
   441  	errNotFound := errors.NewNotFound(gvr.GroupResource(), "node")
   442  	assert.EqualError(t, err, errNotFound.Error())
   443  
   444  	// Object with non-empty namespace
   445  	o = NewObjectTracker(scheme, codecs.UniversalDecoder())
   446  	podResource := schema.GroupVersionResource{Group: "", Version: "v1", Resource: "pod"}
   447  	pod, gvr := constructObject(podResource, "pod", "default")
   448  	assert.Nil(t, o.Add(pod))
   449  
   450  	// Exact match
   451  	_, err = o.Get(gvr, "default", "pod")
   452  	assert.NoError(t, err)
   453  
   454  	// Missing namespace
   455  	_, err = o.Get(gvr, "", "pod")
   456  	assert.Error(t, err)
   457  	errNotFound = errors.NewNotFound(gvr.GroupResource(), "pod")
   458  	assert.EqualError(t, err, errNotFound.Error())
   459  }
   460  
   461  func Test_resourceCovers(t *testing.T) {
   462  	type args struct {
   463  		resource string
   464  		action   Action
   465  	}
   466  	tests := []struct {
   467  		name string
   468  		args args
   469  		want bool
   470  	}{
   471  		{
   472  			args: args{
   473  				resource: "*",
   474  				action:   ActionImpl{},
   475  			},
   476  			want: true,
   477  		},
   478  		{
   479  			args: args{
   480  				resource: "serviceaccounts",
   481  				action:   ActionImpl{},
   482  			},
   483  			want: false,
   484  		},
   485  		{
   486  			args: args{
   487  				resource: "serviceaccounts",
   488  				action: ActionImpl{
   489  					Resource: schema.GroupVersionResource{
   490  						Resource: "serviceaccounts",
   491  					},
   492  				},
   493  			},
   494  			want: true,
   495  		},
   496  		{
   497  			args: args{
   498  				resource: "serviceaccounts/token",
   499  				action: ActionImpl{
   500  					Resource: schema.GroupVersionResource{},
   501  				},
   502  			},
   503  			want: false,
   504  		},
   505  		{
   506  			args: args{
   507  				resource: "serviceaccounts/token",
   508  				action: ActionImpl{
   509  					Resource: schema.GroupVersionResource{
   510  						Resource: "serviceaccounts",
   511  					},
   512  				},
   513  			},
   514  			want: false,
   515  		},
   516  		{
   517  			args: args{
   518  				resource: "serviceaccounts/token",
   519  				action: ActionImpl{
   520  					Resource:    schema.GroupVersionResource{},
   521  					Subresource: "token",
   522  				},
   523  			},
   524  			want: false,
   525  		},
   526  		{
   527  			args: args{
   528  				resource: "serviceaccounts/token",
   529  				action: ActionImpl{
   530  					Resource: schema.GroupVersionResource{
   531  						Resource: "serviceaccounts",
   532  					},
   533  					Subresource: "token",
   534  				},
   535  			},
   536  			want: true,
   537  		},
   538  	}
   539  	for _, tt := range tests {
   540  		t.Run(tt.name, func(t *testing.T) {
   541  			if got := resourceCovers(tt.args.resource, tt.args.action); got != tt.want {
   542  				t.Errorf("resourceCovers() = %v, want %v", got, tt.want)
   543  			}
   544  		})
   545  	}
   546  }
   547  
   548  func configMapTypeConverter(scheme *runtime.Scheme) managedfields.TypeConverter {
   549  	parser, err := typed.NewParser(configMapTypedSchema)
   550  	if err != nil {
   551  		panic(fmt.Sprintf("Failed to parse schema: %v", err))
   552  	}
   553  
   554  	return TypeConverter{Scheme: scheme, TypeResolver: parser}
   555  }
   556  
   557  var configMapTypedSchema = typed.YAMLObject(`types:
   558  - name: io.k8s.api.core.v1.ConfigMap
   559    map:
   560      fields:
   561      - name: apiVersion
   562        type:
   563          scalar: string
   564      - name: data
   565        type:
   566          map:
   567            elementType:
   568              scalar: string
   569      - name: kind
   570        type:
   571          scalar: string
   572      - name: metadata
   573        type:
   574          namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta
   575        default: {}
   576  - name: io.k8s.apimachinery.pkg.apis.meta.v1.ObjectMeta
   577    map:
   578      fields:
   579      - name: creationTimestamp
   580        type:
   581          namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time
   582      - name: managedFields
   583        type:
   584          list:
   585            elementType:
   586              namedType: io.k8s.apimachinery.pkg.apis.meta.v1.ManagedFieldsEntry
   587            elementRelationship: atomic
   588      - name: name
   589        type:
   590          scalar: string
   591      - name: namespace
   592        type:
   593          scalar: string
   594  - name: io.k8s.apimachinery.pkg.apis.meta.v1.ManagedFieldsEntry
   595    map:
   596      fields:
   597      - name: apiVersion
   598        type:
   599          scalar: string
   600      - name: fieldsType
   601        type:
   602          scalar: string
   603      - name: fieldsV1
   604        type:
   605          namedType: io.k8s.apimachinery.pkg.apis.meta.v1.FieldsV1
   606      - name: manager
   607        type:
   608          scalar: string
   609      - name: operation
   610        type:
   611          scalar: string
   612      - name: subresource
   613        type:
   614          scalar: string
   615      - name: time
   616        type:
   617          namedType: io.k8s.apimachinery.pkg.apis.meta.v1.Time
   618  - name: io.k8s.apimachinery.pkg.apis.meta.v1.FieldsV1
   619    map:
   620      elementType:
   621        scalar: untyped
   622        list:
   623          elementType:
   624            namedType: __untyped_atomic_
   625          elementRelationship: atomic
   626        map:
   627          elementType:
   628            namedType: __untyped_deduced_
   629          elementRelationship: separable
   630  - name: io.k8s.apimachinery.pkg.apis.meta.v1.Time
   631    scalar: untyped
   632  - name: __untyped_deduced_
   633    scalar: untyped
   634    list:
   635      elementType:
   636        namedType: __untyped_atomic_
   637      elementRelationship: atomic
   638    map:
   639      elementType:
   640        namedType: __untyped_deduced_
   641      elementRelationship: separable
   642  `)