k8s.io/client-go@v0.31.1/rest/plugin_test.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 rest 18 19 import ( 20 "fmt" 21 "net/http" 22 "reflect" 23 "strconv" 24 "testing" 25 26 clientcmdapi "k8s.io/client-go/tools/clientcmd/api" 27 ) 28 29 func TestAuthPluginWrapTransport(t *testing.T) { 30 if err := RegisterAuthProviderPlugin("pluginA", pluginAProvider); err != nil { 31 t.Errorf("Unexpected error: failed to register pluginA: %v", err) 32 } 33 if err := RegisterAuthProviderPlugin("pluginB", pluginBProvider); err != nil { 34 t.Errorf("Unexpected error: failed to register pluginB: %v", err) 35 } 36 if err := RegisterAuthProviderPlugin("pluginFail", pluginFailProvider); err != nil { 37 t.Errorf("Unexpected error: failed to register pluginFail: %v", err) 38 } 39 testCases := []struct { 40 useWrapTransport bool 41 plugin string 42 expectErr bool 43 expectPluginA bool 44 expectPluginB bool 45 }{ 46 {false, "", false, false, false}, 47 {false, "pluginA", false, true, false}, 48 {false, "pluginB", false, false, true}, 49 {false, "pluginFail", true, false, false}, 50 {false, "pluginUnknown", true, false, false}, 51 } 52 for i, tc := range testCases { 53 c := Config{} 54 if tc.useWrapTransport { 55 // Specify an existing WrapTransport in the config to make sure that 56 // plugins play nicely. 57 c.WrapTransport = func(rt http.RoundTripper) http.RoundTripper { 58 return &wrapTransport{rt} 59 } 60 } 61 if len(tc.plugin) != 0 { 62 c.AuthProvider = &clientcmdapi.AuthProviderConfig{Name: tc.plugin} 63 } 64 tConfig, err := c.TransportConfig() 65 if err != nil { 66 // Unknown/bad plugins are expected to fail here. 67 if !tc.expectErr { 68 t.Errorf("%d. Did not expect errors loading Auth Plugin: %q. Got: %v", i, tc.plugin, err) 69 } 70 continue 71 } 72 var fullyWrappedTransport http.RoundTripper 73 fullyWrappedTransport = &emptyTransport{} 74 if tConfig.WrapTransport != nil { 75 fullyWrappedTransport = tConfig.WrapTransport(&emptyTransport{}) 76 } 77 res, err := fullyWrappedTransport.RoundTrip(&http.Request{}) 78 if err != nil { 79 t.Errorf("%d. Unexpected error in RoundTrip: %v", i, err) 80 continue 81 } 82 hasWrapTransport := res.Header.Get("wrapTransport") == "Y" 83 hasPluginA := res.Header.Get("pluginA") == "Y" 84 hasPluginB := res.Header.Get("pluginB") == "Y" 85 if hasWrapTransport != tc.useWrapTransport { 86 t.Errorf("%d. Expected Existing config.WrapTransport: %t; Got: %t", i, tc.useWrapTransport, hasWrapTransport) 87 } 88 if hasPluginA != tc.expectPluginA { 89 t.Errorf("%d. Expected Plugin A: %t; Got: %t", i, tc.expectPluginA, hasPluginA) 90 } 91 if hasPluginB != tc.expectPluginB { 92 t.Errorf("%d. Expected Plugin B: %t; Got: %t", i, tc.expectPluginB, hasPluginB) 93 } 94 } 95 } 96 97 func TestAuthPluginPersist(t *testing.T) { 98 // register pluginA by a different name so we don't collide across tests. 99 if err := RegisterAuthProviderPlugin("pluginA2", pluginAProvider); err != nil { 100 t.Errorf("Unexpected error: failed to register pluginA: %v", err) 101 } 102 if err := RegisterAuthProviderPlugin("pluginPersist", pluginPersistProvider); err != nil { 103 t.Errorf("Unexpected error: failed to register pluginPersist: %v", err) 104 } 105 fooBarConfig := map[string]string{"foo": "bar"} 106 testCases := []struct { 107 plugin string 108 startingConfig map[string]string 109 expectedConfigAfterLogin map[string]string 110 expectedConfigAfterRoundTrip map[string]string 111 }{ 112 // non-persisting plugins should work fine without modifying config. 113 {"pluginA2", map[string]string{}, map[string]string{}, map[string]string{}}, 114 {"pluginA2", fooBarConfig, fooBarConfig, fooBarConfig}, 115 // plugins that persist config should be able to persist when they want. 116 { 117 "pluginPersist", 118 map[string]string{}, 119 map[string]string{ 120 "login": "Y", 121 }, 122 map[string]string{ 123 "login": "Y", 124 "roundTrips": "1", 125 }, 126 }, 127 { 128 "pluginPersist", 129 map[string]string{ 130 "login": "Y", 131 "roundTrips": "123", 132 }, 133 map[string]string{ 134 "login": "Y", 135 "roundTrips": "123", 136 }, 137 map[string]string{ 138 "login": "Y", 139 "roundTrips": "124", 140 }, 141 }, 142 } 143 for i, tc := range testCases { 144 cfg := &clientcmdapi.AuthProviderConfig{ 145 Name: tc.plugin, 146 Config: tc.startingConfig, 147 } 148 persister := &inMemoryPersister{make(map[string]string)} 149 persister.Persist(tc.startingConfig) 150 plugin, err := GetAuthProvider("127.0.0.1", cfg, persister) 151 if err != nil { 152 t.Errorf("%d. Unexpected error: failed to get plugin %q: %v", i, tc.plugin, err) 153 } 154 if err := plugin.Login(); err != nil { 155 t.Errorf("%d. Unexpected error calling Login() w/ plugin %q: %v", i, tc.plugin, err) 156 } 157 // Make sure the plugin persisted what we expect after Login(). 158 if !reflect.DeepEqual(persister.savedConfig, tc.expectedConfigAfterLogin) { 159 t.Errorf("%d. Unexpected persisted config after calling %s.Login(): \nGot:\n%v\nExpected:\n%v", 160 i, tc.plugin, persister.savedConfig, tc.expectedConfigAfterLogin) 161 } 162 if _, err := plugin.WrapTransport(&emptyTransport{}).RoundTrip(&http.Request{}); err != nil { 163 t.Errorf("%d. Unexpected error round-tripping w/ plugin %q: %v", i, tc.plugin, err) 164 } 165 // Make sure the plugin persisted what we expect after RoundTrip(). 166 if !reflect.DeepEqual(persister.savedConfig, tc.expectedConfigAfterRoundTrip) { 167 t.Errorf("%d. Unexpected persisted config after calling %s.WrapTransport.RoundTrip(): \nGot:\n%v\nExpected:\n%v", 168 i, tc.plugin, persister.savedConfig, tc.expectedConfigAfterLogin) 169 } 170 } 171 172 } 173 174 func Test_WhenNilPersister_NoOpPersisterIsAssigned(t *testing.T) { 175 176 if err := RegisterAuthProviderPlugin("anyPlugin", pluginPersistProvider); err != nil { 177 t.Errorf("unexpected error: failed to register 'anyPlugin': %v", err) 178 } 179 cfg := &clientcmdapi.AuthProviderConfig{ 180 Name: "anyPlugin", 181 Config: nil, 182 } 183 plugin, err := GetAuthProvider("127.0.0.1", cfg, nil) 184 if err != nil { 185 t.Errorf("unexpected error: failed to get 'anyPlugin': %v", err) 186 } 187 188 anyPlugin := plugin.(*pluginPersist) 189 190 if _, ok := anyPlugin.persister.(*noopPersister); !ok { 191 t.Errorf("expected to be No Operation persister") 192 } 193 194 } 195 196 // emptyTransport provides an empty http.Response with an initialized header 197 // to allow wrapping RoundTrippers to set header values. 198 type emptyTransport struct{} 199 200 func (*emptyTransport) RoundTrip(req *http.Request) (*http.Response, error) { 201 res := &http.Response{ 202 Header: make(map[string][]string), 203 } 204 return res, nil 205 } 206 207 // wrapTransport sets "wrapTransport" = "Y" on the response. 208 type wrapTransport struct { 209 rt http.RoundTripper 210 } 211 212 func (w *wrapTransport) RoundTrip(req *http.Request) (*http.Response, error) { 213 res, err := w.rt.RoundTrip(req) 214 if err != nil { 215 return nil, err 216 } 217 res.Header.Add("wrapTransport", "Y") 218 return res, nil 219 } 220 221 // wrapTransportA sets "pluginA" = "Y" on the response. 222 type wrapTransportA struct { 223 rt http.RoundTripper 224 } 225 226 func (w *wrapTransportA) RoundTrip(req *http.Request) (*http.Response, error) { 227 res, err := w.rt.RoundTrip(req) 228 if err != nil { 229 return nil, err 230 } 231 res.Header.Add("pluginA", "Y") 232 return res, nil 233 } 234 235 type pluginA struct{} 236 237 func (*pluginA) WrapTransport(rt http.RoundTripper) http.RoundTripper { 238 return &wrapTransportA{rt} 239 } 240 241 func (*pluginA) Login() error { return nil } 242 243 func pluginAProvider(string, map[string]string, AuthProviderConfigPersister) (AuthProvider, error) { 244 return &pluginA{}, nil 245 } 246 247 // wrapTransportB sets "pluginB" = "Y" on the response. 248 type wrapTransportB struct { 249 rt http.RoundTripper 250 } 251 252 func (w *wrapTransportB) RoundTrip(req *http.Request) (*http.Response, error) { 253 res, err := w.rt.RoundTrip(req) 254 if err != nil { 255 return nil, err 256 } 257 res.Header.Add("pluginB", "Y") 258 return res, nil 259 } 260 261 type pluginB struct{} 262 263 func (*pluginB) WrapTransport(rt http.RoundTripper) http.RoundTripper { 264 return &wrapTransportB{rt} 265 } 266 267 func (*pluginB) Login() error { return nil } 268 269 func pluginBProvider(string, map[string]string, AuthProviderConfigPersister) (AuthProvider, error) { 270 return &pluginB{}, nil 271 } 272 273 // pluginFailProvider simulates a registered AuthPlugin that fails to load. 274 func pluginFailProvider(string, map[string]string, AuthProviderConfigPersister) (AuthProvider, error) { 275 return nil, fmt.Errorf("Failed to load AuthProvider") 276 } 277 278 type inMemoryPersister struct { 279 savedConfig map[string]string 280 } 281 282 func (i *inMemoryPersister) Persist(config map[string]string) error { 283 i.savedConfig = make(map[string]string) 284 for k, v := range config { 285 i.savedConfig[k] = v 286 } 287 return nil 288 } 289 290 // wrapTransportPersist increments the "roundTrips" entry from the config when 291 // roundTrip is called. 292 type wrapTransportPersist struct { 293 rt http.RoundTripper 294 config map[string]string 295 persister AuthProviderConfigPersister 296 } 297 298 func (w *wrapTransportPersist) RoundTrip(req *http.Request) (*http.Response, error) { 299 roundTrips := 0 300 if rtVal, ok := w.config["roundTrips"]; ok { 301 var err error 302 roundTrips, err = strconv.Atoi(rtVal) 303 if err != nil { 304 return nil, err 305 } 306 } 307 roundTrips++ 308 w.config["roundTrips"] = fmt.Sprintf("%d", roundTrips) 309 if err := w.persister.Persist(w.config); err != nil { 310 return nil, err 311 } 312 return w.rt.RoundTrip(req) 313 } 314 315 type pluginPersist struct { 316 config map[string]string 317 persister AuthProviderConfigPersister 318 } 319 320 func (p *pluginPersist) WrapTransport(rt http.RoundTripper) http.RoundTripper { 321 return &wrapTransportPersist{rt, p.config, p.persister} 322 } 323 324 // Login sets the config entry "login" to "Y". 325 func (p *pluginPersist) Login() error { 326 p.config["login"] = "Y" 327 p.persister.Persist(p.config) 328 return nil 329 } 330 331 func pluginPersistProvider(_ string, config map[string]string, persister AuthProviderConfigPersister) (AuthProvider, error) { 332 return &pluginPersist{config, persister}, nil 333 }