Skip to content
This repository has been archived by the owner on Jan 8, 2024. It is now read-only.

Add Service Account Field to Cloud Run #850

Merged
merged 1 commit into from
Nov 28, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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:

Expand Down
11 changes: 11 additions & 0 deletions builtin/google/cloudrun/deployment.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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)
Expand Down
59 changes: 56 additions & 3 deletions builtin/google/cloudrun/platform.go
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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
}

Expand All @@ -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,
Expand Down Expand Up @@ -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")
Expand Down Expand Up @@ -433,6 +476,8 @@ app "wpmini" {
request_timeout = 300
}
service_account_name = "cloudrun@waypoint-project-id.iam.gserviceaccount.com"
auto_scaling {
max = 10
}
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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
Expand Down