k8s.io/client-go@v0.31.1/testing/fake.go (about) 1 /* 2 Copyright 2016 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 "sync" 22 23 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" 24 "k8s.io/apimachinery/pkg/runtime" 25 "k8s.io/apimachinery/pkg/watch" 26 restclient "k8s.io/client-go/rest" 27 ) 28 29 // Fake implements client.Interface. Meant to be embedded into a struct to get 30 // a default implementation. This makes faking out just the method you want to 31 // test easier. 32 type Fake struct { 33 sync.RWMutex 34 actions []Action // these may be castable to other types, but "Action" is the minimum 35 36 // ReactionChain is the list of reactors that will be attempted for every 37 // request in the order they are tried. 38 ReactionChain []Reactor 39 // WatchReactionChain is the list of watch reactors that will be attempted 40 // for every request in the order they are tried. 41 WatchReactionChain []WatchReactor 42 // ProxyReactionChain is the list of proxy reactors that will be attempted 43 // for every request in the order they are tried. 44 ProxyReactionChain []ProxyReactor 45 46 Resources []*metav1.APIResourceList 47 } 48 49 // Reactor is an interface to allow the composition of reaction functions. 50 type Reactor interface { 51 // Handles indicates whether or not this Reactor deals with a given 52 // action. 53 Handles(action Action) bool 54 // React handles the action and returns results. It may choose to 55 // delegate by indicated handled=false. 56 React(action Action) (handled bool, ret runtime.Object, err error) 57 } 58 59 // WatchReactor is an interface to allow the composition of watch functions. 60 type WatchReactor interface { 61 // Handles indicates whether or not this Reactor deals with a given 62 // action. 63 Handles(action Action) bool 64 // React handles a watch action and returns results. It may choose to 65 // delegate by indicating handled=false. 66 React(action Action) (handled bool, ret watch.Interface, err error) 67 } 68 69 // ProxyReactor is an interface to allow the composition of proxy get 70 // functions. 71 type ProxyReactor interface { 72 // Handles indicates whether or not this Reactor deals with a given 73 // action. 74 Handles(action Action) bool 75 // React handles a watch action and returns results. It may choose to 76 // delegate by indicating handled=false. 77 React(action Action) (handled bool, ret restclient.ResponseWrapper, err error) 78 } 79 80 // ReactionFunc is a function that returns an object or error for a given 81 // Action. If "handled" is false, then the test client will ignore the 82 // results and continue to the next ReactionFunc. A ReactionFunc can describe 83 // reactions on subresources by testing the result of the action's 84 // GetSubresource() method. 85 type ReactionFunc func(action Action) (handled bool, ret runtime.Object, err error) 86 87 // WatchReactionFunc is a function that returns a watch interface. If 88 // "handled" is false, then the test client will ignore the results and 89 // continue to the next ReactionFunc. 90 type WatchReactionFunc func(action Action) (handled bool, ret watch.Interface, err error) 91 92 // ProxyReactionFunc is a function that returns a ResponseWrapper interface 93 // for a given Action. If "handled" is false, then the test client will 94 // ignore the results and continue to the next ProxyReactionFunc. 95 type ProxyReactionFunc func(action Action) (handled bool, ret restclient.ResponseWrapper, err error) 96 97 // AddReactor appends a reactor to the end of the chain. 98 func (c *Fake) AddReactor(verb, resource string, reaction ReactionFunc) { 99 c.ReactionChain = append(c.ReactionChain, &SimpleReactor{verb, resource, reaction}) 100 } 101 102 // PrependReactor adds a reactor to the beginning of the chain. 103 func (c *Fake) PrependReactor(verb, resource string, reaction ReactionFunc) { 104 c.ReactionChain = append([]Reactor{&SimpleReactor{verb, resource, reaction}}, c.ReactionChain...) 105 } 106 107 // AddWatchReactor appends a reactor to the end of the chain. 108 func (c *Fake) AddWatchReactor(resource string, reaction WatchReactionFunc) { 109 c.Lock() 110 defer c.Unlock() 111 c.WatchReactionChain = append(c.WatchReactionChain, &SimpleWatchReactor{resource, reaction}) 112 } 113 114 // PrependWatchReactor adds a reactor to the beginning of the chain. 115 func (c *Fake) PrependWatchReactor(resource string, reaction WatchReactionFunc) { 116 c.Lock() 117 defer c.Unlock() 118 c.WatchReactionChain = append([]WatchReactor{&SimpleWatchReactor{resource, reaction}}, c.WatchReactionChain...) 119 } 120 121 // AddProxyReactor appends a reactor to the end of the chain. 122 func (c *Fake) AddProxyReactor(resource string, reaction ProxyReactionFunc) { 123 c.ProxyReactionChain = append(c.ProxyReactionChain, &SimpleProxyReactor{resource, reaction}) 124 } 125 126 // PrependProxyReactor adds a reactor to the beginning of the chain. 127 func (c *Fake) PrependProxyReactor(resource string, reaction ProxyReactionFunc) { 128 c.ProxyReactionChain = append([]ProxyReactor{&SimpleProxyReactor{resource, reaction}}, c.ProxyReactionChain...) 129 } 130 131 // Invokes records the provided Action and then invokes the ReactionFunc that 132 // handles the action if one exists. defaultReturnObj is expected to be of the 133 // same type a normal call would return. 134 func (c *Fake) Invokes(action Action, defaultReturnObj runtime.Object) (runtime.Object, error) { 135 c.Lock() 136 defer c.Unlock() 137 138 actionCopy := action.DeepCopy() 139 c.actions = append(c.actions, action.DeepCopy()) 140 for _, reactor := range c.ReactionChain { 141 if !reactor.Handles(actionCopy) { 142 continue 143 } 144 145 handled, ret, err := reactor.React(actionCopy) 146 if !handled { 147 continue 148 } 149 150 return ret, err 151 } 152 153 return defaultReturnObj, nil 154 } 155 156 // InvokesWatch records the provided Action and then invokes the ReactionFunc 157 // that handles the action if one exists. 158 func (c *Fake) InvokesWatch(action Action) (watch.Interface, error) { 159 c.Lock() 160 defer c.Unlock() 161 162 actionCopy := action.DeepCopy() 163 c.actions = append(c.actions, action.DeepCopy()) 164 for _, reactor := range c.WatchReactionChain { 165 if !reactor.Handles(actionCopy) { 166 continue 167 } 168 169 handled, ret, err := reactor.React(actionCopy) 170 if !handled { 171 continue 172 } 173 174 return ret, err 175 } 176 177 return nil, fmt.Errorf("unhandled watch: %#v", action) 178 } 179 180 // InvokesProxy records the provided Action and then invokes the ReactionFunc 181 // that handles the action if one exists. 182 func (c *Fake) InvokesProxy(action Action) restclient.ResponseWrapper { 183 c.Lock() 184 defer c.Unlock() 185 186 actionCopy := action.DeepCopy() 187 c.actions = append(c.actions, action.DeepCopy()) 188 for _, reactor := range c.ProxyReactionChain { 189 if !reactor.Handles(actionCopy) { 190 continue 191 } 192 193 handled, ret, err := reactor.React(actionCopy) 194 if !handled || err != nil { 195 continue 196 } 197 198 return ret 199 } 200 201 return nil 202 } 203 204 // ClearActions clears the history of actions called on the fake client. 205 func (c *Fake) ClearActions() { 206 c.Lock() 207 defer c.Unlock() 208 209 c.actions = make([]Action, 0) 210 } 211 212 // Actions returns a chronologically ordered slice fake actions called on the 213 // fake client. 214 func (c *Fake) Actions() []Action { 215 c.RLock() 216 defer c.RUnlock() 217 fa := make([]Action, len(c.actions)) 218 copy(fa, c.actions) 219 return fa 220 }