From 6c7ad392e07d4bb7722ae6578b6534896d139fce Mon Sep 17 00:00:00 2001 From: upodroid Date: Thu, 26 Nov 2020 13:37:50 +0000 Subject: [PATCH] add service account option --- CHANGELOG.md | 2 + builtin/google/cloudrun/deployment.go | 11 +++++ builtin/google/cloudrun/platform.go | 59 +++++++++++++++++++++++++-- 3 files changed, 69 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index e6c80545b3d..0d296442be8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -20,6 +20,8 @@ IMPROVEMENTS: * core: add better error messaging when prefix is missing from the `-raw` flag in `waypoint config set` [GH-815] * install/k8s: support for OpenShift [GH-715] * server: APIs for Waypoint database snapshot/restore [GH-723] +* plugin/google-cloud-run: added service account name field [GH-850] + BUG FIXES: diff --git a/builtin/google/cloudrun/deployment.go b/builtin/google/cloudrun/deployment.go index 24e5db091a7..d87d627b47d 100644 --- a/builtin/google/cloudrun/deployment.go +++ b/builtin/google/cloudrun/deployment.go @@ -7,6 +7,7 @@ import ( "github.com/hashicorp/go-hclog" "google.golang.org/api/googleapi" + "google.golang.org/api/iam/v1" "google.golang.org/api/option" run "google.golang.org/api/run/v1" "google.golang.org/grpc/codes" @@ -53,6 +54,16 @@ func (d *Deployment) apiService(ctx context.Context) (*run.APIService, error) { return result, nil } +// iamAPIService returns the IAM API service for GCP client usage. +func (d *Deployment) iamAPIService(ctx context.Context) (*iam.Service, error) { + result, err := iam.NewService(ctx) + if err != nil { + return nil, status.Errorf(codes.Aborted, err.Error()) + } + + return result, nil +} + // getLocationsForProject returns the Cloud Run regions which are usable by this project func (d *Deployment) getLocationsForProject(ctx context.Context) ([]*run.Location, error) { apiService, err := run.NewService(ctx) diff --git a/builtin/google/cloudrun/platform.go b/builtin/google/cloudrun/platform.go index d29eac62a47..2a20529077f 100644 --- a/builtin/google/cloudrun/platform.go +++ b/builtin/google/cloudrun/platform.go @@ -10,6 +10,7 @@ import ( "time" "github.com/hashicorp/go-hclog" + "google.golang.org/api/iam/v1" run "google.golang.org/api/run/v1" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -119,10 +120,10 @@ func (p *Platform) ValidateAuth( src.App, ) - st.Update("Testing IAM permissions...") + st.Update("Testing Cloud Run IAM permissions...") result, err := client.TestIamPermissions(apiResource, &testReq).Do() if err != nil { - st.Step(terminal.StatusError, "Error testing IAM permissions: "+err.Error()) + st.Step(terminal.StatusError, "Error testing Cloud Run IAM permissions: "+err.Error()) return err } @@ -132,10 +133,48 @@ func (p *Platform) ValidateAuth( return fmt.Errorf("incorrect IAM permissions, received %s", strings.Join(result.Permissions, ", ")) } + // Validate if user has access to the service account specified + if p.config.ServiceAccountName != "" { + + iamAPIService, err := deployment.iamAPIService(ctx) + if err != nil { + ui.Output("Error constructing api client: "+err.Error(), terminal.WithErrorStyle()) + return status.Errorf(codes.Aborted, err.Error()) + } + + client := iam.NewProjectsServiceAccountsService(iamAPIService) + + expectedPermissions := []string{ + "iam.serviceAccounts.actAs", + } + + // We need to ensure that the service creator has Service Account User role. + testReq := iam.TestIamPermissionsRequest{ + Permissions: expectedPermissions, + } + + apiResource := fmt.Sprintf("projects/%s/serviceAccounts/%s", + p.config.Project, + p.config.ServiceAccountName, + ) + + st.Update("Testing IAM permissions on the supplied service account...") + result, err := client.TestIamPermissions(apiResource, &testReq).Do() + if err != nil { + st.Step(terminal.StatusError, "Error testing IAM permissions of the Service Account: "+err.Error()) + return err + } + + // If our resulting permissions do not equal our expected permissions, auth does not validate + if !reflect.DeepEqual(result.Permissions, expectedPermissions) { + st.Step(terminal.StatusError, "Incorrect IAM permissions on the Service Account, received "+strings.Join(result.Permissions, ", ")) + return fmt.Errorf("Incorrect IAM permissions on the Service Account, received %s", strings.Join(result.Permissions, ", ")) + } + } return nil } -// Deploy deploys an image to GCR. +// Deploy deploys an image to Cloud Run. func (p *Platform) Deploy( ctx context.Context, log hclog.Logger, @@ -323,6 +362,10 @@ func (p *Platform) Deploy( */ } + if p.config.ServiceAccountName != "" { + service.Spec.Template.Spec.ServiceAccountName = p.config.ServiceAccountName + } + if create { // Create the service log.Info("creating the service") @@ -433,6 +476,8 @@ app "wpmini" { request_timeout = 300 } + service_account_name = "cloudrun@waypoint-project-id.iam.gserviceaccount.com" + auto_scaling { max = 10 } @@ -504,6 +549,11 @@ app "wpmini" { "Configuration to control the auto scaling parameters for Cloud Run.", ) + doc.SetField( + "service_account_name", + "Specify a service account email that Cloud Run will use to run the service. You must have the `iam.serviceAccounts.actAs` permission on the service account.", + ) + doc.SetField( "auto_scaling.max", `Maximum number of Cloud Run instances. When the maximum requests per container is exceeded, Cloud Run will create an additional container instance to handle load. @@ -543,6 +593,9 @@ type Config struct { // AutoScaling details. AutoScaling *AutoScaling `hcl:"auto_scaling,block"` + + // Service Account details + ServiceAccountName string `hcl:"service_account_name,optional"` } // Capacity defines configuration for deployed Cloud Run resources