6666 Namespace : "default" ,
6767 },
6868 Data : map [string ][]byte {
69- "serviceaccount" : []byte ("ewogICAgInR5cGUiOiAic2VydmljZV9hY2NvdW50IiwKICAgICJwcm9qZWN0X2lkIjogInBvZGluZm8iLAogICAgInByaXZhdGVfa2V5X2lkIjogIjI4cXdnaDNnZGY1aGozZ2I1ZmozZ3N1NXlmZ2gzNGY0NTMyNDU2OGh5MiIsCiAgICAicHJpdmF0ZV9rZXkiOiAiLS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tXG5Id2V0aGd5MTIzaHVnZ2hoaGJkY3U2MzU2ZGd5amhzdmd2R0ZESFlnY2RqYnZjZGhic3g2M2Ncbjc2dGd5Y2ZlaHVoVkdURllmdzZ0N3lkZ3lWZ3lkaGV5aHVnZ3ljdWhland5NnQzNWZ0aHl1aGVndmNldGZcblRGVUhHVHlnZ2h1Ymh4ZTY1eWd0NnRneWVkZ3kzMjZodWN5dnN1aGJoY3Zjc2poY3NqaGNzdmdkdEhGQ0dpXG5IY3llNnR5eWczZ2Z5dWhjaGNzYmh5Z2NpamRiaHl5VEY2NnR1aGNldnVoZGNiaHVoaHZmdGN1aGJoM3VoN3Q2eVxuZ2d2ZnRVSGJoNnQ1cmZ0aGh1R1ZSdGZqaGJmY3JkNXI2N3l1aHV2Z0ZUWWpndnRmeWdoYmZjZHJoeWpoYmZjdGZkZnlodmZnXG50Z3ZnZ3RmeWdodmZ0NnR1Z3ZURjVyNjZ0dWpoZ3ZmcnR5aGhnZmN0Nnk3eXRmcjVjdHZnaGJoaHZ0Z2hoanZjdHRmeWNmXG5mZnhmZ2hqYnZnY2d5dDY3dWpiZ3ZjdGZ5aFZDN3VodmdjeWp2aGhqdnl1amNcbmNnZ2hndmdjZmhnZzc2NTQ1NHRjZnRoaGdmdHloaHZ2eXZ2ZmZnZnJ5eXU3N3JlcmVkc3dmdGhoZ2ZjZnR5Y2ZkcnR0ZmhmL1xuLS0tLS1FTkQgUFJJVkFURSBLRVktLS0tLVxuIiwKICAgICJjbGllbnRfZW1haWwiOiAidGVzdEBwb2RpbmZvLmlhbS5nc2VydmljZWFjY291bnQuY29tIiwKICAgICJjbGllbnRfaWQiOiAiMzI2NTc2MzQ2Nzg3NjI1MzY3NDYiLAogICAgImF1dGhfdXJpIjogImh0dHBzOi8vYWNjb3VudHMuZ29vZ2xlLmNvbS9vL29hdXRoMi9hdXRoIiwKICAgICJ0b2tlbl91cmkiOiAiaHR0cHM6Ly9vYXV0aDIuZ29vZ2xlYXBpcy5jb20vdG9rZW4iLAogICAgImF1dGhfcHJvdmlkZXJfeDUwOV9jZXJ0X3VybCI6ICJodHRwczovL3d3dy5nb29nbGVhcGlzLmNvbS9vYXV0aDIvdjEvY2VydHMiLAogICAgImNsaWVudF94NTA5X2NlcnRfdXJsIjogImh0dHBzOi8vd3d3Lmdvb2dsZWFwaXMuY29tL3JvYm90L3YxL21ldGFkYXRhL3g1MDkvdGVzdCU0MHBvZGluZm8uaWFtLmdzZXJ2aWNlYWNjb3VudC5jb20iCn0=" ),
69+ "serviceaccount" : []byte (serviceAccountJSON ),
7070 },
7171 Type : "Opaque" ,
7272 }
8080 },
8181 Type : "Opaque" ,
8282 }
83+ malformedSecret = corev1.Secret {
84+ ObjectMeta : v1.ObjectMeta {
85+ Name : "gcp-secret" ,
86+ Namespace : "default" ,
87+ },
88+ Data : map [string ][]byte {
89+ "serviceaccount" : []byte ("not-json" ),
90+ },
91+ Type : "Opaque" ,
92+ }
93+ externalAccountSecret = corev1.Secret {
94+ ObjectMeta : v1.ObjectMeta {
95+ Name : "gcp-secret" ,
96+ Namespace : "default" ,
97+ },
98+ Data : map [string ][]byte {
99+ "serviceaccount" : []byte (externalAccountJSON ),
100+ },
101+ Type : "Opaque" ,
102+ }
83103)
84104
105+ // serviceAccountJSON is a dummy GCP service account key. The private key is a
106+ // throwaway RSA key generated for this test only; it does not belong to any
107+ // real service account and cannot be used to retrieve tokens, only to
108+ // exercise credential construction.
109+ const serviceAccountJSON = `{
110+ "type": "service_account",
111+ "project_id": "podinfo",
112+ "private_key_id": "28qwgh3gdf5hj3gb5fj3gsu5yfgh34f45324568hy2",
113+ "private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQC02OoAWFKPVfE2\nTVYmdQKb8WRZo8ciQomAYb7WluGqa6avPu/9z7+q4K7hZtN/9Q2t0EvvBuKXTL8+\nV+AUV2N+oi4nY7T2yq3Ebc66R/c21o9sNvYJog6S8HWtOOd0tvotrvrjFQnfcCX5\nzC48HC9PgUjHAeR8kacYlpKbaEobRI1i9AfN5deuS5tsI0Di8Ag6lGf7G+0gHUpM\nLmUttJCleOltOl1Xwb57/zZCgGwB5VdkaBtkq6KW0xxwhOqF1RiFuGOUf+vRNjfj\nLPx58XSTL0TNtYa3M8koBLMaVznY5du6Pqpq1VBSUo3Yw0Z8DwxErZBICkvwbEsQ\njmQNpK+5AgMBAAECgf8l/OqQ5/yi3z3fI9LU55jrHBzx0QiJycnYWq/ocG+dIyYz\ngz6MsiowwSf7CpgJNaojhX2hCz4A474uxyBRJYotlfB1lbXA1KvSEL7Vom64T8zd\njmEtGApRRosLHmKAKDw+tfxwqhqvNLLFcuTDYg6nqyCYE22x1pnWMGR1AJMqGgFr\nSjfTl2wtSQzT705Cd3oNoItqPdYh4Ky6dxImfiHcj237mFXWy9e5x6l0N7ShJzqS\nH3t8s5wnxjt9jAK7NBqCFSxvYyKSO7MTLBlOLUdM8KuzjsYgKw0e2j+D4LhXKc/N\n1nXY2hUgq4NAOnYoK4HUAkOcN34wLjxTvp6wlwECgYEA7pkh0/QPrGVZ9bjAExq6\nCY9Z+gejUDOHWl4aWQeZA36g3FvlPgHTFTB7hMbmABbVEjCbLrnSFe9aMQsYm0O2\n/4MAUAqXX9bV134YK3kbtYH2qXX/60oGjACcbx0CIzOAO8prF7h0MKol065POaK8\nLGupztnn1fP/cH3GXF/cVLkCgYEAwgmD874uAYrXGPZ25PjH+J+L2IGj7WIa173O\ni+WJe/5Lp/A/fp9zr/ln5x2t5Pg7btysayGz1e8TGfrEBJ8ADzIm9z9ale9XHRz5\nVIBqO+bh3PW+iBs2ocZkfMtXigDCkIBP/lutvzvcFN/fsvGw1DF8PCmN6no6/gC5\nwzNjswECgYEAmbzp4xx7jOWxVXc5rBWokchgfY62WFMbf8rqxzryCSJqnBJKX+3l\nCN44eJGAWcZcfF/9Xdo12BRl1PwFWuYC4BiU9v4cE5DmMPf6suhSRl37ha2WvRDx\nrvwl0CKs4empUt1Wq+4aT9ESlpbWTZjiDu1AeRxHGcEicmVYjuTln2ECgYEAkciC\naiQN/ryoxSmPxJKh88szT7R/TD/0OPlzcKpBdHZns0KPAfyc967kAMHMwAY86RtF\nM6x7qBVafZ9pnKs1aTVeD097KMFM6yO0tGdS6bSbJ98+ipYfosYjA5vnJllR1S2C\nbHHHBbHctZZKRPDP0W1okO8LoAq7vdEfwGgg1QECgYBBuHECqDcz+buKSZRAQqRm\ngqt4hdcu+qRMqleZY4WPNHAZoPna9hU+7EFM+D1qsB6iZxzsUXzKF9gmJfB+Zvcn\nmLQq0YXDeXBt2KWeQSyQLM2bBW0J3aFIpilaP3VRcXUPEF7FstT0DC+SkydbANIf\n5IiZW7E1qU/WBIWMkD4WuA==\n-----END PRIVATE KEY-----\n",
114+ "client_email": "test@podinfo.iam.gserviceaccount.com",
115+ "client_id": "32657634678762536746",
116+ "auth_uri": "https://accounts.google.com/o/oauth2/auth",
117+ "token_uri": "https://oauth2.googleapis.com/token",
118+ "auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
119+ "client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/test%40podinfo.iam.gserviceaccount.com"
120+ }`
121+
122+ // externalAccountJSON is a workload identity federation configuration, which
123+ // is not a supported credential type for the 'serviceaccount' secret field.
124+ const externalAccountJSON = `{
125+ "type": "external_account",
126+ "audience": "//iam.googleapis.com/projects/1/locations/global/workloadIdentityPools/p/providers/v",
127+ "subject_token_type": "urn:ietf:params:oauth:token-type:jwt",
128+ "token_url": "https://sts.googleapis.com/v1/token",
129+ "credential_source": {
130+ "file": "/var/run/service-account/token"
131+ }
132+ }`
133+
85134// createTestBucket creates a test bucket for testing purposes
86135func createTestBucket () * sourcev1.Bucket {
87136 return & sourcev1.Bucket {
@@ -162,14 +211,48 @@ func TestMain(m *testing.M) {
162211 os .Exit (run )
163212}
164213
165- func TestNewClientWithSecretErr (t * testing.T ) {
166- bucket := createTestBucket ()
167- gcpClient , err := NewClient (context .Background (), bucket , WithSecret (secret .DeepCopy ()))
168- t .Log (err )
169- g := NewWithT (t )
170- g .Expect (err ).To (HaveOccurred ())
171- g .Expect (err .Error ()).To (Equal ("dialing: invalid character 'e' looking for beginning of value" ))
172- g .Expect (gcpClient ).To (BeNil ())
214+ func TestNewClientWithSecret (t * testing.T ) {
215+ tests := []struct {
216+ name string
217+ secret * corev1.Secret
218+ wantErr string
219+ }{
220+ {
221+ name : "service account key" ,
222+ secret : secret .DeepCopy (),
223+ },
224+ {
225+ name : "missing serviceaccount field" ,
226+ secret : badSecret .DeepCopy (),
227+ wantErr : "invalid 'gcp-secret' secret data: required fields 'serviceaccount'" ,
228+ },
229+ {
230+ name : "serviceaccount is not JSON" ,
231+ secret : malformedSecret .DeepCopy (),
232+ wantErr : "invalid 'gcp-secret' secret data: failed to parse 'serviceaccount' as JSON" ,
233+ },
234+ {
235+ name : "unsupported credential type" ,
236+ secret : externalAccountSecret .DeepCopy (),
237+ wantErr : "invalid 'gcp-secret' secret data: 'serviceaccount' must contain a service account key with 'type' set to 'service_account'" ,
238+ },
239+ }
240+
241+ for _ , tt := range tests {
242+ t .Run (tt .name , func (t * testing.T ) {
243+ g := NewWithT (t )
244+ gcpClient , err := NewClient (context .Background (), createTestBucket (), WithSecret (tt .secret ))
245+ if tt .wantErr != "" {
246+ g .Expect (err ).To (HaveOccurred ())
247+ g .Expect (err .Error ()).To (Equal (tt .wantErr ))
248+ g .Expect (gcpClient ).To (BeNil ())
249+ return
250+ }
251+ g .Expect (err ).NotTo (HaveOccurred ())
252+ g .Expect (gcpClient ).NotTo (BeNil ())
253+ gcpClient .Close (context .Background ())
254+ })
255+ }
173256}
174257
175258func TestNewClientWithProxyErr (t * testing.T ) {
@@ -184,9 +267,20 @@ func TestNewClientWithProxyErr(t *testing.T) {
184267 gcpClient , err := NewClient (context .Background (), bucket ,
185268 WithProxyURL (& url.URL {}),
186269 WithSecret (secret .DeepCopy ()))
270+ g .Expect (err ).NotTo (HaveOccurred ())
271+ g .Expect (gcpClient ).NotTo (BeNil ())
272+ gcpClient .Close (context .Background ())
273+ })
274+
275+ t .Run ("with unsupported credential type" , func (t * testing.T ) {
276+ g := NewWithT (t )
277+ bucket := createTestBucket ()
278+ gcpClient , err := NewClient (context .Background (), bucket ,
279+ WithProxyURL (& url.URL {}),
280+ WithSecret (externalAccountSecret .DeepCopy ()))
187281 g .Expect (err ).To (HaveOccurred ())
188282 g .Expect (gcpClient ).To (BeNil ())
189- g .Expect (err .Error ()).To (Equal ("failed to create Google credentials from secret: invalid character 'e' looking for beginning of value " ))
283+ g .Expect (err .Error ()).To (Equal ("invalid 'gcp-secret' secret data: 'serviceaccount' must contain a service account key with 'type' set to 'service_account' " ))
190284 })
191285
192286 t .Run ("without secret" , func (t * testing.T ) {
@@ -369,18 +463,32 @@ func TestFGetObjectDirectoryIsFileName(t *testing.T) {
369463func TestValidateSecret (t * testing.T ) {
370464 t .Parallel ()
371465 testCases := []struct {
372- name string
373- secret * corev1.Secret
374- error bool
466+ name string
467+ secret * corev1.Secret
468+ wantErr string
375469 }{
376470 {
377- name : "valid secret" ,
471+ name : "nil secret" ,
472+ secret : nil ,
473+ },
474+ {
475+ name : "service account key" ,
378476 secret : secret .DeepCopy (),
379477 },
380478 {
381- name : "invalid secret" ,
382- secret : badSecret .DeepCopy (),
383- error : true ,
479+ name : "missing serviceaccount field" ,
480+ secret : badSecret .DeepCopy (),
481+ wantErr : "invalid 'gcp-secret' secret data: required fields 'serviceaccount'" ,
482+ },
483+ {
484+ name : "serviceaccount is not JSON" ,
485+ secret : malformedSecret .DeepCopy (),
486+ wantErr : "invalid 'gcp-secret' secret data: failed to parse 'serviceaccount' as JSON" ,
487+ },
488+ {
489+ name : "unsupported credential type" ,
490+ secret : externalAccountSecret .DeepCopy (),
491+ wantErr : "invalid 'gcp-secret' secret data: 'serviceaccount' must contain a service account key with 'type' set to 'service_account'" ,
384492 },
385493 }
386494 for _ , testCase := range testCases {
@@ -389,9 +497,9 @@ func TestValidateSecret(t *testing.T) {
389497 t .Parallel ()
390498 err := ValidateSecret (tt .secret )
391499 g := NewWithT (t )
392- if tt .error {
500+ if tt .wantErr != "" {
393501 g .Expect (err ).To (HaveOccurred ())
394- g .Expect (err .Error ()).To (Equal (fmt . Sprintf ( "invalid '%v' secret data: required fields 'serviceaccount'" , tt .secret . Name ) ))
502+ g .Expect (err .Error ()).To (Equal (tt .wantErr ))
395503 } else {
396504 g .Expect (err ).NotTo (HaveOccurred ())
397505 }
0 commit comments