acr_test.go 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220
  1. /*
  2. Copyright © 2025 ESO Maintainer Team
  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. https://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 acr
  14. import (
  15. "context"
  16. "fmt"
  17. "reflect"
  18. "testing"
  19. "github.com/Azure/azure-sdk-for-go/sdk/azcore"
  20. "github.com/Azure/azure-sdk-for-go/sdk/azcore/policy"
  21. "github.com/Azure/azure-sdk-for-go/sdk/azidentity"
  22. "github.com/stretchr/testify/assert"
  23. v1 "k8s.io/api/core/v1"
  24. apiextensions "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
  25. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  26. "k8s.io/client-go/kubernetes"
  27. "sigs.k8s.io/controller-runtime/pkg/client"
  28. clientfake "sigs.k8s.io/controller-runtime/pkg/client/fake"
  29. )
  30. func TestGenerate(t *testing.T) {
  31. const (
  32. testUsername = "11111111-2222-3333-4444-111111111111"
  33. testURL = "example.azurecr.io"
  34. )
  35. type args struct {
  36. ctx context.Context
  37. jsonSpec *apiextensions.JSON
  38. crClient client.Client
  39. kubeClient kubernetes.Interface
  40. namespace string
  41. accessTokenFetcher accessTokenFetcher
  42. refreshTokenFetcher refreshTokenFetcher
  43. clientSecretCreds clientSecretCredentialFunc
  44. }
  45. tests := []struct {
  46. name string
  47. g *Generator
  48. args args
  49. want map[string][]byte
  50. wantErr bool
  51. }{
  52. {
  53. name: "no spec",
  54. args: args{
  55. jsonSpec: nil,
  56. },
  57. wantErr: true,
  58. },
  59. {
  60. name: "empty spec",
  61. args: args{
  62. jsonSpec: &apiextensions.JSON{},
  63. },
  64. wantErr: true,
  65. },
  66. {
  67. name: "return acr access token if scope is defined",
  68. args: args{
  69. jsonSpec: &apiextensions.JSON{
  70. Raw: []byte(fmt.Sprintf(`apiVersion: generators.external-secrets.io/v1alpha1
  71. kind: ACRAccessToken
  72. spec:
  73. tenantId: %s
  74. registry: %s
  75. scope: "repository:foo:pull,push"
  76. environmentType: "PublicCloud"
  77. auth:
  78. servicePrincipal:
  79. secretRef:
  80. clientSecret:
  81. name: az-secret
  82. key: clientsecret
  83. clientId:
  84. name: az-secret
  85. key: clientid`, testUsername, testURL)),
  86. },
  87. crClient: clientfake.NewClientBuilder().WithObjects(&v1.Secret{
  88. ObjectMeta: metav1.ObjectMeta{
  89. Name: "az-secret",
  90. Namespace: "foobar",
  91. },
  92. Data: map[string][]byte{
  93. "clientsecret": []byte("foo"),
  94. "clientid": []byte("bar"),
  95. },
  96. }).Build(),
  97. namespace: "foobar",
  98. ctx: context.Background(),
  99. accessTokenFetcher: func(acrRefreshToken, tenantID, registryURL, scope string) (string, error) {
  100. assert.Equal(t, "acrrefreshtoken", acrRefreshToken)
  101. assert.Equal(t, tenantID, testUsername)
  102. assert.Equal(t, registryURL, testURL)
  103. assert.Equal(t, scope, "repository:foo:pull,push")
  104. return "acraccesstoken", nil
  105. },
  106. refreshTokenFetcher: func(aadAccessToken, tenantID, registryURL string) (string, error) {
  107. assert.Equal(t, "1234", aadAccessToken)
  108. assert.Equal(t, tenantID, testUsername)
  109. assert.Equal(t, registryURL, testURL)
  110. return "acrrefreshtoken", nil
  111. },
  112. clientSecretCreds: func(tenantID, clientID, clientSecret string, options *azidentity.ClientSecretCredentialOptions) (TokenGetter, error) {
  113. return &FakeTokenGetter{
  114. token: azcore.AccessToken{
  115. Token: "1234",
  116. },
  117. }, nil
  118. },
  119. },
  120. want: map[string][]byte{
  121. "username": []byte(defaultLoginUsername),
  122. "password": []byte("acraccesstoken"),
  123. },
  124. },
  125. {
  126. name: "return acr refresh token if scope is not defined",
  127. args: args{
  128. jsonSpec: &apiextensions.JSON{
  129. Raw: []byte(fmt.Sprintf(`apiVersion: generators.external-secrets.io/v1alpha1
  130. kind: ACRAccessToken
  131. spec:
  132. tenantId: %s
  133. registry: %s
  134. environmentType: "PublicCloud"
  135. auth:
  136. servicePrincipal:
  137. secretRef:
  138. clientSecret:
  139. name: az-secret
  140. key: clientsecret
  141. clientId:
  142. name: az-secret
  143. key: clientid`, testUsername, testURL)),
  144. },
  145. crClient: clientfake.NewClientBuilder().WithObjects(&v1.Secret{
  146. ObjectMeta: metav1.ObjectMeta{
  147. Name: "az-secret",
  148. Namespace: "foobar",
  149. },
  150. Data: map[string][]byte{
  151. "clientsecret": []byte("foo"),
  152. "clientid": []byte("bar"),
  153. },
  154. }).Build(),
  155. namespace: "foobar",
  156. ctx: context.Background(),
  157. accessTokenFetcher: func(acrRefreshToken, tenantID, registryURL, scope string) (string, error) {
  158. t.Fail()
  159. return "", nil
  160. },
  161. refreshTokenFetcher: func(aadAccessToken, tenantID, registryURL string) (string, error) {
  162. assert.Equal(t, "1234", aadAccessToken)
  163. assert.Equal(t, tenantID, testUsername)
  164. assert.Equal(t, registryURL, testURL)
  165. return "acrrefreshtoken", nil
  166. },
  167. clientSecretCreds: func(tenantID, clientID, clientSecret string, options *azidentity.ClientSecretCredentialOptions) (TokenGetter, error) {
  168. return &FakeTokenGetter{
  169. token: azcore.AccessToken{
  170. Token: "1234",
  171. },
  172. }, nil
  173. },
  174. },
  175. want: map[string][]byte{
  176. "username": []byte(defaultLoginUsername),
  177. "password": []byte("acrrefreshtoken"),
  178. },
  179. },
  180. }
  181. for _, tt := range tests {
  182. t.Run(tt.name, func(t *testing.T) {
  183. g := &Generator{
  184. clientSecretCreds: tt.args.clientSecretCreds,
  185. }
  186. got, _, err := g.generate(
  187. tt.args.ctx,
  188. tt.args.jsonSpec,
  189. tt.args.crClient,
  190. tt.args.namespace,
  191. tt.args.kubeClient,
  192. tt.args.accessTokenFetcher,
  193. tt.args.refreshTokenFetcher,
  194. )
  195. if (err != nil) != tt.wantErr {
  196. t.Errorf("Generator.Generate() error = %v, wantErr %v", err, tt.wantErr)
  197. return
  198. }
  199. if !reflect.DeepEqual(got, tt.want) {
  200. t.Errorf("Generator.Generate() = %v, want %v", got, tt.want)
  201. }
  202. })
  203. }
  204. }
  205. type FakeTokenGetter struct {
  206. token azcore.AccessToken
  207. err error
  208. }
  209. func (f *FakeTokenGetter) GetToken(_ context.Context, _ policy.TokenRequestOptions) (azcore.AccessToken, error) {
  210. return f.token, f.err
  211. }