k8s.io/client-go@v0.22.2/metadata/fake/simple.go (about)

     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/labels"
    27  	"k8s.io/apimachinery/pkg/runtime"
    28  	"k8s.io/apimachinery/pkg/runtime/schema"
    29  	"k8s.io/apimachinery/pkg/runtime/serializer"
    30  	"k8s.io/apimachinery/pkg/types"
    31  	"k8s.io/apimachinery/pkg/watch"
    32  	"k8s.io/client-go/metadata"
    33  	"k8s.io/client-go/testing"
    34  )
    35  
    36  // MetadataClient assists in creating fake objects for use when testing, since metadata.Getter
    37  // does not expose create
    38  type MetadataClient interface {
    39  	metadata.Getter
    40  	CreateFake(obj *metav1.PartialObjectMetadata, opts metav1.CreateOptions, subresources ...string) (*metav1.PartialObjectMetadata, error)
    41  	UpdateFake(obj *metav1.PartialObjectMetadata, opts metav1.UpdateOptions, subresources ...string) (*metav1.PartialObjectMetadata, error)
    42  }
    43  
    44  // NewSimpleMetadataClient creates a new client that will use the provided scheme and respond with the
    45  // provided objects when requests are made. It will track actions made to the client which can be checked
    46  // with GetActions().
    47  func NewSimpleMetadataClient(scheme *runtime.Scheme, objects ...runtime.Object) *FakeMetadataClient {
    48  	gvkFakeList := schema.GroupVersionKind{Group: "fake-metadata-client-group", Version: "v1", Kind: "List"}
    49  	if !scheme.Recognizes(gvkFakeList) {
    50  		// In order to use List with this client, you have to have the v1.List registered in your scheme, since this is a test
    51  		// type we modify the input scheme
    52  		scheme.AddKnownTypeWithName(gvkFakeList, &metav1.List{})
    53  	}
    54  
    55  	codecs := serializer.NewCodecFactory(scheme)
    56  	o := testing.NewObjectTracker(scheme, codecs.UniversalDeserializer())
    57  	for _, obj := range objects {
    58  		if err := o.Add(obj); err != nil {
    59  			panic(err)
    60  		}
    61  	}
    62  
    63  	cs := &FakeMetadataClient{scheme: scheme}
    64  	cs.AddReactor("*", "*", testing.ObjectReaction(o))
    65  	cs.AddWatchReactor("*", func(action testing.Action) (handled bool, ret watch.Interface, err error) {
    66  		gvr := action.GetResource()
    67  		ns := action.GetNamespace()
    68  		watch, err := o.Watch(gvr, ns)
    69  		if err != nil {
    70  			return false, nil, err
    71  		}
    72  		return true, watch, nil
    73  	})
    74  
    75  	return cs
    76  }
    77  
    78  // FakeMetadataClient implements clientset.Interface. Meant to be embedded into a
    79  // struct to get a default implementation. This makes faking out just the method
    80  // you want to test easier.
    81  type FakeMetadataClient struct {
    82  	testing.Fake
    83  	scheme *runtime.Scheme
    84  }
    85  
    86  type metadataResourceClient struct {
    87  	client    *FakeMetadataClient
    88  	namespace string
    89  	resource  schema.GroupVersionResource
    90  }
    91  
    92  var _ metadata.Interface = &FakeMetadataClient{}
    93  
    94  // Resource returns an interface for accessing the provided resource.
    95  func (c *FakeMetadataClient) Resource(resource schema.GroupVersionResource) metadata.Getter {
    96  	return &metadataResourceClient{client: c, resource: resource}
    97  }
    98  
    99  // Namespace returns an interface for accessing the current resource in the specified
   100  // namespace.
   101  func (c *metadataResourceClient) Namespace(ns string) metadata.ResourceInterface {
   102  	ret := *c
   103  	ret.namespace = ns
   104  	return &ret
   105  }
   106  
   107  // CreateFake records the object creation and processes it via the reactor.
   108  func (c *metadataResourceClient) CreateFake(obj *metav1.PartialObjectMetadata, opts metav1.CreateOptions, subresources ...string) (*metav1.PartialObjectMetadata, error) {
   109  	var uncastRet runtime.Object
   110  	var err error
   111  	switch {
   112  	case len(c.namespace) == 0 && len(subresources) == 0:
   113  		uncastRet, err = c.client.Fake.
   114  			Invokes(testing.NewRootCreateAction(c.resource, obj), obj)
   115  
   116  	case len(c.namespace) == 0 && len(subresources) > 0:
   117  		var accessor metav1.Object // avoid shadowing err
   118  		accessor, err = meta.Accessor(obj)
   119  		if err != nil {
   120  			return nil, err
   121  		}
   122  		name := accessor.GetName()
   123  		uncastRet, err = c.client.Fake.
   124  			Invokes(testing.NewRootCreateSubresourceAction(c.resource, name, strings.Join(subresources, "/"), obj), obj)
   125  
   126  	case len(c.namespace) > 0 && len(subresources) == 0:
   127  		uncastRet, err = c.client.Fake.
   128  			Invokes(testing.NewCreateAction(c.resource, c.namespace, obj), obj)
   129  
   130  	case len(c.namespace) > 0 && len(subresources) > 0:
   131  		var accessor metav1.Object // avoid shadowing err
   132  		accessor, err = meta.Accessor(obj)
   133  		if err != nil {
   134  			return nil, err
   135  		}
   136  		name := accessor.GetName()
   137  		uncastRet, err = c.client.Fake.
   138  			Invokes(testing.NewCreateSubresourceAction(c.resource, name, strings.Join(subresources, "/"), c.namespace, obj), obj)
   139  
   140  	}
   141  
   142  	if err != nil {
   143  		return nil, err
   144  	}
   145  	if uncastRet == nil {
   146  		return nil, err
   147  	}
   148  	ret, ok := uncastRet.(*metav1.PartialObjectMetadata)
   149  	if !ok {
   150  		return nil, fmt.Errorf("unexpected return value type %T", uncastRet)
   151  	}
   152  	return ret, err
   153  }
   154  
   155  // UpdateFake records the object update and processes it via the reactor.
   156  func (c *metadataResourceClient) UpdateFake(obj *metav1.PartialObjectMetadata, opts metav1.UpdateOptions, subresources ...string) (*metav1.PartialObjectMetadata, error) {
   157  	var uncastRet runtime.Object
   158  	var err error
   159  	switch {
   160  	case len(c.namespace) == 0 && len(subresources) == 0:
   161  		uncastRet, err = c.client.Fake.
   162  			Invokes(testing.NewRootUpdateAction(c.resource, obj), obj)
   163  
   164  	case len(c.namespace) == 0 && len(subresources) > 0:
   165  		uncastRet, err = c.client.Fake.
   166  			Invokes(testing.NewRootUpdateSubresourceAction(c.resource, strings.Join(subresources, "/"), obj), obj)
   167  
   168  	case len(c.namespace) > 0 && len(subresources) == 0:
   169  		uncastRet, err = c.client.Fake.
   170  			Invokes(testing.NewUpdateAction(c.resource, c.namespace, obj), obj)
   171  
   172  	case len(c.namespace) > 0 && len(subresources) > 0:
   173  		uncastRet, err = c.client.Fake.
   174  			Invokes(testing.NewUpdateSubresourceAction(c.resource, strings.Join(subresources, "/"), c.namespace, obj), obj)
   175  
   176  	}
   177  
   178  	if err != nil {
   179  		return nil, err
   180  	}
   181  	if uncastRet == nil {
   182  		return nil, err
   183  	}
   184  	ret, ok := uncastRet.(*metav1.PartialObjectMetadata)
   185  	if !ok {
   186  		return nil, fmt.Errorf("unexpected return value type %T", uncastRet)
   187  	}
   188  	return ret, err
   189  }
   190  
   191  // UpdateStatus records the object status update and processes it via the reactor.
   192  func (c *metadataResourceClient) UpdateStatus(obj *metav1.PartialObjectMetadata, opts metav1.UpdateOptions) (*metav1.PartialObjectMetadata, error) {
   193  	var uncastRet runtime.Object
   194  	var err error
   195  	switch {
   196  	case len(c.namespace) == 0:
   197  		uncastRet, err = c.client.Fake.
   198  			Invokes(testing.NewRootUpdateSubresourceAction(c.resource, "status", obj), obj)
   199  
   200  	case len(c.namespace) > 0:
   201  		uncastRet, err = c.client.Fake.
   202  			Invokes(testing.NewUpdateSubresourceAction(c.resource, "status", c.namespace, obj), obj)
   203  
   204  	}
   205  
   206  	if err != nil {
   207  		return nil, err
   208  	}
   209  	if uncastRet == nil {
   210  		return nil, err
   211  	}
   212  	ret, ok := uncastRet.(*metav1.PartialObjectMetadata)
   213  	if !ok {
   214  		return nil, fmt.Errorf("unexpected return value type %T", uncastRet)
   215  	}
   216  	return ret, err
   217  }
   218  
   219  // Delete records the object deletion and processes it via the reactor.
   220  func (c *metadataResourceClient) Delete(ctx context.Context, name string, opts metav1.DeleteOptions, subresources ...string) error {
   221  	var err error
   222  	switch {
   223  	case len(c.namespace) == 0 && len(subresources) == 0:
   224  		_, err = c.client.Fake.
   225  			Invokes(testing.NewRootDeleteAction(c.resource, name), &metav1.Status{Status: "metadata delete fail"})
   226  
   227  	case len(c.namespace) == 0 && len(subresources) > 0:
   228  		_, err = c.client.Fake.
   229  			Invokes(testing.NewRootDeleteSubresourceAction(c.resource, strings.Join(subresources, "/"), name), &metav1.Status{Status: "metadata delete fail"})
   230  
   231  	case len(c.namespace) > 0 && len(subresources) == 0:
   232  		_, err = c.client.Fake.
   233  			Invokes(testing.NewDeleteAction(c.resource, c.namespace, name), &metav1.Status{Status: "metadata delete fail"})
   234  
   235  	case len(c.namespace) > 0 && len(subresources) > 0:
   236  		_, err = c.client.Fake.
   237  			Invokes(testing.NewDeleteSubresourceAction(c.resource, strings.Join(subresources, "/"), c.namespace, name), &metav1.Status{Status: "metadata delete fail"})
   238  	}
   239  
   240  	return err
   241  }
   242  
   243  // DeleteCollection records the object collection deletion and processes it via the reactor.
   244  func (c *metadataResourceClient) DeleteCollection(ctx context.Context, opts metav1.DeleteOptions, listOptions metav1.ListOptions) error {
   245  	var err error
   246  	switch {
   247  	case len(c.namespace) == 0:
   248  		action := testing.NewRootDeleteCollectionAction(c.resource, listOptions)
   249  		_, err = c.client.Fake.Invokes(action, &metav1.Status{Status: "metadata deletecollection fail"})
   250  
   251  	case len(c.namespace) > 0:
   252  		action := testing.NewDeleteCollectionAction(c.resource, c.namespace, listOptions)
   253  		_, err = c.client.Fake.Invokes(action, &metav1.Status{Status: "metadata deletecollection fail"})
   254  
   255  	}
   256  
   257  	return err
   258  }
   259  
   260  // Get records the object retrieval and processes it via the reactor.
   261  func (c *metadataResourceClient) Get(ctx context.Context, name string, opts metav1.GetOptions, subresources ...string) (*metav1.PartialObjectMetadata, error) {
   262  	var uncastRet runtime.Object
   263  	var err error
   264  	switch {
   265  	case len(c.namespace) == 0 && len(subresources) == 0:
   266  		uncastRet, err = c.client.Fake.
   267  			Invokes(testing.NewRootGetAction(c.resource, name), &metav1.Status{Status: "metadata get fail"})
   268  
   269  	case len(c.namespace) == 0 && len(subresources) > 0:
   270  		uncastRet, err = c.client.Fake.
   271  			Invokes(testing.NewRootGetSubresourceAction(c.resource, strings.Join(subresources, "/"), name), &metav1.Status{Status: "metadata get fail"})
   272  
   273  	case len(c.namespace) > 0 && len(subresources) == 0:
   274  		uncastRet, err = c.client.Fake.
   275  			Invokes(testing.NewGetAction(c.resource, c.namespace, name), &metav1.Status{Status: "metadata get fail"})
   276  
   277  	case len(c.namespace) > 0 && len(subresources) > 0:
   278  		uncastRet, err = c.client.Fake.
   279  			Invokes(testing.NewGetSubresourceAction(c.resource, c.namespace, strings.Join(subresources, "/"), name), &metav1.Status{Status: "metadata get fail"})
   280  	}
   281  
   282  	if err != nil {
   283  		return nil, err
   284  	}
   285  	if uncastRet == nil {
   286  		return nil, err
   287  	}
   288  	ret, ok := uncastRet.(*metav1.PartialObjectMetadata)
   289  	if !ok {
   290  		return nil, fmt.Errorf("unexpected return value type %T", uncastRet)
   291  	}
   292  	return ret, err
   293  }
   294  
   295  // List records the object deletion and processes it via the reactor.
   296  func (c *metadataResourceClient) List(ctx context.Context, opts metav1.ListOptions) (*metav1.PartialObjectMetadataList, error) {
   297  	var obj runtime.Object
   298  	var err error
   299  	switch {
   300  	case len(c.namespace) == 0:
   301  		obj, err = c.client.Fake.
   302  			Invokes(testing.NewRootListAction(c.resource, schema.GroupVersionKind{Group: "fake-metadata-client-group", Version: "v1", Kind: "" /*List is appended by the tracker automatically*/}, opts), &metav1.Status{Status: "metadata list fail"})
   303  
   304  	case len(c.namespace) > 0:
   305  		obj, err = c.client.Fake.
   306  			Invokes(testing.NewListAction(c.resource, schema.GroupVersionKind{Group: "fake-metadata-client-group", Version: "v1", Kind: "" /*List is appended by the tracker automatically*/}, c.namespace, opts), &metav1.Status{Status: "metadata list fail"})
   307  
   308  	}
   309  
   310  	if obj == nil {
   311  		return nil, err
   312  	}
   313  
   314  	label, _, _ := testing.ExtractFromListOptions(opts)
   315  	if label == nil {
   316  		label = labels.Everything()
   317  	}
   318  
   319  	inputList, ok := obj.(*metav1.List)
   320  	if !ok {
   321  		return nil, fmt.Errorf("incoming object is incorrect type %T", obj)
   322  	}
   323  
   324  	list := &metav1.PartialObjectMetadataList{
   325  		ListMeta: inputList.ListMeta,
   326  	}
   327  	for i := range inputList.Items {
   328  		item, ok := inputList.Items[i].Object.(*metav1.PartialObjectMetadata)
   329  		if !ok {
   330  			return nil, fmt.Errorf("item %d in list %T is %T", i, inputList, inputList.Items[i].Object)
   331  		}
   332  		metadata, err := meta.Accessor(item)
   333  		if err != nil {
   334  			return nil, err
   335  		}
   336  		if label.Matches(labels.Set(metadata.GetLabels())) {
   337  			list.Items = append(list.Items, *item)
   338  		}
   339  	}
   340  	return list, nil
   341  }
   342  
   343  func (c *metadataResourceClient) Watch(ctx context.Context, opts metav1.ListOptions) (watch.Interface, error) {
   344  	switch {
   345  	case len(c.namespace) == 0:
   346  		return c.client.Fake.
   347  			InvokesWatch(testing.NewRootWatchAction(c.resource, opts))
   348  
   349  	case len(c.namespace) > 0:
   350  		return c.client.Fake.
   351  			InvokesWatch(testing.NewWatchAction(c.resource, c.namespace, opts))
   352  
   353  	}
   354  
   355  	panic("math broke")
   356  }
   357  
   358  // Patch records the object patch and processes it via the reactor.
   359  func (c *metadataResourceClient) Patch(ctx context.Context, name string, pt types.PatchType, data []byte, opts metav1.PatchOptions, subresources ...string) (*metav1.PartialObjectMetadata, error) {
   360  	var uncastRet runtime.Object
   361  	var err error
   362  	switch {
   363  	case len(c.namespace) == 0 && len(subresources) == 0:
   364  		uncastRet, err = c.client.Fake.
   365  			Invokes(testing.NewRootPatchAction(c.resource, name, pt, data), &metav1.Status{Status: "metadata patch fail"})
   366  
   367  	case len(c.namespace) == 0 && len(subresources) > 0:
   368  		uncastRet, err = c.client.Fake.
   369  			Invokes(testing.NewRootPatchSubresourceAction(c.resource, name, pt, data, subresources...), &metav1.Status{Status: "metadata patch fail"})
   370  
   371  	case len(c.namespace) > 0 && len(subresources) == 0:
   372  		uncastRet, err = c.client.Fake.
   373  			Invokes(testing.NewPatchAction(c.resource, c.namespace, name, pt, data), &metav1.Status{Status: "metadata patch fail"})
   374  
   375  	case len(c.namespace) > 0 && len(subresources) > 0:
   376  		uncastRet, err = c.client.Fake.
   377  			Invokes(testing.NewPatchSubresourceAction(c.resource, c.namespace, name, pt, data, subresources...), &metav1.Status{Status: "metadata patch fail"})
   378  
   379  	}
   380  
   381  	if err != nil {
   382  		return nil, err
   383  	}
   384  	if uncastRet == nil {
   385  		return nil, err
   386  	}
   387  	ret, ok := uncastRet.(*metav1.PartialObjectMetadata)
   388  	if !ok {
   389  		return nil, fmt.Errorf("unexpected return value type %T", uncastRet)
   390  	}
   391  	return ret, err
   392  }