...

Source file src/k8s.io/client-go/dynamic/fake/simple.go

Documentation: k8s.io/client-go/dynamic/fake

     1  /*
     2  Copyright 2018 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 fake
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"strings"
    23  
    24  	"k8s.io/apimachinery/pkg/api/meta"
    25  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    26  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    27  	"k8s.io/apimachinery/pkg/labels"
    28  	"k8s.io/apimachinery/pkg/runtime"
    29  	"k8s.io/apimachinery/pkg/runtime/schema"
    30  	"k8s.io/apimachinery/pkg/runtime/serializer"
    31  	"k8s.io/apimachinery/pkg/types"
    32  	"k8s.io/apimachinery/pkg/watch"
    33  	"k8s.io/client-go/dynamic"
    34  	"k8s.io/client-go/testing"
    35  )
    36  
    37  func NewSimpleDynamicClient(scheme *runtime.Scheme, objects ...runtime.Object) *FakeDynamicClient {
    38  	unstructuredScheme := runtime.NewScheme()
    39  	for gvk := range scheme.AllKnownTypes() {
    40  		if unstructuredScheme.Recognizes(gvk) {
    41  			continue
    42  		}
    43  		if strings.HasSuffix(gvk.Kind, "List") {
    44  			unstructuredScheme.AddKnownTypeWithName(gvk, &unstructured.UnstructuredList{})
    45  			continue
    46  		}
    47  		unstructuredScheme.AddKnownTypeWithName(gvk, &unstructured.Unstructured{})
    48  	}
    49  
    50  	objects, err := convertObjectsToUnstructured(scheme, objects)
    51  	if err != nil {
    52  		panic(err)
    53  	}
    54  
    55  	for _, obj := range objects {
    56  		gvk := obj.GetObjectKind().GroupVersionKind()
    57  		if !unstructuredScheme.Recognizes(gvk) {
    58  			unstructuredScheme.AddKnownTypeWithName(gvk, &unstructured.Unstructured{})
    59  		}
    60  		gvk.Kind += "List"
    61  		if !unstructuredScheme.Recognizes(gvk) {
    62  			unstructuredScheme.AddKnownTypeWithName(gvk, &unstructured.UnstructuredList{})
    63  		}
    64  	}
    65  
    66  	return NewSimpleDynamicClientWithCustomListKinds(unstructuredScheme, nil, objects...)
    67  }
    68  
    69  // NewSimpleDynamicClientWithCustomListKinds try not to use this.  In general you want to have the scheme have the List types registered
    70  // and allow the default guessing for resources match.  Sometimes that doesn't work, so you can specify a custom mapping here.
    71  func NewSimpleDynamicClientWithCustomListKinds(scheme *runtime.Scheme, gvrToListKind map[schema.GroupVersionResource]string, objects ...runtime.Object) *FakeDynamicClient {
    72  	// In order to use List with this client, you have to have your lists registered so that the object tracker will find them
    73  	// in the scheme to support the t.scheme.New(listGVK) call when it's building the return value.
    74  	// Since the base fake client needs the listGVK passed through the action (in cases where there are no instances, it
    75  	// cannot look up the actual hits), we need to know a mapping of GVR to listGVK here.  For GETs and other types of calls,
    76  	// there is no return value that contains a GVK, so it doesn't have to know the mapping in advance.
    77  
    78  	// first we attempt to invert known List types from the scheme to auto guess the resource with unsafe guesses
    79  	// this covers common usage of registering types in scheme and passing them
    80  	completeGVRToListKind := map[schema.GroupVersionResource]string{}
    81  	for listGVK := range scheme.AllKnownTypes() {
    82  		if !strings.HasSuffix(listGVK.Kind, "List") {
    83  			continue
    84  		}
    85  		nonListGVK := listGVK.GroupVersion().WithKind(listGVK.Kind[:len(listGVK.Kind)-4])
    86  		plural, _ := meta.UnsafeGuessKindToResource(nonListGVK)
    87  		completeGVRToListKind[plural] = listGVK.Kind
    88  	}
    89  
    90  	for gvr, listKind := range gvrToListKind {
    91  		if !strings.HasSuffix(listKind, "List") {
    92  			panic("coding error, listGVK must end in List or this fake client doesn't work right")
    93  		}
    94  		listGVK := gvr.GroupVersion().WithKind(listKind)
    95  
    96  		// if we already have this type registered, just skip it
    97  		if _, err := scheme.New(listGVK); err == nil {
    98  			completeGVRToListKind[gvr] = listKind
    99  			continue
   100  		}
   101  
   102  		scheme.AddKnownTypeWithName(listGVK, &unstructured.UnstructuredList{})
   103  		completeGVRToListKind[gvr] = listKind
   104  	}
   105  
   106  	codecs := serializer.NewCodecFactory(scheme)
   107  	o := testing.NewObjectTracker(scheme, codecs.UniversalDecoder())
   108  	for _, obj := range objects {
   109  		if err := o.Add(obj); err != nil {
   110  			panic(err)
   111  		}
   112  	}
   113  
   114  	cs := &FakeDynamicClient{scheme: scheme, gvrToListKind: completeGVRToListKind, tracker: o}
   115  	cs.AddReactor("*", "*", testing.ObjectReaction(o))
   116  	cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) {
   117  		gvr := action.GetResource()
   118  		ns := action.GetNamespace()
   119  		watch, err := o.Watch(gvr, ns)
   120  		if err != nil {
   121  			return false, nil, err
   122  		}
   123  		return true, watch, nil
   124  	})
   125  
   126  	return cs
   127  }
   128  
   129  // Clientset implements clientset.Interface. Meant to be embedded into a
   130  // struct to get a default implementation. This makes faking out just the method
   131  // you want to test easier.
   132  type FakeDynamicClient struct {
   133  	testing.Fake
   134  	scheme        *runtime.Scheme
   135  	gvrToListKind map[schema.GroupVersionResource]string
   136  	tracker       testing.ObjectTracker
   137  }
   138  
   139  type dynamicResourceClient struct {
   140  	client    *FakeDynamicClient
   141  	namespace string
   142  	resource  schema.GroupVersionResource
   143  	listKind  string
   144  }
   145  
   146  var (
   147  	_ dynamic.Interface  = &FakeDynamicClient{}
   148  	_ testing.FakeClient = &FakeDynamicClient{}
   149  )
   150  
   151  func (c *FakeDynamicClient) Tracker() testing.ObjectTracker {
   152  	return c.tracker
   153  }
   154  
   155  func (c *FakeDynamicClient) Resource(resource schema.GroupVersionResource) dynamic.NamespaceableResourceInterface {
   156  	return &dynamicResourceClient{client: c, resource: resource, listKind: c.gvrToListKind[resource]}
   157  }
   158  
   159  func (c *dynamicResourceClient) Namespace(ns string) dynamic.ResourceInterface {
   160  	ret := *c
   161  	ret.namespace = ns
   162  	return &ret
   163  }
   164  
   165  func (c *dynamicResourceClient) Create(ctx context.Context, obj *unstructured.Unstructured, opts metav1.CreateOptions, subresources ...string) (*unstructured.Unstructured, error) {
   166  	var uncastRet runtime.Object
   167  	var err error
   168  	switch {
   169  	case len(c.namespace) == 0 && len(subresources) == 0:
   170  		uncastRet, err = c.client.Fake.
   171  			Invokes(testing.NewRootCreateAction(c.resource, obj), obj)
   172  
   173  	case len(c.namespace) == 0 && len(subresources) > 0:
   174  		var accessor metav1.Object // avoid shadowing err
   175  		accessor, err = meta.Accessor(obj)
   176  		if err != nil {
   177  			return nil, err
   178  		}
   179  		name := accessor.GetName()
   180  		uncastRet, err = c.client.Fake.
   181  			Invokes(testing.NewRootCreateSubresourceAction(c.resource, name, strings.Join(subresources, "/"), obj), obj)
   182  
   183  	case len(c.namespace) > 0 && len(subresources) == 0:
   184  		uncastRet, err = c.client.Fake.
   185  			Invokes(testing.NewCreateAction(c.resource, c.namespace, obj), obj)
   186  
   187  	case len(c.namespace) > 0 && len(subresources) > 0:
   188  		var accessor metav1.Object // avoid shadowing err
   189  		accessor, err = meta.Accessor(obj)
   190  		if err != nil {
   191  			return nil, err
   192  		}
   193  		name := accessor.GetName()
   194  		uncastRet, err = c.client.Fake.
   195  			Invokes(testing.NewCreateSubresourceAction(c.resource, name, strings.Join(subresources, "/"), c.namespace, obj), obj)
   196  
   197  	}
   198  
   199  	if err != nil {
   200  		return nil, err
   201  	}
   202  	if uncastRet == nil {
   203  		return nil, err
   204  	}
   205  
   206  	ret := &unstructured.Unstructured{}
   207  	if err := c.client.scheme.Convert(uncastRet, ret, nil); err != nil {
   208  		return nil, err
   209  	}
   210  	return ret, err
   211  }
   212  
   213  func (c *dynamicResourceClient) Update(ctx context.Context, obj *unstructured.Unstructured, opts metav1.UpdateOptions, subresources ...string) (*unstructured.Unstructured, error) {
   214  	var uncastRet runtime.Object
   215  	var err error
   216  	switch {
   217  	case len(c.namespace) == 0 && len(subresources) == 0:
   218  		uncastRet, err = c.client.Fake.
   219  			Invokes(testing.NewRootUpdateAction(c.resource, obj), obj)
   220  
   221  	case len(c.namespace) == 0 && len(subresources) > 0:
   222  		uncastRet, err = c.client.Fake.
   223  			Invokes(testing.NewRootUpdateSubresourceAction(c.resource, strings.Join(subresources, "/"), obj), obj)
   224  
   225  	case len(c.namespace) > 0 && len(subresources) == 0:
   226  		uncastRet, err = c.client.Fake.
   227  			Invokes(testing.NewUpdateAction(c.resource, c.namespace, obj), obj)
   228  
   229  	case len(c.namespace) > 0 && len(subresources) > 0:
   230  		uncastRet, err = c.client.Fake.
   231  			Invokes(testing.NewUpdateSubresourceAction(c.resource, strings.Join(subresources, "/"), c.namespace, obj), obj)
   232  
   233  	}
   234  
   235  	if err != nil {
   236  		return nil, err
   237  	}
   238  	if uncastRet == nil {
   239  		return nil, err
   240  	}
   241  
   242  	ret := &unstructured.Unstructured{}
   243  	if err := c.client.scheme.Convert(uncastRet, ret, nil); err != nil {
   244  		return nil, err
   245  	}
   246  	return ret, err
   247  }
   248  
   249  func (c *dynamicResourceClient) UpdateStatus(ctx context.Context, obj *unstructured.Unstructured, opts metav1.UpdateOptions) (*unstructured.Unstructured, error) {
   250  	var uncastRet runtime.Object
   251  	var err error
   252  	switch {
   253  	case len(c.namespace) == 0:
   254  		uncastRet, err = c.client.Fake.
   255  			Invokes(testing.NewRootUpdateSubresourceAction(c.resource, "status", obj), obj)
   256  
   257  	case len(c.namespace) > 0:
   258  		uncastRet, err = c.client.Fake.
   259  			Invokes(testing.NewUpdateSubresourceAction(c.resource, "status", c.namespace, obj), obj)
   260  
   261  	}
   262  
   263  	if err != nil {
   264  		return nil, err
   265  	}
   266  	if uncastRet == nil {
   267  		return nil, err
   268  	}
   269  
   270  	ret := &unstructured.Unstructured{}
   271  	if err := c.client.scheme.Convert(uncastRet, ret, nil); err != nil {
   272  		return nil, err
   273  	}
   274  	return ret, err
   275  }
   276  
   277  func (c *dynamicResourceClient) Delete(ctx context.Context, name string, opts metav1.DeleteOptions, subresources ...string) error {
   278  	var err error
   279  	switch {
   280  	case len(c.namespace) == 0 && len(subresources) == 0:
   281  		_, err = c.client.Fake.
   282  			Invokes(testing.NewRootDeleteAction(c.resource, name), &metav1.Status{Status: "dynamic delete fail"})
   283  
   284  	case len(c.namespace) == 0 && len(subresources) > 0:
   285  		_, err = c.client.Fake.
   286  			Invokes(testing.NewRootDeleteSubresourceAction(c.resource, strings.Join(subresources, "/"), name), &metav1.Status{Status: "dynamic delete fail"})
   287  
   288  	case len(c.namespace) > 0 && len(subresources) == 0:
   289  		_, err = c.client.Fake.
   290  			Invokes(testing.NewDeleteAction(c.resource, c.namespace, name), &metav1.Status{Status: "dynamic delete fail"})
   291  
   292  	case len(c.namespace) > 0 && len(subresources) > 0:
   293  		_, err = c.client.Fake.
   294  			Invokes(testing.NewDeleteSubresourceAction(c.resource, strings.Join(subresources, "/"), c.namespace, name), &metav1.Status{Status: "dynamic delete fail"})
   295  	}
   296  
   297  	return err
   298  }
   299  
   300  func (c *dynamicResourceClient) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOptions metav1.ListOptions) error {
   301  	var err error
   302  	switch {
   303  	case len(c.namespace) == 0:
   304  		action := testing.NewRootDeleteCollectionAction(c.resource, listOptions)
   305  		_, err = c.client.Fake.Invokes(action, &metav1.Status{Status: "dynamic deletecollection fail"})
   306  
   307  	case len(c.namespace) > 0:
   308  		action := testing.NewDeleteCollectionAction(c.resource, c.namespace, listOptions)
   309  		_, err = c.client.Fake.Invokes(action, &metav1.Status{Status: "dynamic deletecollection fail"})
   310  
   311  	}
   312  
   313  	return err
   314  }
   315  
   316  func (c *dynamicResourceClient) Get(ctx context.Context, name string, opts metav1.GetOptions, subresources ...string) (*unstructured.Unstructured, error) {
   317  	var uncastRet runtime.Object
   318  	var err error
   319  	switch {
   320  	case len(c.namespace) == 0 && len(subresources) == 0:
   321  		uncastRet, err = c.client.Fake.
   322  			Invokes(testing.NewRootGetAction(c.resource, name), &metav1.Status{Status: "dynamic get fail"})
   323  
   324  	case len(c.namespace) == 0 && len(subresources) > 0:
   325  		uncastRet, err = c.client.Fake.
   326  			Invokes(testing.NewRootGetSubresourceAction(c.resource, strings.Join(subresources, "/"), name), &metav1.Status{Status: "dynamic get fail"})
   327  
   328  	case len(c.namespace) > 0 && len(subresources) == 0:
   329  		uncastRet, err = c.client.Fake.
   330  			Invokes(testing.NewGetAction(c.resource, c.namespace, name), &metav1.Status{Status: "dynamic get fail"})
   331  
   332  	case len(c.namespace) > 0 && len(subresources) > 0:
   333  		uncastRet, err = c.client.Fake.
   334  			Invokes(testing.NewGetSubresourceAction(c.resource, c.namespace, strings.Join(subresources, "/"), name), &metav1.Status{Status: "dynamic get fail"})
   335  	}
   336  
   337  	if err != nil {
   338  		return nil, err
   339  	}
   340  	if uncastRet == nil {
   341  		return nil, err
   342  	}
   343  
   344  	ret := &unstructured.Unstructured{}
   345  	if err := c.client.scheme.Convert(uncastRet, ret, nil); err != nil {
   346  		return nil, err
   347  	}
   348  	return ret, err
   349  }
   350  
   351  func (c *dynamicResourceClient) List(ctx context.Context, opts metav1.ListOptions) (*unstructured.UnstructuredList, error) {
   352  	if len(c.listKind) == 0 {
   353  		panic(fmt.Sprintf("coding error: you must register resource to list kind for every resource you're going to LIST when creating the client.  See NewSimpleDynamicClientWithCustomListKinds or register the list into the scheme: %v out of %v", c.resource, c.client.gvrToListKind))
   354  	}
   355  	listGVK := c.resource.GroupVersion().WithKind(c.listKind)
   356  	listForFakeClientGVK := c.resource.GroupVersion().WithKind(c.listKind[:len(c.listKind)-4]) /*base library appends List*/
   357  
   358  	var obj runtime.Object
   359  	var err error
   360  	switch {
   361  	case len(c.namespace) == 0:
   362  		obj, err = c.client.Fake.
   363  			Invokes(testing.NewRootListAction(c.resource, listForFakeClientGVK, opts), &metav1.Status{Status: "dynamic list fail"})
   364  
   365  	case len(c.namespace) > 0:
   366  		obj, err = c.client.Fake.
   367  			Invokes(testing.NewListAction(c.resource, listForFakeClientGVK, c.namespace, opts), &metav1.Status{Status: "dynamic list fail"})
   368  
   369  	}
   370  
   371  	if obj == nil {
   372  		return nil, err
   373  	}
   374  
   375  	label, _, _ := testing.ExtractFromListOptions(opts)
   376  	if label == nil {
   377  		label = labels.Everything()
   378  	}
   379  
   380  	retUnstructured := &unstructured.Unstructured{}
   381  	if err := c.client.scheme.Convert(obj, retUnstructured, nil); err != nil {
   382  		return nil, err
   383  	}
   384  	entireList, err := retUnstructured.ToList()
   385  	if err != nil {
   386  		return nil, err
   387  	}
   388  
   389  	list := &unstructured.UnstructuredList{}
   390  	list.SetRemainingItemCount(entireList.GetRemainingItemCount())
   391  	list.SetResourceVersion(entireList.GetResourceVersion())
   392  	list.SetContinue(entireList.GetContinue())
   393  	list.GetObjectKind().SetGroupVersionKind(listGVK)
   394  	for i := range entireList.Items {
   395  		item := &entireList.Items[i]
   396  		metadata, err := meta.Accessor(item)
   397  		if err != nil {
   398  			return nil, err
   399  		}
   400  		if label.Matches(labels.Set(metadata.GetLabels())) {
   401  			list.Items = append(list.Items, *item)
   402  		}
   403  	}
   404  	return list, nil
   405  }
   406  
   407  func (c *dynamicResourceClient) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {
   408  	switch {
   409  	case len(c.namespace) == 0:
   410  		return c.client.Fake.
   411  			InvokesWatch(testing.NewRootWatchAction(c.resource, opts))
   412  
   413  	case len(c.namespace) > 0:
   414  		return c.client.Fake.
   415  			InvokesWatch(testing.NewWatchAction(c.resource, c.namespace, opts))
   416  
   417  	}
   418  
   419  	panic("math broke")
   420  }
   421  
   422  // TODO: opts are currently ignored.
   423  func (c *dynamicResourceClient) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (*unstructured.Unstructured, error) {
   424  	var uncastRet runtime.Object
   425  	var err error
   426  	switch {
   427  	case len(c.namespace) == 0 && len(subresources) == 0:
   428  		uncastRet, err = c.client.Fake.
   429  			Invokes(testing.NewRootPatchAction(c.resource, name, pt, data), &metav1.Status{Status: "dynamic patch fail"})
   430  
   431  	case len(c.namespace) == 0 && len(subresources) > 0:
   432  		uncastRet, err = c.client.Fake.
   433  			Invokes(testing.NewRootPatchSubresourceAction(c.resource, name, pt, data, subresources...), &metav1.Status{Status: "dynamic patch fail"})
   434  
   435  	case len(c.namespace) > 0 && len(subresources) == 0:
   436  		uncastRet, err = c.client.Fake.
   437  			Invokes(testing.NewPatchAction(c.resource, c.namespace, name, pt, data), &metav1.Status{Status: "dynamic patch fail"})
   438  
   439  	case len(c.namespace) > 0 && len(subresources) > 0:
   440  		uncastRet, err = c.client.Fake.
   441  			Invokes(testing.NewPatchSubresourceAction(c.resource, c.namespace, name, pt, data, subresources...), &metav1.Status{Status: "dynamic patch fail"})
   442  
   443  	}
   444  
   445  	if err != nil {
   446  		return nil, err
   447  	}
   448  	if uncastRet == nil {
   449  		return nil, err
   450  	}
   451  
   452  	ret := &unstructured.Unstructured{}
   453  	if err := c.client.scheme.Convert(uncastRet, ret, nil); err != nil {
   454  		return nil, err
   455  	}
   456  	return ret, err
   457  }
   458  
   459  // TODO: opts are currently ignored.
   460  func (c *dynamicResourceClient) Apply(ctx context.Context, name string, obj *unstructured.Unstructured, options metav1.ApplyOptions, subresources ...string) (*unstructured.Unstructured, error) {
   461  	outBytes, err := runtime.Encode(unstructured.UnstructuredJSONScheme, obj)
   462  	if err != nil {
   463  		return nil, err
   464  	}
   465  	var uncastRet runtime.Object
   466  	switch {
   467  	case len(c.namespace) == 0 && len(subresources) == 0:
   468  		uncastRet, err = c.client.Fake.
   469  			Invokes(testing.NewRootPatchAction(c.resource, name, types.ApplyPatchType, outBytes), &metav1.Status{Status: "dynamic patch fail"})
   470  
   471  	case len(c.namespace) == 0 && len(subresources) > 0:
   472  		uncastRet, err = c.client.Fake.
   473  			Invokes(testing.NewRootPatchSubresourceAction(c.resource, name, types.ApplyPatchType, outBytes, subresources...), &metav1.Status{Status: "dynamic patch fail"})
   474  
   475  	case len(c.namespace) > 0 && len(subresources) == 0:
   476  		uncastRet, err = c.client.Fake.
   477  			Invokes(testing.NewPatchAction(c.resource, c.namespace, name, types.ApplyPatchType, outBytes), &metav1.Status{Status: "dynamic patch fail"})
   478  
   479  	case len(c.namespace) > 0 && len(subresources) > 0:
   480  		uncastRet, err = c.client.Fake.
   481  			Invokes(testing.NewPatchSubresourceAction(c.resource, c.namespace, name, types.ApplyPatchType, outBytes, subresources...), &metav1.Status{Status: "dynamic patch fail"})
   482  
   483  	}
   484  
   485  	if err != nil {
   486  		return nil, err
   487  	}
   488  	if uncastRet == nil {
   489  		return nil, err
   490  	}
   491  
   492  	ret := &unstructured.Unstructured{}
   493  	if err := c.client.scheme.Convert(uncastRet, ret, nil); err != nil {
   494  		return nil, err
   495  	}
   496  	return ret, nil
   497  }
   498  
   499  func (c *dynamicResourceClient) ApplyStatus(ctx context.Context, name string, obj *unstructured.Unstructured, options metav1.ApplyOptions) (*unstructured.Unstructured, error) {
   500  	return c.Apply(ctx, name, obj, options, "status")
   501  }
   502  
   503  func convertObjectsToUnstructured(s *runtime.Scheme, objs []runtime.Object) ([]runtime.Object, error) {
   504  	ul := make([]runtime.Object, 0, len(objs))
   505  
   506  	for _, obj := range objs {
   507  		u, err := convertToUnstructured(s, obj)
   508  		if err != nil {
   509  			return nil, err
   510  		}
   511  
   512  		ul = append(ul, u)
   513  	}
   514  	return ul, nil
   515  }
   516  
   517  func convertToUnstructured(s *runtime.Scheme, obj runtime.Object) (runtime.Object, error) {
   518  	var (
   519  		err error
   520  		u   unstructured.Unstructured
   521  	)
   522  
   523  	u.Object, err = runtime.DefaultUnstructuredConverter.ToUnstructured(obj)
   524  	if err != nil {
   525  		return nil, fmt.Errorf("failed to convert to unstructured: %w", err)
   526  	}
   527  
   528  	gvk := u.GroupVersionKind()
   529  	if gvk.Group == "" || gvk.Kind == "" {
   530  		gvks, _, err := s.ObjectKinds(obj)
   531  		if err != nil {
   532  			return nil, fmt.Errorf("failed to convert to unstructured - unable to get GVK %w", err)
   533  		}
   534  		apiv, k := gvks[0].ToAPIVersionAndKind()
   535  		u.SetAPIVersion(apiv)
   536  		u.SetKind(k)
   537  	}
   538  	return &u, nil
   539  }
   540  

View as plain text