aws_credentials_test.go 9.6 KB

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