github.com/operator-framework/operator-lifecycle-manager@v0.30.0/pkg/lib/clientfake/decorator.go (about)

     1  package clientfake
     2  
     3  import (
     4  	"k8s.io/apimachinery/pkg/runtime"
     5  	fake "k8s.io/client-go/kubernetes/fake"
     6  	"k8s.io/client-go/testing"
     7  )
     8  
     9  // This is used to prepend reactors to the k8s fake client. should be removed when client go is updated.
    10  // TODO: see if we can merge the OLM ClientsetDecorator and this one.
    11  
    12  // ClientsetDecorator defines decorator methods for a Clientset.
    13  type ClientsetDecorator interface {
    14  	// PrependReactor adds a reactor to the beginning of the chain.
    15  	PrependReactor(verb, resource string, reaction testing.ReactionFunc)
    16  }
    17  
    18  // ReactionForwardingClientsetDecorator wraps a Clientset and "forwards" Action object mutations
    19  // from all successful non-handling Reactors along the chain to the first handling Reactor. This is
    20  // is a stopgap until we can upgrade to client-go v11.0, where the behavior is the default
    21  // (see https://github.com/kubernetes/client-go/blob/6ee68ca5fd8355d024d02f9db0b3b667e8357a0f/testing/fake.go#L130).
    22  type ReactionForwardingClientsetDecorator struct {
    23  	fake.Clientset
    24  	ReactionChain []testing.Reactor // shadow embedded ReactionChain
    25  	actions       []testing.Action  // these may be castable to other types, but "Action" is the minimum
    26  }
    27  
    28  // NewReactionForwardingClientsetDecorator returns the ReactionForwardingClientsetDecorator wrapped Clientset result
    29  // of calling NewSimpleClientset with the given objects.
    30  func NewReactionForwardingClientsetDecorator(objects []runtime.Object, options ...Option) *ReactionForwardingClientsetDecorator {
    31  	decorator := &ReactionForwardingClientsetDecorator{
    32  		Clientset: *fake.NewSimpleClientset(objects...),
    33  	}
    34  
    35  	// Swap out the embedded ReactionChain with a Reactor that reduces over the decorator's ReactionChain.
    36  	decorator.ReactionChain = decorator.Clientset.ReactionChain
    37  	decorator.Clientset.ReactionChain = []testing.Reactor{&testing.SimpleReactor{Verb: "*", Resource: "*", Reaction: decorator.reduceReactions}}
    38  
    39  	// Apply options
    40  	for _, option := range options {
    41  		option(decorator)
    42  	}
    43  
    44  	return decorator
    45  }
    46  
    47  // reduceReactions reduces over all reactions in the chain while "forwarding" Action object mutations
    48  // from all successful non-handling Reactors along the chain to the first handling Reactor.
    49  func (c *ReactionForwardingClientsetDecorator) reduceReactions(action testing.Action) (handled bool, ret runtime.Object, err error) {
    50  	// The embedded Client set is already locked, so there's no need to lock again
    51  	actionCopy := action.DeepCopy()
    52  	c.actions = append(c.actions, action.DeepCopy())
    53  	for _, reactor := range c.ReactionChain {
    54  		if !reactor.Handles(actionCopy) {
    55  			continue
    56  		}
    57  
    58  		handled, ret, err = reactor.React(actionCopy)
    59  		if !handled {
    60  			continue
    61  		}
    62  
    63  		return
    64  	}
    65  
    66  	return
    67  }
    68  
    69  // PrependReactor adds a reactor to the beginning of the chain.
    70  func (c *ReactionForwardingClientsetDecorator) PrependReactor(verb, resource string, reaction testing.ReactionFunc) {
    71  	c.ReactionChain = append([]testing.Reactor{&testing.SimpleReactor{Verb: verb, Resource: resource, Reaction: reaction}}, c.ReactionChain...)
    72  }