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  }