Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce service account keyless authentication #199

Merged
merged 1 commit into from
Apr 12, 2024
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
185 changes: 106 additions & 79 deletions README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -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.

Expand Down Expand Up @@ -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 |
Expand All @@ -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.
Expand Down
3 changes: 2 additions & 1 deletion action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
14 changes: 7 additions & 7 deletions entrypoint.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions tests/run.bats
Original file line number Diff line number Diff line change
Expand Up @@ -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" {
Expand Down