@@ -13,7 +13,10 @@ import (
1313 "path/filepath"
1414 "testing"
1515
16+ "cloud.google.com/go/auth/credentials"
1617 "github.com/gruntwork-io/terragrunt/internal/gcphelper"
18+ "github.com/gruntwork-io/terragrunt/internal/venv"
19+ "github.com/gruntwork-io/terragrunt/internal/vfs"
1720 "github.com/gruntwork-io/terragrunt/test/helpers/venvtest"
1821 "github.com/stretchr/testify/assert"
1922 "github.com/stretchr/testify/require"
@@ -253,42 +256,56 @@ func TestGcpConfigCredentialsPayloads(t *testing.T) {
253256 t .Parallel ()
254257
255258 testCases := []struct {
256- expected error
257- name string
258- payload string
259+ name string
260+ payload string
259261 }{
260- {name : "unsupported type" , payload : `{"type":"gce_metadata"}` , expected : gcphelper .ErrBuildingCredentials },
261- {name : "missing type" , payload : `{"client_email":"a@b.com"}` , expected : gcphelper .ErrParsingCredentials },
262- {name : "not json" , payload : `not-json` , expected : gcphelper .ErrParsingCredentials },
263- {name : "json array" , payload : `["a"]` , expected : gcphelper .ErrParsingCredentials },
262+ {name : "missing type" , payload : `{"client_email":"a@b.com"}` },
263+ {name : "not json" , payload : `not-json` },
264+ {name : "json array" , payload : `["a"]` },
264265 }
265266
266267 for _ , tc := range testCases {
267268 t .Run (tc .name , func (t * testing.T ) {
268269 t .Parallel ()
269270
270- credsFile := filepath .Join (t .TempDir (), "credentials.json" )
271- require .NoError (t , os .WriteFile (credsFile , []byte (tc .payload ), 0o600 ))
271+ v := gcpCredentialsVenv (t , []byte (tc .payload ))
272272
273273 _ , err := gcphelper .NewGCPConfigBuilder ().
274- WithSessionConfig (& gcphelper.GCPSessionConfig {Credentials : credsFile }).
275- Build (context .Background (), venvtest .NewWithOSFS ().WithEnv (map [string ]string {}))
276- require .ErrorIs (t , err , tc .expected )
274+ WithSessionConfig (& gcphelper.GCPSessionConfig {Credentials : virtualCredentialsPath }).
275+ Build (t .Context (), v )
276+
277+ var parseErr gcphelper.ParsingCredentialsError
278+ require .ErrorAs (t , err , & parseErr )
277279 })
278280 }
279281}
280282
283+ // TestGcpConfigUnsupportedCredentialsType pins that a type the SDK does not accept is
284+ // reported as a build failure naming the type, not as a parse failure.
285+ func TestGcpConfigUnsupportedCredentialsType (t * testing.T ) {
286+ t .Parallel ()
287+
288+ v := gcpCredentialsVenv (t , []byte (`{"type":"gce_metadata"}` ))
289+
290+ _ , err := gcphelper .NewGCPConfigBuilder ().
291+ WithSessionConfig (& gcphelper.GCPSessionConfig {Credentials : virtualCredentialsPath }).
292+ Build (t .Context (), v )
293+
294+ var buildErr gcphelper.BuildingCredentialsError
295+ require .ErrorAs (t , err , & buildErr )
296+ assert .Equal (t , credentials .CredType ("gce_metadata" ), buildErr .CredType )
297+ }
298+
281299// TestGcpConfigEmptyCredentialsFileFallsBackToADC pins the behaviour an unpopulated secret
282300// volume depends on: an empty file contributes no option rather than failing the run.
283301func TestGcpConfigEmptyCredentialsFileFallsBackToADC (t * testing.T ) {
284302 t .Parallel ()
285303
286- credsFile := filepath .Join (t .TempDir (), "credentials.json" )
287- require .NoError (t , os .WriteFile (credsFile , nil , 0o600 ))
304+ v := gcpCredentialsVenv (t , nil )
288305
289306 clientOpts , err := gcphelper .NewGCPConfigBuilder ().
290- WithSessionConfig (& gcphelper.GCPSessionConfig {Credentials : credsFile }).
291- Build (context . Background (), venvtest . NewWithOSFS (). WithEnv ( map [ string ] string {}) )
307+ WithSessionConfig (& gcphelper.GCPSessionConfig {Credentials : virtualCredentialsPath }).
308+ Build (t . Context (), v )
292309 require .NoError (t , err )
293310 assert .Empty (t , clientOpts )
294311}
@@ -299,16 +316,27 @@ func TestGcpConfigEmptyCredentialsFileFallsBackToADC(t *testing.T) {
299316func TestGcpConfigEmptyGACDoesNotFallBackToGoogleCredentials (t * testing.T ) {
300317 t .Parallel ()
301318
302- gacFile := filepath .Join (t .TempDir (), "gac.json" )
303- require .NoError (t , os .WriteFile (gacFile , nil , 0o600 ))
304-
305- env := map [string ]string {
306- "GOOGLE_APPLICATION_CREDENTIALS" : gacFile ,
319+ v := gcpCredentialsVenv (t , nil ).WithEnv (map [string ]string {
320+ "GOOGLE_APPLICATION_CREDENTIALS" : virtualCredentialsPath ,
307321 "GOOGLE_CREDENTIALS" : string (serviceAccountJSON (t )),
308- }
322+ })
309323
310- clientOpts , err := gcphelper .NewGCPConfigBuilder ().
311- Build (context .Background (), venvtest .NewWithOSFS ().WithEnv (env ))
324+ clientOpts , err := gcphelper .NewGCPConfigBuilder ().Build (t .Context (), v )
312325 require .NoError (t , err )
313326 assert .Empty (t , clientOpts , "leftover GOOGLE_CREDENTIALS must not win over an empty GAC file" )
314327}
328+
329+ // virtualCredentialsPath is where gcpCredentialsVenv writes the payload under test.
330+ const virtualCredentialsPath = "/virtual/gcp/credentials.json"
331+
332+ // gcpCredentialsVenv returns an in-memory venv holding payload at [virtualCredentialsPath].
333+ func gcpCredentialsVenv (t * testing.T , payload []byte ) * venv.Venv {
334+ t .Helper ()
335+
336+ v := venvtest .New ().WithEnv (map [string ]string {})
337+
338+ require .NoError (t , v .FS .MkdirAll (filepath .Dir (virtualCredentialsPath ), 0o755 ))
339+ require .NoError (t , vfs .WriteFile (v .FS , virtualCredentialsPath , payload , 0o600 ))
340+
341+ return v
342+ }
0 commit comments