|
2 | 2 | package gcphelper |
3 | 3 |
|
4 | 4 | import ( |
| 5 | + "bytes" |
5 | 6 | "context" |
6 | 7 | "encoding/json" |
7 | | - "errors" |
8 | 8 | "fmt" |
9 | 9 |
|
10 | 10 | "net/http" |
@@ -143,7 +143,9 @@ func (b *GCPConfigBuilder) Build( |
143 | 143 | return nil, err |
144 | 144 | } |
145 | 145 |
|
146 | | - clientOpts = append(clientOpts, credOpt) |
| 146 | + if credOpt != nil { |
| 147 | + clientOpts = append(clientOpts, credOpt) |
| 148 | + } |
147 | 149 | } else if gcpCfg != nil && gcpCfg.AccessToken != "" { |
148 | 150 | // Use access token from config |
149 | 151 | tokenSource := oauth2.StaticTokenSource(&oauth2.Token{ |
@@ -213,37 +215,39 @@ func credentialsFileOption(v *venv.Venv, filename string) (option.ClientOption, |
213 | 215 | return credentialsJSONOption(v, data) |
214 | 216 | } |
215 | 217 |
|
216 | | -// credentialsJSONOption authenticates with a credentials JSON payload. |
| 218 | +// credentialsJSONOption authenticates with a credentials JSON payload, or returns a nil |
| 219 | +// option when the payload is empty so the caller falls through to the ADC chain the way |
| 220 | +// the SDK's own detection does. |
217 | 221 | // |
218 | | -// The credential type is selected explicitly because the SDK's generic JSON |
219 | | -// detection is deprecated. Terragrunt retains support for every credential type |
220 | | -// the SDK supports, but rejects unknown values before constructing credentials. |
| 222 | +// The payload's type is read here rather than by the SDK's generic JSON detection, whose |
| 223 | +// CredentialsJSON option is deprecated. The credentials are still built on v.HTTP, because |
| 224 | +// the SDK would otherwise put the token exchange on a client of its own making |
| 225 | +// (google.golang.org/api/internal.creds) that the venv never sees. |
221 | 226 | func credentialsJSONOption(v *venv.Venv, data []byte) (option.ClientOption, error) { |
| 227 | + if len(bytes.TrimSpace(data)) == 0 { |
| 228 | + return nil, nil |
| 229 | + } |
| 230 | + |
222 | 231 | var metadata struct { |
223 | 232 | Type credentials.CredType `json:"type"` |
224 | 233 | } |
225 | 234 |
|
226 | 235 | if err := json.Unmarshal(data, &metadata); err != nil { |
227 | | - return nil, fmt.Errorf("error parsing GCP credentials: %w", err) |
| 236 | + return nil, fmt.Errorf("%w: %w", ErrParsingCredentials, err) |
228 | 237 | } |
229 | 238 |
|
230 | | - switch metadata.Type { |
231 | | - case credentials.ServiceAccount, |
232 | | - credentials.AuthorizedUser, |
233 | | - credentials.ExternalAccount, |
234 | | - credentials.ExternalAccountAuthorizedUser, |
235 | | - credentials.ImpersonatedServiceAccount, |
236 | | - credentials.GDCHServiceAccount: |
237 | | - default: |
238 | | - return nil, fmt.Errorf("unsupported GCP credentials type %q", metadata.Type) |
| 239 | + // The SDK reports a missing type as an unsupported filetype, which does not say what is wrong. |
| 240 | + if metadata.Type == "" { |
| 241 | + return nil, fmt.Errorf("%w: the payload has no \"type\" field", ErrParsingCredentials) |
239 | 242 | } |
240 | 243 |
|
| 244 | + // Every credential type the SDK accepts is passed through; it rejects the rest itself. |
241 | 245 | creds, err := credentials.NewCredentialsFromJSON(metadata.Type, data, &credentials.DetectOptions{ |
242 | 246 | Scopes: gcsScopes(), |
243 | 247 | Client: v.HTTP, |
244 | 248 | }) |
245 | 249 | if err != nil { |
246 | | - return nil, fmt.Errorf("error detecting GCP credentials: %w", err) |
| 250 | + return nil, fmt.Errorf("%w of type %q: %w", ErrBuildingCredentials, metadata.Type, err) |
247 | 251 | } |
248 | 252 |
|
249 | 253 | return option.WithAuthCredentials(creds), nil |
@@ -271,7 +275,7 @@ func createGCPCredentialsFromGoogleCredentialsEnv( |
271 | 275 | } |
272 | 276 |
|
273 | 277 | if err := json.Unmarshal([]byte(contents), &account); err != nil { |
274 | | - return nil, errors.New("error parsing GCP credentials") |
| 278 | + return nil, fmt.Errorf("%w from GOOGLE_CREDENTIALS: %w", ErrParsingCredentials, err) |
275 | 279 | } |
276 | 280 |
|
277 | 281 | conf := jwt.Config{ |
|
0 commit comments