From 0e6e4807b19b5ec3bbe0c77dc7d9bb9b76c92156 Mon Sep 17 00:00:00 2001 From: Christopher Banck Date: Fri, 1 Feb 2019 13:14:57 +0100 Subject: [PATCH] option to specify gcloud service account json by env as string (#776) Added the option to provide the gcloud service account as a string via the environment variable `GCE_SERVICE_ACCOUNT` in addition to the already available option to specify a filepath to a keyfile `GCE_SERVICE_ACCOUNT_FILE`. --- cmd/cmd_dnshelp.go | 2 +- providers/dns/gcloud/googlecloud.go | 40 +++++++++++++++--------- providers/dns/gcloud/googlecloud_test.go | 12 +++++-- 3 files changed, 36 insertions(+), 18 deletions(-) diff --git a/cmd/cmd_dnshelp.go b/cmd/cmd_dnshelp.go index 4592607b..d62590ef 100644 --- a/cmd/cmd_dnshelp.go +++ b/cmd/cmd_dnshelp.go @@ -51,7 +51,7 @@ Here is an example bash command using the CloudFlare DNS provider: fmt.Fprintln(w, "\tfastdns:\tAKAMAI_HOST, AKAMAI_CLIENT_TOKEN, AKAMAI_CLIENT_SECRET, AKAMAI_ACCESS_TOKEN") fmt.Fprintln(w, "\tgandi:\tGANDI_API_KEY") fmt.Fprintln(w, "\tgandiv5:\tGANDIV5_API_KEY") - fmt.Fprintln(w, "\tgcloud:\tGCE_PROJECT, 'Application Default Credentials', [GCE_SERVICE_ACCOUNT_FILE]") + fmt.Fprintln(w, "\tgcloud:\tGCE_PROJECT, 'Application Default Credentials', [GCE_SERVICE_ACCOUNT_FILE], [GCE_SERVICE_ACCOUNT]") fmt.Fprintln(w, "\tglesys:\tGLESYS_API_USER, GLESYS_API_KEY") fmt.Fprintln(w, "\tgodaddy:\tGODADDY_API_KEY, GODADDY_API_SECRET") fmt.Fprintln(w, "\thostingde:\tHOSTINGDE_API_KEY, HOSTINGDE_ZONE_NAME") diff --git a/providers/dns/gcloud/googlecloud.go b/providers/dns/gcloud/googlecloud.go index d2cd04f4..877b4e97 100644 --- a/providers/dns/gcloud/googlecloud.go +++ b/providers/dns/gcloud/googlecloud.go @@ -7,7 +7,6 @@ import ( "fmt" "io/ioutil" "net/http" - "os" "strconv" "time" @@ -53,11 +52,12 @@ type DNSProvider struct { // NewDNSProvider returns a DNSProvider instance configured for Google Cloud DNS. // Project name must be passed in the environment variable: GCE_PROJECT. -// A Service Account file can be passed in the environment variable: GCE_SERVICE_ACCOUNT_FILE +// A Service Account can be passed in the environment variable: GCE_SERVICE_ACCOUNT +// or by specifying the keyfile location: GCE_SERVICE_ACCOUNT_FILE func NewDNSProvider() (*DNSProvider, error) { // Use a service account file if specified via environment variable. - if saFile, ok := os.LookupEnv("GCE_SERVICE_ACCOUNT_FILE"); ok { - return NewDNSProviderServiceAccount(saFile) + if saKey := env.GetOrFile("GCE_SERVICE_ACCOUNT"); len(saKey) > 0 { + return NewDNSProviderServiceAccountKey([]byte(saKey)) } // Use default credentials. @@ -84,16 +84,11 @@ func NewDNSProviderCredentials(project string) (*DNSProvider, error) { return NewDNSProviderConfig(config) } -// NewDNSProviderServiceAccount uses the supplied service account JSON file +// NewDNSProviderServiceAccountKey uses the supplied service account JSON // to return a DNSProvider instance configured for Google Cloud DNS. -func NewDNSProviderServiceAccount(saFile string) (*DNSProvider, error) { - if saFile == "" { - return nil, fmt.Errorf("googlecloud: Service Account file missing") - } - - dat, err := ioutil.ReadFile(saFile) - if err != nil { - return nil, fmt.Errorf("googlecloud: unable to read Service Account file: %v", err) +func NewDNSProviderServiceAccountKey(saKey []byte) (*DNSProvider, error) { + if len(saKey) == 0 { + return nil, fmt.Errorf("googlecloud: Service Account is missing") } // If GCE_PROJECT is non-empty it overrides the project in the service @@ -104,14 +99,14 @@ func NewDNSProviderServiceAccount(saFile string) (*DNSProvider, error) { var datJSON struct { ProjectID string `json:"project_id"` } - err = json.Unmarshal(dat, &datJSON) + err := json.Unmarshal(saKey, &datJSON) if err != nil || datJSON.ProjectID == "" { return nil, fmt.Errorf("googlecloud: project ID not found in Google Cloud Service Account file") } project = datJSON.ProjectID } - conf, err := google.JWTConfigFromJSON(dat, dns.NdevClouddnsReadwriteScope) + conf, err := google.JWTConfigFromJSON(saKey, dns.NdevClouddnsReadwriteScope) if err != nil { return nil, fmt.Errorf("googlecloud: unable to acquire config: %v", err) } @@ -124,6 +119,21 @@ func NewDNSProviderServiceAccount(saFile string) (*DNSProvider, error) { return NewDNSProviderConfig(config) } +// NewDNSProviderServiceAccount uses the supplied service account JSON file +// to return a DNSProvider instance configured for Google Cloud DNS. +func NewDNSProviderServiceAccount(saFile string) (*DNSProvider, error) { + if saFile == "" { + return nil, fmt.Errorf("googlecloud: Service Account file missing") + } + + saKey, err := ioutil.ReadFile(saFile) + if err != nil { + return nil, fmt.Errorf("googlecloud: unable to read Service Account file: %v", err) + } + + return NewDNSProviderServiceAccountKey(saKey) +} + // NewDNSProviderConfig return a DNSProvider instance configured for Google Cloud DNS. func NewDNSProviderConfig(config *Config) (*DNSProvider, error) { if config == nil { diff --git a/providers/dns/gcloud/googlecloud_test.go b/providers/dns/gcloud/googlecloud_test.go index 0cde500c..8455d516 100644 --- a/providers/dns/gcloud/googlecloud_test.go +++ b/providers/dns/gcloud/googlecloud_test.go @@ -19,7 +19,8 @@ import ( var envTest = tester.NewEnvTest( "GCE_PROJECT", "GCE_SERVICE_ACCOUNT_FILE", - "GOOGLE_APPLICATION_CREDENTIALS"). + "GOOGLE_APPLICATION_CREDENTIALS", + "GCE_SERVICE_ACCOUNT"). WithDomain("GCE_DOMAIN"). WithLiveTestExtra(func() bool { _, err := google.DefaultClient(context.Background(), dns.NdevClouddnsReadwriteScope) @@ -51,12 +52,19 @@ func TestNewDNSProvider(t *testing.T) { expected: "googlecloud: project name missing", }, { - desc: "success", + desc: "success key file", envVars: map[string]string{ "GCE_PROJECT": "", "GCE_SERVICE_ACCOUNT_FILE": "fixtures/gce_account_service_file.json", }, }, + { + desc: "success key", + envVars: map[string]string{ + "GCE_PROJECT": "", + "GCE_SERVICE_ACCOUNT": `{"project_id": "A","type": "service_account","client_email": "foo@bar.com","private_key_id": "pki","private_key": "pk","token_uri": "/token","client_secret": "secret","client_id": "C","refresh_token": "D"}`, + }, + }, } for _, test := range testCases {