github.com/vdemeester/k8s-pkg-credentialprovider@v1.22.4/aws/aws_credentials_test.go (about) 1 /* 2 Copyright 2014 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 credentials 18 19 import ( 20 "encoding/base64" 21 "fmt" 22 "math/rand" 23 "path" 24 "strconv" 25 "testing" 26 "time" 27 28 "github.com/aws/aws-sdk-go/aws" 29 "github.com/aws/aws-sdk-go/service/ecr" 30 31 "github.com/vdemeester/k8s-pkg-credentialprovider" 32 ) 33 34 const user = "foo" 35 const password = "1234567890abcdef" // Fake value for testing. 36 const email = "not@val.id" 37 38 // Mock implementation 39 // randomizePassword is used to check for a cache hit to verify the password 40 // has not changed 41 type testTokenGetter struct { 42 user string 43 password string 44 endpoint string 45 randomizePassword bool 46 } 47 48 type testTokenGetterFactory struct { 49 getter tokenGetter 50 } 51 52 func (f *testTokenGetterFactory) GetTokenGetterForRegion(region string) (tokenGetter, error) { 53 return f.getter, nil 54 } 55 56 func (p *testTokenGetter) GetAuthorizationToken(input *ecr.GetAuthorizationTokenInput) (*ecr.GetAuthorizationTokenOutput, error) { 57 if p.randomizePassword { 58 rand.Seed(int64(time.Now().Nanosecond())) 59 p.password = strconv.Itoa(rand.Int()) 60 } 61 expiration := time.Now().Add(1 * time.Hour) 62 // expiration := time.Now().Add(5 * time.Second) //for testing with the cache expiring 63 creds := []byte(fmt.Sprintf("%s:%s", p.user, p.password)) 64 data := &ecr.AuthorizationData{ 65 AuthorizationToken: aws.String(base64.StdEncoding.EncodeToString(creds)), 66 ExpiresAt: &expiration, 67 ProxyEndpoint: aws.String(p.endpoint), 68 } 69 output := &ecr.GetAuthorizationTokenOutput{ 70 AuthorizationData: []*ecr.AuthorizationData{data}, 71 } 72 return output, nil //p.svc.GetAuthorizationToken(input) 73 } 74 75 func TestRegistryPatternMatch(t *testing.T) { 76 grid := []struct { 77 Registry string 78 Expected bool 79 }{ 80 {"123456789012.dkr.ecr.lala-land-1.amazonaws.com", true}, 81 // fips 82 {"123456789012.dkr.ecr-fips.lala-land-1.amazonaws.com", true}, 83 // .cn 84 {"123456789012.dkr.ecr.lala-land-1.amazonaws.com.cn", true}, 85 // registry ID too long 86 {"1234567890123.dkr.ecr.lala-land-1.amazonaws.com", false}, 87 // registry ID too short 88 {"12345678901.dkr.ecr.lala-land-1.amazonaws.com", false}, 89 // registry ID has invalid chars 90 {"12345678901A.dkr.ecr.lala-land-1.amazonaws.com", false}, 91 // region has invalid chars 92 {"123456789012.dkr.ecr.lala-land-1!.amazonaws.com", false}, 93 // region starts with invalid char 94 {"123456789012.dkr.ecr.#lala-land-1.amazonaws.com", false}, 95 // invalid host suffix 96 {"123456789012.dkr.ecr.lala-land-1.amazonaws.hacker.com", false}, 97 // invalid host suffix 98 {"123456789012.dkr.ecr.lala-land-1.hacker.com", false}, 99 // invalid host suffix 100 {"123456789012.dkr.ecr.lala-land-1.amazonaws.lol", false}, 101 // without dkr 102 {"123456789012.dog.ecr.lala-land-1.amazonaws.com", false}, 103 // without ecr 104 {"123456789012.dkr.cat.lala-land-1.amazonaws.com", false}, 105 // without amazonaws 106 {"123456789012.dkr.cat.lala-land-1.awsamazon.com", false}, 107 // too short 108 {"123456789012.lala-land-1.amazonaws.com", false}, 109 } 110 for _, g := range grid { 111 actual := ecrPattern.MatchString(g.Registry) 112 if actual != g.Expected { 113 t.Errorf("unexpected pattern match value, want %v for %s", g.Expected, g.Registry) 114 } 115 } 116 } 117 118 func TestParseRepoURLPass(t *testing.T) { 119 registryID := "123456789012" 120 region := "lala-land-1" 121 port := "9001" 122 registry := "123456789012.dkr.ecr.lala-land-1.amazonaws.com" 123 image := path.Join(registry, port, "foo/bar") 124 parsedURL, err := parseRepoURL(image) 125 126 if err != nil { 127 t.Errorf("Could not parse URL: %s, err: %v", image, err) 128 } 129 if registryID != parsedURL.registryID { 130 t.Errorf("Unexpected registryID value, want: %s, got: %s", registryID, parsedURL.registryID) 131 } 132 if region != parsedURL.region { 133 t.Errorf("Unexpected region value, want: %s, got: %s", region, parsedURL.region) 134 } 135 if registry != parsedURL.registry { 136 t.Errorf("Unexpected registry value, want: %s, got: %s", registry, parsedURL.registry) 137 } 138 } 139 140 func TestParseRepoURLFail(t *testing.T) { 141 registry := "123456789012.foo.bar.baz" 142 image := path.Join(registry, "foo/bar") 143 parsedURL, err := parseRepoURL(image) 144 expectedErr := "123456789012.foo.bar.baz is not a valid ECR repository URL" 145 146 if err == nil { 147 t.Errorf("Should fail to parse URL %s", image) 148 } 149 if err.Error() != expectedErr { 150 t.Errorf("Unexpected error, want: %s, got: %v", expectedErr, err) 151 } 152 if parsedURL != nil { 153 t.Errorf("Expected parsedURL to be nil") 154 } 155 } 156 157 func isAlwaysEC2() bool { 158 return true 159 } 160 161 func TestECRProvide(t *testing.T) { 162 registry := "123456789012.dkr.ecr.lala-land-1.amazonaws.com" 163 otherRegistries := []string{ 164 "123456789012.dkr.ecr.cn-foo-1.amazonaws.com.cn", 165 "private.registry.com", 166 "gcr.io", 167 } 168 image := path.Join(registry, "foo/bar") 169 p := newECRProvider(&testTokenGetterFactory{ 170 getter: &testTokenGetter{ 171 user: user, 172 password: password, 173 endpoint: registry, 174 }, 175 }, isAlwaysEC2) 176 keyring := &credentialprovider.BasicDockerKeyring{} 177 keyring.Add(p.Provide(image)) 178 179 // Verify that we get the expected username/password combo for 180 // an ECR image name. 181 creds, ok := keyring.Lookup(image) 182 if !ok { 183 t.Errorf("Didn't find expected URL: %s", image) 184 return 185 } 186 if len(creds) > 1 { 187 t.Errorf("Got more hits than expected: %s", creds) 188 } 189 cred := creds[0] 190 if user != cred.Username { 191 t.Errorf("Unexpected username value, want: %s, got: %s", user, cred.Username) 192 } 193 if password != creds[0].Password { 194 t.Errorf("Unexpected password value, want: %s, got: %s", password, cred.Password) 195 } 196 if email != creds[0].Email { 197 t.Errorf("Unexpected email value, want: %s, got: %s", email, cred.Email) 198 } 199 200 // Verify that we get an error for other images. 201 for _, otherRegistry := range otherRegistries { 202 image = path.Join(otherRegistry, "foo/bar") 203 _, ok = keyring.Lookup(image) 204 if ok { 205 t.Errorf("Unexpectedly found image: %s", image) 206 return 207 } 208 } 209 } 210 211 func TestECRProvideCached(t *testing.T) { 212 registry := "123456789012.dkr.ecr.lala-land-1.amazonaws.com" 213 p := newECRProvider(&testTokenGetterFactory{ 214 getter: &testTokenGetter{ 215 user: user, 216 password: password, 217 endpoint: registry, 218 randomizePassword: true, 219 }, 220 }, isAlwaysEC2) 221 image1 := path.Join(registry, "foo/bar") 222 image2 := path.Join(registry, "bar/baz") 223 keyring := &credentialprovider.BasicDockerKeyring{} 224 keyring.Add(p.Provide(image1)) 225 // time.Sleep(6 * time.Second) //for testing with the cache expiring 226 keyring.Add(p.Provide(image2)) 227 // Verify that we get the credentials from the 228 // cache the second time 229 creds1, ok := keyring.Lookup(image1) 230 if !ok { 231 t.Errorf("Didn't find expected URL: %s", image1) 232 return 233 } 234 if len(creds1) != 2 { 235 t.Errorf("Got more hits than expected: %s", creds1) 236 } 237 238 if creds1[0].Password != creds1[1].Password { 239 t.Errorf("cached credentials do not match") 240 } 241 242 creds2, ok := keyring.Lookup(image2) 243 if !ok { 244 t.Errorf("Didn't find expected URL: %s", image1) 245 return 246 } 247 if len(creds2) != 2 { 248 t.Errorf("Got more hits than expected: %s", creds2) 249 } 250 251 if creds2[0].Password != creds2[1].Password { 252 t.Errorf("cached credentials do not match") 253 } 254 if creds1[0].Password != creds2[0].Password { 255 t.Errorf("cached credentials do not match") 256 } 257 } 258 259 func TestChinaECRProvide(t *testing.T) { 260 registry := "123456789012.dkr.ecr.cn-foo-1.amazonaws.com.cn" 261 otherRegistries := []string{ 262 "123456789012.dkr.ecr.lala-land-1.amazonaws.com", 263 "private.registry.com", 264 "gcr.io", 265 } 266 image := path.Join(registry, "foo/bar") 267 p := newECRProvider(&testTokenGetterFactory{ 268 getter: &testTokenGetter{ 269 user: user, 270 password: password, 271 endpoint: registry, 272 }, 273 }, isAlwaysEC2) 274 keyring := &credentialprovider.BasicDockerKeyring{} 275 keyring.Add(p.Provide(image)) 276 // Verify that we get the expected username/password combo for 277 // an ECR image name. 278 creds, ok := keyring.Lookup(image) 279 if !ok { 280 t.Errorf("Didn't find expected URL: %s", image) 281 return 282 } 283 if len(creds) > 1 { 284 t.Errorf("Got more hits than expected: %s", creds) 285 } 286 cred := creds[0] 287 if user != cred.Username { 288 t.Errorf("Unexpected username value, want: %s, got: %s", user, cred.Username) 289 } 290 if password != cred.Password { 291 t.Errorf("Unexpected password value, want: %s, got: %s", password, cred.Password) 292 } 293 if email != cred.Email { 294 t.Errorf("Unexpected email value, want: %s, got: %s", email, cred.Email) 295 } 296 297 // Verify that we get an error for other images. 298 for _, otherRegistry := range otherRegistries { 299 image = path.Join(otherRegistry, image) 300 _, ok = keyring.Lookup(image) 301 if ok { 302 t.Errorf("Unexpectedly found image: %s", image) 303 return 304 } 305 } 306 } 307 308 func TestChinaECRProvideCached(t *testing.T) { 309 registry := "123456789012.dkr.ecr.cn-foo-1.amazonaws.com.cn" 310 p := newECRProvider(&testTokenGetterFactory{ 311 getter: &testTokenGetter{ 312 user: user, 313 password: password, 314 endpoint: registry, 315 randomizePassword: true, 316 }, 317 }, isAlwaysEC2) 318 image := path.Join(registry, "foo/bar") 319 keyring := &credentialprovider.BasicDockerKeyring{} 320 keyring.Add(p.Provide(image)) 321 // time.Sleep(6 * time.Second) //for testing with the cache expiring 322 keyring.Add(p.Provide(image)) 323 // Verify that we get the credentials from the 324 // cache the second time 325 creds, ok := keyring.Lookup(image) 326 if !ok { 327 t.Errorf("Didn't find expected URL: %s", image) 328 return 329 } 330 if len(creds) != 2 { 331 t.Errorf("Got more hits than expected: %s", creds) 332 } 333 334 if creds[0].Password != creds[1].Password { 335 t.Errorf("cached credentials do not match") 336 } 337 } 338 339 func BenchmarkSetupLatency(b *testing.B) { 340 p := newECRProvider(&ecrTokenGetterFactory{cache: make(map[string]tokenGetter)}, ec2ValidationImpl) 341 _ = p.Enabled() 342 }