From 561053d4c4936318565ec9a86c2e11fd3ba838ae Mon Sep 17 00:00:00 2001 From: kharf Date: Thu, 11 Apr 2024 08:05:32 +0200 Subject: [PATCH] feat: introduce service account keyless authentication --- README.MD | 185 ++++++++++++++++++++++++++++--------------------- action.yml | 3 +- entrypoint.sh | 14 ++-- tests/run.bats | 4 +- 4 files changed, 117 insertions(+), 89 deletions(-) diff --git a/README.MD b/README.MD index c11d6c9..65b9c31 100644 --- a/README.MD +++ b/README.MD @@ -5,55 +5,79 @@ A Github Action that deploys a service to Google Cloud Run (GCP managed Knative-Serving). -## Input +## Usage -### Parameter +### Authentication -| Parameter | Description | Default | Required | Reference | -|---|---|---|---|---| -| `project_id` | GCP project ID | | true | [gcloud](https://cloud.google.com/sdk/gcloud/reference#--project) | -| `service_account_key` | Base64 encoded JSON key for GCP service account | | true | [gcloud auth](https://cloud.google.com/sdk/gcloud/reference/auth/activate-service-account#--key-file) | -| `image_name` | Name of container image to be deployed | | true | [gcloud run deploy](https://cloud.google.com/sdk/gcloud/reference/run/deploy#--image) | -| `service_name` | Name of the service to be deployed | | true | [gcloud run deploy](https://cloud.google.com/sdk/gcloud/reference/run/deploy#SERVICE) | -| `gcp_region` | GCP region to deploy the service in | | true | [gcloud run deploy](https://cloud.google.com/sdk/gcloud/reference/run/deploy#--region) | -| `image_tag` | Tag of container image to be deployed | `latest` | false | [gcloud run deploy](https://cloud.google.com/sdk/gcloud/reference/run/deploy#--image) | -| `image_tag_pattern` | Regex pattern to identify the image_tag automatically | `` | false | [see below](https://github.com/p1nkun1c0rns/deploy-google-cloud-run-action#image_tag_pattern) | -| `concurrency_per_instance` | Max number of concurrent requests per instance, max: 250 | `80` | false | [gcloud run deploy](https://cloud.google.com/sdk/gcloud/reference/run/deploy#--concurrency) | -| `cpu` | VCPU limit per instance, max: `4` | `1` | false | [gcloud run deploy](https://cloud.google.com/sdk/gcloud/reference/run/deploy#--cpu) | -| `memory` | Memory limit per instance, with `4` CPU, min is `2Gi` | `256Mi` | false | [gcloud run deploy](https://cloud.google.com/sdk/gcloud/reference/run/deploy#--memory) | -| `max_instances` | Max nummber of instances to be scaled | `10` | false | [gcloud run deploy](https://cloud.google.com/sdk/gcloud/reference/run/deploy#--max-instances) | -| `min_instances` | Min nummber of instances to be available in idle | `0` | false | [gcloud run deploy](https://cloud.google.com/sdk/gcloud/reference/run/deploy#--min-instances) | -| `request_timeout` | Timeout for a single request to be processed | `10s` | false | [gcloud run deploy](https://cloud.google.com/sdk/gcloud/reference/run/deploy#--timeout) | -| `allow_unauthenticated` | Whether the service should not be protected by GCP authorization | `true` | false | [gcloud run deploy](https://cloud.google.com/sdk/gcloud/reference/run/deploy#--[no-]allow-unauthenticated) | -| `cpu_throttling` | Set to false so that CPU is always allocated and available even when there are no incoming requests | `true` | false | [gcloud run deploy](https://cloud.google.com/sdk/gcloud/reference/run/deploy#--[no-]cpu-throttling) | -| `startup_boost` | Set to true to have additional CPU available at startup time | `false` | false | [cloud run docs](https://cloud.google.com/run/docs/configuring/cpu#startup-boost) | -| `service_account` | Service Account to be used by the revision to be deployed | GCP docs: "If not provided, the revision will use the project's default service account." | false | [gcloud_run_deploy](https://cloud.google.com/sdk/gcloud/reference/run/deploy#--service-account) | -| `no_traffic` | Set to true to just deploy a new revision without shifting traffic | `false` | false | [gcloud run deploy](https://cloud.google.com/sdk/gcloud/reference/run/deploy#--no-traffic) | -| `cloudsql_instances` | Comma separated list of CloudSQL instances to connect to | | false | [gcloud run deploy](https://cloud.google.com/sdk/gcloud/reference/run/deploy#--set-cloudsql-instances) | -| `vpc_connector` | Name of the Serverless VPC Access connector to use with this service | | false | [gcloud run deploy](https://cloud.google.com/sdk/gcloud/reference/run/deploy#--vpc-connector) | -| `vpc_egress` | Outbound traffic configuration, if a vpc_connector is configured; options are: `private-ranges-only`, `all-traffic` | `private-ranges-only` | false | [gcloud run deploy](https://cloud.google.com/sdk/gcloud/reference/run/deploy#--vpc-egress) | -| `vpc_network` | Name of VPC network when using direct VPC egress w/o vpc connector | | false | [gcloud run deploy](https://cloud.google.com/run/docs/configuring/vpc-direct-vpc#direct-vpc-service) | -| `vpc_subnet` | Name of VPC network's subnet when using direct VPC egress w/o vpc connector | | false | [gcloud run deploy](https://cloud.google.com/run/docs/configuring/vpc-direct-vpc#direct-vpc-service) | -| `vpc_network_tags` | Comma-separated list of network tags for the VPC network to be used | | false | [gcloud run deploy](https://cloud.google.com/run/docs/configuring/vpc-direct-vpc#direct-vpc-service)| -| `ingress` | Allowed ingress traffic sources; options are: `all`, `internal`, `internal-and-cloud-load-balancing` | `all` | false | [gcloud run deploy](https://cloud.google.com/sdk/gcloud/reference/run/deploy#--ingress) | -| `execution_environment` | Selects the execution environment where the application will run; options are: `gen1`, `gen2` | | false | [gcloud run deploy](https://cloud.google.com/sdk/gcloud/reference/run/deploy#--execution-environment), [cloud run docs](https://cloud.google.com/run/docs/about-execution-environments) | -| `debug` | Whether the gcloud commands should be printed to output | `false` | false | | +There are multiple ways to authenticate this action to Google Cloud. +The following roles are required: +* `roles/run.admin` +* `roles/iam.serviceAccountUser` +* (optional) `roles/storage.objectViewer` - see the **`image_tag_pattern`** parameter -#### `image_tag_pattern` +#### Recommended: [google-github-actions/auth](https://github.com/google-github-actions/auth) +```yaml +jobs: + job_id: + # Add "id-token" with the intended permissions. + permissions: + contents: 'read' + id-token: 'write' + + steps: + - uses: 'actions/checkout@v4' + + - uses: 'google-github-actions/auth@v2' + with: + service_account: my-service-account + workload_identity_provider: 'projects/123456789/locations/global/workloadIdentityPools/my-pool/providers/my-provider' + + - name: Deploy Cloud Run + id: deploy + uses: p1nkun1c0rns/deploy-google-cloud-run-action@master + with: + project_id: your-gcp-project-id + gcp_region: europe-west4 + service_name: yourservice + image_name: eu.gcr.io/your-gcp-project-id/yourservice + image_tag: '1.5.1' + env: + SET_ENV_DISABLE_SIGNAL_HANDLERS: yeah + SET_ENV_APPLICATION_SECRET: ${{ secrets.APPLICATION_SECRET }} +``` -If parameter `image_tag_pattern` is provided and no explicit `image_tag` is given, the highest image tag (bash `sort`) matching the pattern (`grep -E "^${image_tag_pattern}"`) will be deployed. -For doing this, the used GCP service account has to have the role _`roles/storage.objectViewer`_. +#### Service Account Key JSON +```yaml +jobs: + job_id: + + steps: + - name: Deploy Cloud Run + id: deploy + uses: p1nkun1c0rns/deploy-google-cloud-run-action@master + with: + service_account_key: ${{ secrets.GOOGLE_SERVICEACCOUNT_KEY }} + project_id: your-gcp-project-id + gcp_region: europe-west4 + service_name: yourservice + image_name: eu.gcr.io/your-gcp-project-id/yourservice + image_tag: '1.5.1' + env: + SET_ENV_DISABLE_SIGNAL_HANDLERS: yeah + SET_ENV_APPLICATION_SECRET: ${{ secrets.APPLICATION_SECRET }} +``` -Example: `image_tag_pattern: 1\.3\.\d+` +### Prebuilt latest image -#### `service_account_key` +Instead of using the latest (`@master`) or a tagged version, a prebuilt image of the latest release in the `action` branch is available: -The service account needs the following roles for the deployment to work properly: -* `roles/run.admin` -* `roles/iam.serviceAccountUser` -* (optional) `roles/storage.objectViewer` - see **`image_tag_pattern`** +```yaml + - name: Deploy Cloud Run + id: deploy + uses: p1nkun1c0rns/deploy-google-cloud-run-action@action +``` -### Environment +### Environment Variables For passing configuration and secrets to the Cloud Run service [`--set-env-vars`](https://cloud.google.com/sdk/gcloud/reference/run/deploy#--set-env-vars) is used. @@ -94,6 +118,47 @@ becomes: Remark: The service account used by the deployed Cloud Run service needs IAM rights to access all configured secrets, and the Cloud Run service account needs to be "Secret Manager Secret Accessor". +## Input + +### Parameter + +| Parameter | Description | Default | Required | Reference | +|---|---|---|---|---| +| `project_id` | GCP project ID | | true | [gcloud](https://cloud.google.com/sdk/gcloud/reference#--project) | +| `service_account_key` | Base64 encoded JSON key for GCP service account | | false | [gcloud auth](https://cloud.google.com/sdk/gcloud/reference/auth/activate-service-account#--key-file) | +| `image_name` | Name of container image to be deployed | | true | [gcloud run deploy](https://cloud.google.com/sdk/gcloud/reference/run/deploy#--image) | +| `service_name` | Name of the service to be deployed | | true | [gcloud run deploy](https://cloud.google.com/sdk/gcloud/reference/run/deploy#SERVICE) | +| `gcp_region` | GCP region to deploy the service in | | true | [gcloud run deploy](https://cloud.google.com/sdk/gcloud/reference/run/deploy#--region) | +| `image_tag` | Tag of container image to be deployed | `latest` | false | [gcloud run deploy](https://cloud.google.com/sdk/gcloud/reference/run/deploy#--image) | +| `image_tag_pattern` | Regex pattern to identify the image_tag automatically | `` | false | [see below](https://github.com/p1nkun1c0rns/deploy-google-cloud-run-action#image_tag_pattern) | +| `concurrency_per_instance` | Max number of concurrent requests per instance, max: 250 | `80` | false | [gcloud run deploy](https://cloud.google.com/sdk/gcloud/reference/run/deploy#--concurrency) | +| `cpu` | VCPU limit per instance, max: `4` | `1` | false | [gcloud run deploy](https://cloud.google.com/sdk/gcloud/reference/run/deploy#--cpu) | +| `memory` | Memory limit per instance, with `4` CPU, min is `2Gi` | `256Mi` | false | [gcloud run deploy](https://cloud.google.com/sdk/gcloud/reference/run/deploy#--memory) | +| `max_instances` | Max nummber of instances to be scaled | `10` | false | [gcloud run deploy](https://cloud.google.com/sdk/gcloud/reference/run/deploy#--max-instances) | +| `min_instances` | Min nummber of instances to be available in idle | `0` | false | [gcloud run deploy](https://cloud.google.com/sdk/gcloud/reference/run/deploy#--min-instances) | +| `request_timeout` | Timeout for a single request to be processed | `10s` | false | [gcloud run deploy](https://cloud.google.com/sdk/gcloud/reference/run/deploy#--timeout) | +| `allow_unauthenticated` | Whether the service should not be protected by GCP authorization | `true` | false | [gcloud run deploy](https://cloud.google.com/sdk/gcloud/reference/run/deploy#--[no-]allow-unauthenticated) | +| `cpu_throttling` | Set to false so that CPU is always allocated and available even when there are no incoming requests | `true` | false | [gcloud run deploy](https://cloud.google.com/sdk/gcloud/reference/run/deploy#--[no-]cpu-throttling) | +| `startup_boost` | Set to true to have additional CPU available at startup time | `false` | false | [cloud run docs](https://cloud.google.com/run/docs/configuring/cpu#startup-boost) | +| `service_account` | Service Account to be used by the revision to be deployed | GCP docs: "If not provided, the revision will use the project's default service account." | false | [gcloud_run_deploy](https://cloud.google.com/sdk/gcloud/reference/run/deploy#--service-account) | +| `no_traffic` | Set to true to just deploy a new revision without shifting traffic | `false` | false | [gcloud run deploy](https://cloud.google.com/sdk/gcloud/reference/run/deploy#--no-traffic) | +| `cloudsql_instances` | Comma separated list of CloudSQL instances to connect to | | false | [gcloud run deploy](https://cloud.google.com/sdk/gcloud/reference/run/deploy#--set-cloudsql-instances) | +| `vpc_connector` | Name of the Serverless VPC Access connector to use with this service | | false | [gcloud run deploy](https://cloud.google.com/sdk/gcloud/reference/run/deploy#--vpc-connector) | +| `vpc_egress` | Outbound traffic configuration, if a vpc_connector is configured; options are: `private-ranges-only`, `all-traffic` | `private-ranges-only` | false | [gcloud run deploy](https://cloud.google.com/sdk/gcloud/reference/run/deploy#--vpc-egress) | +| `vpc_network` | Name of VPC network when using direct VPC egress w/o vpc connector | | false | [gcloud run deploy](https://cloud.google.com/run/docs/configuring/vpc-direct-vpc#direct-vpc-service) | +| `vpc_subnet` | Name of VPC network's subnet when using direct VPC egress w/o vpc connector | | false | [gcloud run deploy](https://cloud.google.com/run/docs/configuring/vpc-direct-vpc#direct-vpc-service) | +| `vpc_network_tags` | Comma-separated list of network tags for the VPC network to be used | | false | [gcloud run deploy](https://cloud.google.com/run/docs/configuring/vpc-direct-vpc#direct-vpc-service)| +| `ingress` | Allowed ingress traffic sources; options are: `all`, `internal`, `internal-and-cloud-load-balancing` | `all` | false | [gcloud run deploy](https://cloud.google.com/sdk/gcloud/reference/run/deploy#--ingress) | +| `execution_environment` | Selects the execution environment where the application will run; options are: `gen1`, `gen2` | | false | [gcloud run deploy](https://cloud.google.com/sdk/gcloud/reference/run/deploy#--execution-environment), [cloud run docs](https://cloud.google.com/run/docs/about-execution-environments) | +| `debug` | Whether the gcloud commands should be printed to output | `false` | false | | + +#### `image_tag_pattern` + +If parameter `image_tag_pattern` is provided and no explicit `image_tag` is given, the highest image tag (bash `sort`) matching the pattern (`grep -E "^${image_tag_pattern}"`) will be deployed. +For doing this, the used GCP service account has to have the role _`roles/storage.objectViewer`_. + +Example: `image_tag_pattern: 1\.3\.\d+` + ## Output | Parameter | Description | Example | @@ -103,44 +168,6 @@ Remark: The service account used by the deployed Cloud Run service needs IAM rig | gcloud_log | Log output of the gcloud run deploy command | | | deployed_image_tag | Tag of the image that was deployed | 1.3.23 | -## Usage - -```yaml - - name: Deploy Cloud Run - id: deploy - uses: p1nkun1c0rns/deploy-google-cloud-run-action@master - with: - service_account_key: ${{ secrets.GOOGLE_SERVICEACCOUNT_KEY }} - project_id: your-gcp-project-id - gcp_region: europe-west4 - service_name: yourservice - image_name: eu.gcr.io/your-gcp-project-id/yourservice - image_tag: '1.5.1' - env: - SET_ENV_DISABLE_SIGNAL_HANDLERS: yeah - SET_ENV_APPLICATION_SECRET: ${{ secrets.APPLICATION_SECRET }} - - - name: Create Release - uses: actions/create-release@latest - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - with: - tag_name: ${{ steps.deploy.outputs.cloud_run_revision }} - release_name: ${{ steps.deploy.outputs.cloud_run_revision }} serving at ${{ steps.deploy.outputs.cloud_run_endpoint }} - body: | - ${{ steps.deploy.outputs.gcloud_log }} -``` - -### Prebuild latest image - -Instead of using the latest (`@master`) or a tagged version there's a prebuild image of the latest release in the `action` branch available: - -```yaml - - name: Deploy Cloud Run - id: deploy - uses: p1nkun1c0rns/deploy-google-cloud-run-action@action -``` - ## Remarks * The service revision suffix is built from the `image_tag` replacing the dots with dashes concatinating the current epoch seconds for beeing able to redeploy the same version with different configuration. diff --git a/action.yml b/action.yml index 8a44005..f8a6f15 100644 --- a/action.yml +++ b/action.yml @@ -10,7 +10,8 @@ inputs: required: true service_account_key: description: 'Base64 encoded JSON key for GCP service account' - required: true + required: false + default: '' image_name: description: 'Name of container image to be deployed' required: true diff --git a/entrypoint.sh b/entrypoint.sh index 5d9aea5..70ccb77 100644 --- a/entrypoint.sh +++ b/entrypoint.sh @@ -21,13 +21,13 @@ SERVICE_NAME_LENGTH_LIMIT=62 set -e set -o pipefail - -echo "$INPUT_SERVICE_ACCOUNT_KEY" | base64 -d >key.json -trap "{ rm -f key.json; }" EXIT - -enableDebug -gcloud auth activate-service-account --key-file=key.json --project="$INPUT_PROJECT_ID" -disableDebug +if [ -n "$INPUT_SERVICE_ACCOUNT_KEY" ]; then + echo "$INPUT_SERVICE_ACCOUNT_KEY" | base64 -d >key.json + trap "{ rm -f key.json; }" EXIT + enableDebug + gcloud auth activate-service-account --key-file=key.json --project="$INPUT_PROJECT_ID" + disableDebug +fi IMAGE_TAG="latest" if [ -n "$INPUT_IMAGE_TAG" ]; then diff --git a/tests/run.bats b/tests/run.bats index 9d3a534..acb5ddf 100755 --- a/tests/run.bats +++ b/tests/run.bats @@ -51,8 +51,8 @@ function debug() { debug "${status}" "${output}" "${lines}" - echo $output | grep -q "Could not read json file key.json" - [[ "${status}" -eq 1 ]] + echo $output | grep -q "Deploying :latest as service" + [[ "${status}" -eq 2 ]] } @test "start hadolint" {