Repository: GoogleCloudPlatform/terraform-validator
Pull request #262 (2021-07-22 18:01:45)

Added support for providing the same env var credentials supported by terraform-provider-google
Also removed the misleading 'credentials' argument to converter initialization, since that is unused.

Resolved https://github.com/GoogleCloudPlatform/terraform-validator/issues/250


Patch:
diff --git a/converters/google/convert.go b/converters/google/convert.go
index 1abe1811..18e7f575 100644
--- a/converters/google/convert.go
+++ b/converters/google/convert.go
@@ -1,4 +1,4 @@
-// Copyright 2019 Google LLC
+// Copyright 2021 Google LLC
 //
 // Licensed under the Apache License, Version 2.0 (the "License");
 // you may not use this file except in compliance with the License.
@@ -127,11 +127,20 @@ type RestoreDefault struct {
 }
 
 // NewConverter is a factory function for Converter.
-func NewConverter(ctx context.Context, ancestryManager ancestrymanager.AncestryManager, project, credentials string, offline bool) (*Converter, error) {
+func NewConverter(ctx context.Context, ancestryManager ancestrymanager.AncestryManager, project string, offline bool) (*Converter, error) {
 	cfg := &converter.Config{
 		Project:     project,
-		Credentials: credentials,
 	}
+	// Search for default credentials
+	cfg.Credentials = multiEnvSearch([]string{
+		"GOOGLE_CREDENTIALS",
+		"GOOGLE_CLOUD_KEYFILE_JSON",
+		"GCLOUD_KEYFILE_JSON",
+	})
+
+	cfg.AccessToken = multiEnvSearch([]string{
+		"GOOGLE_OAUTH_ACCESS_TOKEN",
+	})
 	if !offline {
 		converter.ConfigureBasePaths(cfg)
 		if err := cfg.LoadAndValidate(ctx); err != nil {
diff --git a/converters/google/utils.go b/converters/google/utils.go
new file mode 100644
index 00000000..26631e20
--- /dev/null
+++ b/converters/google/utils.go
@@ -0,0 +1,27 @@
+// Copyright 2021 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//      http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+package google
+
+import (
+	"os"
+)
+
+func multiEnvSearch(ks []string) string {
+	for _, k := range ks {
+		if v := os.Getenv(k); v != "" {
+			return v
+		}
+	}
+	return ""
+}
diff --git a/tfgcv/planned_assets.go b/tfgcv/planned_assets.go
index 83c16c66..a695a033 100644
--- a/tfgcv/planned_assets.go
+++ b/tfgcv/planned_assets.go
@@ -62,7 +62,7 @@ func newConverter(ctx context.Context, path, project, ancestry string, offline b
 	if err != nil {
 		return nil, errors.Wrap(err, "constructing resource manager client")
 	}
-	converter, err := google.NewConverter(ctx, ancestryManager, project, "", offline)
+	converter, err := google.NewConverter(ctx, ancestryManager, project, offline)
 	if err != nil {
 		return nil, errors.Wrap(err, "building google converter")
 	}


Test patch:
diff --git a/converters/google/convert_test.go b/converters/google/convert_test.go
index f7877483..dcb0668b 100644
--- a/converters/google/convert_test.go
+++ b/converters/google/convert_test.go
@@ -16,10 +16,12 @@ package google
 
 import (
 	"context"
+	"os"
 	"sort"
 	"testing"
 	"time"
 
+	converter "github.com/GoogleCloudPlatform/terraform-google-conversion/google"
 	"github.com/GoogleCloudPlatform/terraform-validator/ancestrymanager"
 	tfjson "github.com/hashicorp/terraform-json"
 	"github.com/pkg/errors"
@@ -37,13 +39,85 @@ func newTestConverter() (*Converter, error) {
 	if err != nil {
 		return nil, errors.Wrap(err, "constructing resource manager client")
 	}
-	c, err := NewConverter(ctx, ancestryManager, project, "", offline)
+	c, err := NewConverter(ctx, ancestryManager, project, offline)
 	if err != nil {
 		return nil, errors.Wrap(err, "building converter")
 	}
 	return c, nil
 }
 
+type configAttrGetter func(cfg *converter.Config) string
+
+func getCredentials(cfg *converter.Config) string {
+	return cfg.Credentials
+}
+func getAccessToken(cfg *converter.Config) string {
+	return cfg.AccessToken
+}
+
+func TestNewConverterCredentials(t *testing.T) {
+	cases := []struct {
+		name           string
+		envKey         string
+		envValue       string
+		getConfigValue configAttrGetter
+	}{
+		{
+			name:           "GOOGLE_CREDENTIALS",
+			envKey:         "GOOGLE_CREDENTIALS",
+			envValue:       "whatever",
+			getConfigValue: getCredentials,
+		},
+		{
+			name:           "GOOGLE_CLOUD_KEYFILE_JSON",
+			envKey:         "GOOGLE_CLOUD_KEYFILE_JSON",
+			envValue:       "whatever",
+			getConfigValue: getCredentials,
+		},
+		{
+			name:           "GCLOUD_KEYFILE_JSON",
+			envKey:         "GCLOUD_KEYFILE_JSON",
+			envValue:       "whatever",
+			getConfigValue: getCredentials,
+		},
+		{
+			name:           "GOOGLE_OAUTH_ACCESS_TOKEN",
+			envKey:         "GOOGLE_OAUTH_ACCESS_TOKEN",
+			envValue:       "whatever",
+			getConfigValue: getAccessToken,
+		},
+	}
+
+	for _, c := range cases {
+		t.Run(c.name, func(t *testing.T) {
+			originalValue, isSet := os.LookupEnv(c.envKey)
+			err := os.Setenv(c.envKey, c.envValue)
+			if err != nil {
+				t.Fatalf("error setting env var %s=%s: %s", c.envKey, c.envValue, err)
+			}
+
+			converter, err := newTestConverter()
+			if err != nil {
+				t.Fatalf("error building converter: %s", err)
+			}
+
+			assert.EqualValues(t, c.getConfigValue(converter.cfg), c.envValue)
+
+			if isSet {
+				err = os.Setenv(c.envKey, originalValue)
+				if err != nil {
+					t.Fatalf("error setting env var %s=%s: %s", c.envKey, originalValue, err)
+				}
+			} else {
+				err = os.Unsetenv(c.envKey)
+				if err != nil {
+					t.Fatalf("error unsetting env var %s: %s", c.envKey, err)
+				}
+			}
+		})
+	}
+}
+
 func TestSortByName(t *testing.T) {
 	cases := []struct {
 		name           string
@@ -408,7 +482,7 @@ func TestTimestampMarshalJSON(t *testing.T) {
 	date := time.Date(2021, time.April, 14, 15, 16, 17, 0, time.UTC)
 	ts := Timestamp{
 		Seconds: int64(date.Unix()),
-		Nanos: int64(date.UnixNano()),
+		Nanos:   int64(date.UnixNano()),
 	}
 	json, err := ts.MarshalJSON()
 	if err != nil {
@@ -421,7 +495,7 @@ func TestTimestampUnmarshalJSON(t *testing.T) {
 	expectedDate := time.Date(2021, time.April, 14, 15, 16, 17, 0, time.UTC)
 	expected := Timestamp{
 		Seconds: int64(expectedDate.Unix()),
-		Nanos: int64(expectedDate.UnixNano()),
+		Nanos:   int64(expectedDate.UnixNano()),
 	}
 	json := []byte("\"2021-04-14T15:16:17Z\"")
 	ts := Timestamp{}