plugin_test.go 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311
  1. /*
  2. Copyright 2016 The Kubernetes Authors.
  3. Licensed under the Apache License, Version 2.0 (the "License");
  4. you may not use this file except in compliance with the License.
  5. You may obtain a copy of the License at
  6. http://www.apache.org/licenses/LICENSE-2.0
  7. Unless required by applicable law or agreed to in writing, software
  8. distributed under the License is distributed on an "AS IS" BASIS,
  9. WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  10. See the License for the specific language governing permissions and
  11. limitations under the License.
  12. */
  13. package restclient
  14. import (
  15. "fmt"
  16. "net/http"
  17. "reflect"
  18. "strconv"
  19. "testing"
  20. clientcmdapi "k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api"
  21. )
  22. func TestAuthPluginWrapTransport(t *testing.T) {
  23. if err := RegisterAuthProviderPlugin("pluginA", pluginAProvider); err != nil {
  24. t.Errorf("Unexpected error: failed to register pluginA: %v", err)
  25. }
  26. if err := RegisterAuthProviderPlugin("pluginB", pluginBProvider); err != nil {
  27. t.Errorf("Unexpected error: failed to register pluginB: %v", err)
  28. }
  29. if err := RegisterAuthProviderPlugin("pluginFail", pluginFailProvider); err != nil {
  30. t.Errorf("Unexpected error: failed to register pluginFail: %v", err)
  31. }
  32. testCases := []struct {
  33. useWrapTransport bool
  34. plugin string
  35. expectErr bool
  36. expectPluginA bool
  37. expectPluginB bool
  38. }{
  39. {false, "", false, false, false},
  40. {false, "pluginA", false, true, false},
  41. {false, "pluginB", false, false, true},
  42. {false, "pluginFail", true, false, false},
  43. {false, "pluginUnknown", true, false, false},
  44. }
  45. for i, tc := range testCases {
  46. c := Config{}
  47. if tc.useWrapTransport {
  48. // Specify an existing WrapTransport in the config to make sure that
  49. // plugins play nicely.
  50. c.WrapTransport = func(rt http.RoundTripper) http.RoundTripper {
  51. return &wrapTransport{rt}
  52. }
  53. }
  54. if len(tc.plugin) != 0 {
  55. c.AuthProvider = &clientcmdapi.AuthProviderConfig{Name: tc.plugin}
  56. }
  57. tConfig, err := c.transportConfig()
  58. if err != nil {
  59. // Unknown/bad plugins are expected to fail here.
  60. if !tc.expectErr {
  61. t.Errorf("%d. Did not expect errors loading Auth Plugin: %q. Got: %v", i, tc.plugin, err)
  62. }
  63. continue
  64. }
  65. var fullyWrappedTransport http.RoundTripper
  66. fullyWrappedTransport = &emptyTransport{}
  67. if tConfig.WrapTransport != nil {
  68. fullyWrappedTransport = tConfig.WrapTransport(&emptyTransport{})
  69. }
  70. res, err := fullyWrappedTransport.RoundTrip(&http.Request{})
  71. if err != nil {
  72. t.Errorf("%d. Unexpected error in RoundTrip: %v", i, err)
  73. continue
  74. }
  75. hasWrapTransport := res.Header.Get("wrapTransport") == "Y"
  76. hasPluginA := res.Header.Get("pluginA") == "Y"
  77. hasPluginB := res.Header.Get("pluginB") == "Y"
  78. if hasWrapTransport != tc.useWrapTransport {
  79. t.Errorf("%d. Expected Existing config.WrapTransport: %t; Got: %t", i, tc.useWrapTransport, hasWrapTransport)
  80. }
  81. if hasPluginA != tc.expectPluginA {
  82. t.Errorf("%d. Expected Plugin A: %t; Got: %t", i, tc.expectPluginA, hasPluginA)
  83. }
  84. if hasPluginB != tc.expectPluginB {
  85. t.Errorf("%d. Expected Plugin B: %t; Got: %t", i, tc.expectPluginB, hasPluginB)
  86. }
  87. }
  88. }
  89. func TestAuthPluginPersist(t *testing.T) {
  90. // register pluginA by a different name so we don't collide across tests.
  91. if err := RegisterAuthProviderPlugin("pluginA2", pluginAProvider); err != nil {
  92. t.Errorf("Unexpected error: failed to register pluginA: %v", err)
  93. }
  94. if err := RegisterAuthProviderPlugin("pluginPersist", pluginPersistProvider); err != nil {
  95. t.Errorf("Unexpected error: failed to register pluginPersist: %v", err)
  96. }
  97. fooBarConfig := map[string]string{"foo": "bar"}
  98. testCases := []struct {
  99. plugin string
  100. startingConfig map[string]string
  101. expectedConfigAfterLogin map[string]string
  102. expectedConfigAfterRoundTrip map[string]string
  103. }{
  104. // non-persisting plugins should work fine without modifying config.
  105. {"pluginA2", map[string]string{}, map[string]string{}, map[string]string{}},
  106. {"pluginA2", fooBarConfig, fooBarConfig, fooBarConfig},
  107. // plugins that persist config should be able to persist when they want.
  108. {
  109. "pluginPersist",
  110. map[string]string{},
  111. map[string]string{
  112. "login": "Y",
  113. },
  114. map[string]string{
  115. "login": "Y",
  116. "roundTrips": "1",
  117. },
  118. },
  119. {
  120. "pluginPersist",
  121. map[string]string{
  122. "login": "Y",
  123. "roundTrips": "123",
  124. },
  125. map[string]string{
  126. "login": "Y",
  127. "roundTrips": "123",
  128. },
  129. map[string]string{
  130. "login": "Y",
  131. "roundTrips": "124",
  132. },
  133. },
  134. }
  135. for i, tc := range testCases {
  136. cfg := &clientcmdapi.AuthProviderConfig{
  137. Name: tc.plugin,
  138. Config: tc.startingConfig,
  139. }
  140. persister := &inMemoryPersister{make(map[string]string)}
  141. persister.Persist(tc.startingConfig)
  142. plugin, err := GetAuthProvider("127.0.0.1", cfg, persister)
  143. if err != nil {
  144. t.Errorf("%d. Unexpected error: failed to get plugin %q: %v", i, tc.plugin, err)
  145. }
  146. if err := plugin.Login(); err != nil {
  147. t.Errorf("%d. Unexpected error calling Login() w/ plugin %q: %v", i, tc.plugin, err)
  148. }
  149. // Make sure the plugin persisted what we expect after Login().
  150. if !reflect.DeepEqual(persister.savedConfig, tc.expectedConfigAfterLogin) {
  151. t.Errorf("%d. Unexpected persisted config after calling %s.Login(): \nGot:\n%v\nExpected:\n%v",
  152. i, tc.plugin, persister.savedConfig, tc.expectedConfigAfterLogin)
  153. }
  154. if _, err := plugin.WrapTransport(&emptyTransport{}).RoundTrip(&http.Request{}); err != nil {
  155. t.Errorf("%d. Unexpected error round-tripping w/ plugin %q: %v", i, tc.plugin, err)
  156. }
  157. // Make sure the plugin persisted what we expect after RoundTrip().
  158. if !reflect.DeepEqual(persister.savedConfig, tc.expectedConfigAfterRoundTrip) {
  159. t.Errorf("%d. Unexpected persisted config after calling %s.WrapTransport.RoundTrip(): \nGot:\n%v\nExpected:\n%v",
  160. i, tc.plugin, persister.savedConfig, tc.expectedConfigAfterLogin)
  161. }
  162. }
  163. }
  164. // emptyTransport provides an empty http.Response with an initialized header
  165. // to allow wrapping RoundTrippers to set header values.
  166. type emptyTransport struct{}
  167. func (*emptyTransport) RoundTrip(req *http.Request) (*http.Response, error) {
  168. res := &http.Response{
  169. Header: make(map[string][]string),
  170. }
  171. return res, nil
  172. }
  173. // wrapTransport sets "wrapTransport" = "Y" on the response.
  174. type wrapTransport struct {
  175. rt http.RoundTripper
  176. }
  177. func (w *wrapTransport) RoundTrip(req *http.Request) (*http.Response, error) {
  178. res, err := w.rt.RoundTrip(req)
  179. if err != nil {
  180. return nil, err
  181. }
  182. res.Header.Add("wrapTransport", "Y")
  183. return res, nil
  184. }
  185. // wrapTransportA sets "pluginA" = "Y" on the response.
  186. type wrapTransportA struct {
  187. rt http.RoundTripper
  188. }
  189. func (w *wrapTransportA) RoundTrip(req *http.Request) (*http.Response, error) {
  190. res, err := w.rt.RoundTrip(req)
  191. if err != nil {
  192. return nil, err
  193. }
  194. res.Header.Add("pluginA", "Y")
  195. return res, nil
  196. }
  197. type pluginA struct{}
  198. func (*pluginA) WrapTransport(rt http.RoundTripper) http.RoundTripper {
  199. return &wrapTransportA{rt}
  200. }
  201. func (*pluginA) Login() error { return nil }
  202. func pluginAProvider(string, map[string]string, AuthProviderConfigPersister) (AuthProvider, error) {
  203. return &pluginA{}, nil
  204. }
  205. // wrapTransportB sets "pluginB" = "Y" on the response.
  206. type wrapTransportB struct {
  207. rt http.RoundTripper
  208. }
  209. func (w *wrapTransportB) RoundTrip(req *http.Request) (*http.Response, error) {
  210. res, err := w.rt.RoundTrip(req)
  211. if err != nil {
  212. return nil, err
  213. }
  214. res.Header.Add("pluginB", "Y")
  215. return res, nil
  216. }
  217. type pluginB struct{}
  218. func (*pluginB) WrapTransport(rt http.RoundTripper) http.RoundTripper {
  219. return &wrapTransportB{rt}
  220. }
  221. func (*pluginB) Login() error { return nil }
  222. func pluginBProvider(string, map[string]string, AuthProviderConfigPersister) (AuthProvider, error) {
  223. return &pluginB{}, nil
  224. }
  225. // pluginFailProvider simulates a registered AuthPlugin that fails to load.
  226. func pluginFailProvider(string, map[string]string, AuthProviderConfigPersister) (AuthProvider, error) {
  227. return nil, fmt.Errorf("Failed to load AuthProvider")
  228. }
  229. type inMemoryPersister struct {
  230. savedConfig map[string]string
  231. }
  232. func (i *inMemoryPersister) Persist(config map[string]string) error {
  233. i.savedConfig = make(map[string]string)
  234. for k, v := range config {
  235. i.savedConfig[k] = v
  236. }
  237. return nil
  238. }
  239. // wrapTransportPersist increments the "roundTrips" entry from the config when
  240. // roundTrip is called.
  241. type wrapTransportPersist struct {
  242. rt http.RoundTripper
  243. config map[string]string
  244. persister AuthProviderConfigPersister
  245. }
  246. func (w *wrapTransportPersist) RoundTrip(req *http.Request) (*http.Response, error) {
  247. roundTrips := 0
  248. if rtVal, ok := w.config["roundTrips"]; ok {
  249. var err error
  250. roundTrips, err = strconv.Atoi(rtVal)
  251. if err != nil {
  252. return nil, err
  253. }
  254. }
  255. roundTrips++
  256. w.config["roundTrips"] = fmt.Sprintf("%d", roundTrips)
  257. if err := w.persister.Persist(w.config); err != nil {
  258. return nil, err
  259. }
  260. return w.rt.RoundTrip(req)
  261. }
  262. type pluginPersist struct {
  263. config map[string]string
  264. persister AuthProviderConfigPersister
  265. }
  266. func (p *pluginPersist) WrapTransport(rt http.RoundTripper) http.RoundTripper {
  267. return &wrapTransportPersist{rt, p.config, p.persister}
  268. }
  269. // Login sets the config entry "login" to "Y".
  270. func (p *pluginPersist) Login() error {
  271. p.config["login"] = "Y"
  272. p.persister.Persist(p.config)
  273. return nil
  274. }
  275. func pluginPersistProvider(_ string, config map[string]string, persister AuthProviderConfigPersister) (AuthProvider, error) {
  276. return &pluginPersist{config, persister}, nil
  277. }