github.com/enmand/kubernetes@v1.2.0-alpha.0/pkg/credentialprovider/gcp/metadata_test.go (about) 1 /* 2 Copyright 2014 The Kubernetes Authors All rights reserved. 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 gcp_credentials 18 19 import ( 20 "encoding/base64" 21 "encoding/json" 22 "fmt" 23 "net/http" 24 "net/http/httptest" 25 "net/url" 26 "reflect" 27 "strings" 28 "testing" 29 30 "k8s.io/kubernetes/pkg/credentialprovider" 31 ) 32 33 func TestDockerKeyringFromGoogleDockerConfigMetadata(t *testing.T) { 34 registryUrl := "hello.kubernetes.io" 35 email := "foo@bar.baz" 36 username := "foo" 37 password := "bar" 38 auth := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", username, password))) 39 sampleDockerConfig := fmt.Sprintf(`{ 40 "https://%s": { 41 "email": %q, 42 "auth": %q 43 } 44 }`, registryUrl, email, auth) 45 46 const probeEndpoint = "/computeMetadata/v1/" 47 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 48 // Only serve the one metadata key. 49 if probeEndpoint == r.URL.Path { 50 w.WriteHeader(http.StatusOK) 51 } else if strings.HasSuffix(dockerConfigKey, r.URL.Path) { 52 w.WriteHeader(http.StatusOK) 53 w.Header().Set("Content-Type", "application/json") 54 fmt.Fprintln(w, sampleDockerConfig) 55 } else { 56 w.WriteHeader(http.StatusNotFound) 57 } 58 })) 59 defer server.Close() 60 61 // Make a transport that reroutes all traffic to the example server 62 transport := &http.Transport{ 63 Proxy: func(req *http.Request) (*url.URL, error) { 64 return url.Parse(server.URL + req.URL.Path) 65 }, 66 } 67 68 keyring := &credentialprovider.BasicDockerKeyring{} 69 provider := &dockerConfigKeyProvider{ 70 metadataProvider{Client: &http.Client{Transport: transport}}, 71 } 72 73 if !provider.Enabled() { 74 t.Errorf("Provider is unexpectedly disabled") 75 } 76 77 keyring.Add(provider.Provide()) 78 79 creds, ok := keyring.Lookup(registryUrl) 80 if !ok { 81 t.Errorf("Didn't find expected URL: %s", registryUrl) 82 return 83 } 84 if len(creds) > 1 { 85 t.Errorf("Got more hits than expected: %s", creds) 86 } 87 val := creds[0] 88 89 if username != val.Username { 90 t.Errorf("Unexpected username value, want: %s, got: %s", username, val.Username) 91 } 92 if password != val.Password { 93 t.Errorf("Unexpected password value, want: %s, got: %s", password, val.Password) 94 } 95 if email != val.Email { 96 t.Errorf("Unexpected email value, want: %s, got: %s", email, val.Email) 97 } 98 } 99 100 func TestDockerKeyringFromGoogleDockerConfigMetadataUrl(t *testing.T) { 101 registryUrl := "hello.kubernetes.io" 102 email := "foo@bar.baz" 103 username := "foo" 104 password := "bar" 105 auth := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", username, password))) 106 sampleDockerConfig := fmt.Sprintf(`{ 107 "https://%s": { 108 "email": %q, 109 "auth": %q 110 } 111 }`, registryUrl, email, auth) 112 113 const probeEndpoint = "/computeMetadata/v1/" 114 const valueEndpoint = "/my/value" 115 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 116 // Only serve the URL key and the value endpoint 117 if probeEndpoint == r.URL.Path { 118 w.WriteHeader(http.StatusOK) 119 } else if valueEndpoint == r.URL.Path { 120 w.WriteHeader(http.StatusOK) 121 w.Header().Set("Content-Type", "application/json") 122 fmt.Fprintln(w, sampleDockerConfig) 123 } else if strings.HasSuffix(dockerConfigUrlKey, r.URL.Path) { 124 w.WriteHeader(http.StatusOK) 125 w.Header().Set("Content-Type", "application/text") 126 fmt.Fprint(w, "http://foo.bar.com"+valueEndpoint) 127 } else { 128 w.WriteHeader(http.StatusNotFound) 129 } 130 })) 131 defer server.Close() 132 133 // Make a transport that reroutes all traffic to the example server 134 transport := &http.Transport{ 135 Proxy: func(req *http.Request) (*url.URL, error) { 136 return url.Parse(server.URL + req.URL.Path) 137 }, 138 } 139 140 keyring := &credentialprovider.BasicDockerKeyring{} 141 provider := &dockerConfigUrlKeyProvider{ 142 metadataProvider{Client: &http.Client{Transport: transport}}, 143 } 144 145 if !provider.Enabled() { 146 t.Errorf("Provider is unexpectedly disabled") 147 } 148 149 keyring.Add(provider.Provide()) 150 151 creds, ok := keyring.Lookup(registryUrl) 152 if !ok { 153 t.Errorf("Didn't find expected URL: %s", registryUrl) 154 return 155 } 156 if len(creds) > 1 { 157 t.Errorf("Got more hits than expected: %s", creds) 158 } 159 val := creds[0] 160 161 if username != val.Username { 162 t.Errorf("Unexpected username value, want: %s, got: %s", username, val.Username) 163 } 164 if password != val.Password { 165 t.Errorf("Unexpected password value, want: %s, got: %s", password, val.Password) 166 } 167 if email != val.Email { 168 t.Errorf("Unexpected email value, want: %s, got: %s", email, val.Email) 169 } 170 } 171 172 func TestContainerRegistryBasics(t *testing.T) { 173 registryUrl := "container.cloud.google.com" 174 email := "1234@project.gserviceaccount.com" 175 token := &tokenBlob{AccessToken: "ya26.lots-of-indiscernible-garbage"} 176 177 const ( 178 defaultEndpoint = "/computeMetadata/v1/instance/service-accounts/default/" 179 scopeEndpoint = defaultEndpoint + "scopes" 180 emailEndpoint = defaultEndpoint + "email" 181 tokenEndpoint = defaultEndpoint + "token" 182 ) 183 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 184 // Only serve the URL key and the value endpoint 185 if scopeEndpoint == r.URL.Path { 186 w.WriteHeader(http.StatusOK) 187 w.Header().Set("Content-Type", "application/json") 188 fmt.Fprintf(w, `["%s.read_write"]`, storageScopePrefix) 189 } else if emailEndpoint == r.URL.Path { 190 w.WriteHeader(http.StatusOK) 191 fmt.Fprint(w, email) 192 } else if tokenEndpoint == r.URL.Path { 193 w.WriteHeader(http.StatusOK) 194 w.Header().Set("Content-Type", "application/json") 195 bytes, err := json.Marshal(token) 196 if err != nil { 197 t.Fatalf("unexpected error: %v", err) 198 } 199 fmt.Fprintln(w, string(bytes)) 200 } else { 201 w.WriteHeader(http.StatusNotFound) 202 } 203 })) 204 defer server.Close() 205 206 // Make a transport that reroutes all traffic to the example server 207 transport := &http.Transport{ 208 Proxy: func(req *http.Request) (*url.URL, error) { 209 return url.Parse(server.URL + req.URL.Path) 210 }, 211 } 212 213 keyring := &credentialprovider.BasicDockerKeyring{} 214 provider := &containerRegistryProvider{ 215 metadataProvider{Client: &http.Client{Transport: transport}}, 216 } 217 218 if !provider.Enabled() { 219 t.Errorf("Provider is unexpectedly disabled") 220 } 221 222 keyring.Add(provider.Provide()) 223 224 creds, ok := keyring.Lookup(registryUrl) 225 if !ok { 226 t.Errorf("Didn't find expected URL: %s", registryUrl) 227 return 228 } 229 if len(creds) > 1 { 230 t.Errorf("Got more hits than expected: %s", creds) 231 } 232 val := creds[0] 233 234 if "_token" != val.Username { 235 t.Errorf("Unexpected username value, want: %s, got: %s", "_token", val.Username) 236 } 237 if token.AccessToken != val.Password { 238 t.Errorf("Unexpected password value, want: %s, got: %s", token.AccessToken, val.Password) 239 } 240 if email != val.Email { 241 t.Errorf("Unexpected email value, want: %s, got: %s", email, val.Email) 242 } 243 } 244 245 func TestContainerRegistryNoStorageScope(t *testing.T) { 246 const ( 247 defaultEndpoint = "/computeMetadata/v1/instance/service-accounts/default/" 248 scopeEndpoint = defaultEndpoint + "scopes" 249 ) 250 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 251 // Only serve the URL key and the value endpoint 252 if scopeEndpoint == r.URL.Path { 253 w.WriteHeader(http.StatusOK) 254 w.Header().Set("Content-Type", "application/json") 255 fmt.Fprint(w, `["https://www.googleapis.com/auth/compute.read_write"]`) 256 } else { 257 w.WriteHeader(http.StatusNotFound) 258 } 259 })) 260 defer server.Close() 261 262 // Make a transport that reroutes all traffic to the example server 263 transport := &http.Transport{ 264 Proxy: func(req *http.Request) (*url.URL, error) { 265 return url.Parse(server.URL + req.URL.Path) 266 }, 267 } 268 269 provider := &containerRegistryProvider{ 270 metadataProvider{Client: &http.Client{Transport: transport}}, 271 } 272 273 if provider.Enabled() { 274 t.Errorf("Provider is unexpectedly enabled") 275 } 276 } 277 278 func TestComputePlatformScopeSubstitutesStorageScope(t *testing.T) { 279 const ( 280 defaultEndpoint = "/computeMetadata/v1/instance/service-accounts/default/" 281 scopeEndpoint = defaultEndpoint + "scopes" 282 ) 283 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 284 // Only serve the URL key and the value endpoint 285 if scopeEndpoint == r.URL.Path { 286 w.WriteHeader(http.StatusOK) 287 w.Header().Set("Content-Type", "application/json") 288 fmt.Fprint(w, `["https://www.googleapis.com/auth/compute.read_write","https://www.googleapis.com/auth/cloud-platform.read-only"]`) 289 } else { 290 w.WriteHeader(http.StatusNotFound) 291 } 292 })) 293 defer server.Close() 294 295 // Make a transport that reroutes all traffic to the example server 296 transport := &http.Transport{ 297 Proxy: func(req *http.Request) (*url.URL, error) { 298 return url.Parse(server.URL + req.URL.Path) 299 }, 300 } 301 302 provider := &containerRegistryProvider{ 303 metadataProvider{Client: &http.Client{Transport: transport}}, 304 } 305 306 if !provider.Enabled() { 307 t.Errorf("Provider is unexpectedly disabled") 308 } 309 } 310 311 func TestAllProvidersNoMetadata(t *testing.T) { 312 server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 313 w.WriteHeader(http.StatusNotFound) 314 })) 315 defer server.Close() 316 317 // Make a transport that reroutes all traffic to the example server 318 transport := &http.Transport{ 319 Proxy: func(req *http.Request) (*url.URL, error) { 320 return url.Parse(server.URL + req.URL.Path) 321 }, 322 } 323 324 providers := []credentialprovider.DockerConfigProvider{ 325 &dockerConfigKeyProvider{ 326 metadataProvider{Client: &http.Client{Transport: transport}}, 327 }, 328 &dockerConfigUrlKeyProvider{ 329 metadataProvider{Client: &http.Client{Transport: transport}}, 330 }, 331 &containerRegistryProvider{ 332 metadataProvider{Client: &http.Client{Transport: transport}}, 333 }, 334 } 335 336 for _, provider := range providers { 337 if provider.Enabled() { 338 t.Errorf("Provider %s is unexpectedly enabled", reflect.TypeOf(provider).String()) 339 } 340 } 341 }