mirror of
https://github.com/google-github-actions/setup-gcloud.git
synced 2026-08-30 01:29:21 +00:00
Cloud Run Action (#117)
* Cloud Run Action Drafted * Cloud Run action * refactor * rename * Add metadata and tests * Update Readme and required fields * Update e2e tests * Update tests * Add log messages * fix typo in obj field * Update YAML * Update Env Var names * Update workflow tests * Update tests * Update testing * Update tests * Update workflow * Update workflow dir * fix typo * test output * Update integration tests * add workdir * set up auth for service request * Add check for empty array * Wait for URL * fix test * Update for PR comments
This commit is contained in:
parent
79f381996b
commit
a244a889d6
19 changed files with 329019 additions and 0 deletions
177
.github/workflows/deploy-cloudrun-it.yml
vendored
Normal file
177
.github/workflows/deploy-cloudrun-it.yml
vendored
Normal file
|
|
@ -0,0 +1,177 @@
|
|||
name: deploy-cloudrun Integration
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'deploy-cloudrun/**'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'deploy-cloudrun/**'
|
||||
|
||||
jobs:
|
||||
gcloud:
|
||||
name: with setup-gcloud
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- uses: ./setup-gcloud # Set up ADC to make authenticated request to service
|
||||
with:
|
||||
service_account_email: ${{ secrets.DEPLOY_CLOUDRUN_SA_EMAIL }}
|
||||
service_account_key: ${{ secrets.DEPLOY_CLOUDRUN_SA_KEY_B64 }}
|
||||
export_default_credentials: true
|
||||
- id: deploy
|
||||
uses: ./deploy-cloudrun
|
||||
env:
|
||||
GCLOUD_PROJECT: ${{ secrets.DEPLOY_CLOUDRUN_PROJECT_ID }}
|
||||
with:
|
||||
image: gcr.io/cloudrun/hello
|
||||
service: test-gcloud
|
||||
- uses: actions/setup-node@master
|
||||
with:
|
||||
node-version: 12.x
|
||||
- run: npm install
|
||||
working-directory: ./deploy-cloudrun
|
||||
- name: integration tests
|
||||
run: npm run e2e-tests
|
||||
working-directory: ./deploy-cloudrun
|
||||
env:
|
||||
URL: ${{ steps.deploy.outputs.url }}
|
||||
|
||||
b64_json:
|
||||
name: with base64 json creds
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- id: deploy
|
||||
uses: ./deploy-cloudrun
|
||||
with:
|
||||
credentials: ${{ secrets.DEPLOY_CLOUDRUN_SA_KEY_B64 }}
|
||||
image: gcr.io/cloudrun/hello
|
||||
service: test-b64
|
||||
- uses: actions/setup-node@master
|
||||
with:
|
||||
node-version: 12.x
|
||||
- run: npm install
|
||||
working-directory: ./deploy-cloudrun
|
||||
- uses: ./setup-gcloud # Set up ADC to make authenticated request to service
|
||||
with:
|
||||
service_account_email: ${{ secrets.DEPLOY_CLOUDRUN_SA_EMAIL }}
|
||||
service_account_key: ${{ secrets.DEPLOY_CLOUDRUN_SA_KEY_B64 }}
|
||||
export_default_credentials: true
|
||||
- name: integration tests
|
||||
run: npm run e2e-tests
|
||||
working-directory: ./deploy-cloudrun
|
||||
env:
|
||||
URL: ${{ steps.deploy.outputs.url }}
|
||||
GCLOUD_PROJECT: ${{ secrets.DEPLOY_CLOUDRUN_PROJECT_ID }}
|
||||
|
||||
json:
|
||||
name: with json creds
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- id: deploy
|
||||
uses: ./deploy-cloudrun
|
||||
with:
|
||||
credentials: ${{ secrets.DEPLOY_CLOUDRUN_SA_KEY_JSON }}
|
||||
image: gcr.io/cloudrun/hello
|
||||
service: test-json
|
||||
- uses: actions/setup-node@master
|
||||
with:
|
||||
node-version: 12.x
|
||||
- run: npm install
|
||||
working-directory: ./deploy-cloudrun
|
||||
- uses: ./setup-gcloud # Set up ADC to make authenticated request to service
|
||||
with:
|
||||
service_account_email: ${{ secrets.DEPLOY_CLOUDRUN_SA_EMAIL }}
|
||||
service_account_key: ${{ secrets.DEPLOY_CLOUDRUN_SA_KEY_B64 }}
|
||||
export_default_credentials: true
|
||||
- name: integration tests
|
||||
run: npm run e2e-tests
|
||||
working-directory: ./deploy-cloudrun
|
||||
env:
|
||||
URL: ${{ steps.deploy.outputs.url }}
|
||||
GCLOUD_PROJECT: ${{ secrets.DEPLOY_CLOUDRUN_PROJECT_ID }}
|
||||
|
||||
envVars:
|
||||
name: with Env Vars
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- id: deploy
|
||||
uses: ./deploy-cloudrun
|
||||
with:
|
||||
credentials: ${{ secrets.DEPLOY_CLOUDRUN_SA_KEY_JSON }}
|
||||
image: gcr.io/cloudrun/hello
|
||||
service: test-envvars
|
||||
env_vars:
|
||||
- uses: actions/setup-node@master
|
||||
with:
|
||||
node-version: 12.x
|
||||
- run: npm install
|
||||
working-directory: ./deploy-cloudrun
|
||||
- uses: ./setup-gcloud # Set up ADC to make authenticated request to service
|
||||
with:
|
||||
service_account_email: ${{ secrets.DEPLOY_CLOUDRUN_SA_EMAIL }}
|
||||
service_account_key: ${{ secrets.DEPLOY_CLOUDRUN_SA_KEY_B64 }}
|
||||
export_default_credentials: true
|
||||
- name: integration tests
|
||||
run: npm run e2e-tests
|
||||
working-directory: ./deploy-cloudrun
|
||||
env:
|
||||
URL: ${{ steps.deploy.outputs.url }}
|
||||
GCLOUD_PROJECT: ${{ secrets.DEPLOY_CLOUDRUN_PROJECT_ID }}
|
||||
|
||||
yaml:
|
||||
name: with YAML metadata
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- id: deploy
|
||||
uses: ./deploy-cloudrun
|
||||
with:
|
||||
credentials: ${{ secrets.DEPLOY_CLOUDRUN_SA_KEY_JSON }}
|
||||
metadata: ./deploy-cloudrun/tests/unit/service.basic.yaml
|
||||
- uses: actions/setup-node@master
|
||||
with:
|
||||
node-version: 12.x
|
||||
- run: npm install
|
||||
working-directory: ./deploy-cloudrun
|
||||
- uses: ./setup-gcloud # Set up ADC to make authenticated request to service
|
||||
with:
|
||||
service_account_email: ${{ secrets.DEPLOY_CLOUDRUN_SA_EMAIL }}
|
||||
service_account_key: ${{ secrets.DEPLOY_CLOUDRUN_SA_KEY_B64 }}
|
||||
export_default_credentials: true
|
||||
- name: integration tests
|
||||
run: npm run e2e-tests
|
||||
working-directory: ./deploy-cloudrun
|
||||
env:
|
||||
URL: ${{ steps.deploy.outputs.url }}
|
||||
GCLOUD_PROJECT: ${{ secrets.DEPLOY_CLOUDRUN_PROJECT_ID }}
|
||||
|
||||
metadata:
|
||||
name: with full YAML metada
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- id: deploy
|
||||
uses: ./deploy-cloudrun
|
||||
with:
|
||||
credentials: ${{ secrets.DEPLOY_CLOUDRUN_SA_KEY_JSON }}
|
||||
metadata: ./deploy-cloudrun/tests/unit/service.full.yaml
|
||||
- uses: actions/setup-node@master
|
||||
with:
|
||||
node-version: 12.x
|
||||
- run: npm install
|
||||
working-directory: ./deploy-cloudrun
|
||||
- uses: ./setup-gcloud # Set up ADC to make authenticated request to service # Set up ADC to make authenticated request to service
|
||||
with:
|
||||
service_account_email: ${{ secrets.DEPLOY_CLOUDRUN_SA_EMAIL }}
|
||||
service_account_key: ${{ secrets.DEPLOY_CLOUDRUN_SA_KEY_B64 }}
|
||||
export_default_credentials: true
|
||||
- name: integration tests
|
||||
run: npm run e2e-tests
|
||||
working-directory: ./deploy-cloudrun
|
||||
env:
|
||||
URL: ${{ steps.deploy.outputs.url }}
|
||||
GCLOUD_PROJECT: ${{ secrets.DEPLOY_CLOUDRUN_PROJECT_ID }}
|
||||
44
.github/workflows/deploy-cloudrun.yml
vendored
Normal file
44
.github/workflows/deploy-cloudrun.yml
vendored
Normal file
|
|
@ -0,0 +1,44 @@
|
|||
name: deploy-cloudrun Unit
|
||||
|
||||
on:
|
||||
push:
|
||||
paths:
|
||||
- 'deploy-cloudrun/**'
|
||||
pull_request:
|
||||
paths:
|
||||
- 'deploy-cloudrun/**'
|
||||
|
||||
jobs:
|
||||
run:
|
||||
name: test
|
||||
runs-on: ${{ matrix.operating-system }}
|
||||
strategy:
|
||||
matrix:
|
||||
operating-system: [ubuntu-latest, windows-latest, macos-latest]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
- uses: actions/setup-node@master
|
||||
with:
|
||||
node-version: 12.x
|
||||
|
||||
- name: npm install
|
||||
run: npm install
|
||||
working-directory: ./deploy-cloudrun
|
||||
|
||||
- name: npm lint
|
||||
run: npm run lint
|
||||
working-directory: ./deploy-cloudrun
|
||||
|
||||
- uses: ./setup-gcloud
|
||||
with:
|
||||
service_account_email: ${{ secrets.DEPLOY_CLOUDRUN_SA_EMAIL }}
|
||||
service_account_key: ${{ secrets.DEPLOY_CLOUDRUN_SA_KEY_B64 }}
|
||||
export_default_credentials: true
|
||||
- name: npm test
|
||||
run: npm run test
|
||||
working-directory: ./deploy-cloudrun
|
||||
env:
|
||||
TEST_DEPLOY_CLOUDRUN_CREDENTIALS: ${{ secrets.DEPLOY_CLOUDRUN_SA_KEY_B64 }}
|
||||
TEST_DEPLOY_CLOUDRUN_PROJECT: ${{ secrets.DEPLOY_CLOUDRUN_PROJECT_ID }}
|
||||
GCLOUD_PROJECT: ${{ secrets.DEPLOY_CLOUDRUN_PROJECT_ID }}
|
||||
31
deploy-cloudrun/.eslintrc.js
Normal file
31
deploy-cloudrun/.eslintrc.js
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
root: true,
|
||||
parser: '@typescript-eslint/parser',
|
||||
plugins: ['@typescript-eslint'],
|
||||
extends: [
|
||||
'eslint:recommended',
|
||||
'plugin:@typescript-eslint/eslint-recommended',
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:prettier/recommended',
|
||||
'prettier/@typescript-eslint',
|
||||
],
|
||||
rules: {
|
||||
'@typescript-eslint/camelcase': 'off',
|
||||
}
|
||||
};
|
||||
45
deploy-cloudrun/.gitignore
vendored
Normal file
45
deploy-cloudrun/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
node_modules/
|
||||
runner/
|
||||
|
||||
# Rest of the file pulled from https://github.com/github/gitignore/blob/master/Node.gitignore
|
||||
# Logs
|
||||
logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
lerna-debug.log*
|
||||
|
||||
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||
|
||||
# Runtime data
|
||||
pids
|
||||
*.pid
|
||||
*.seed
|
||||
*.pid.lock
|
||||
|
||||
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||
lib-cov
|
||||
|
||||
# Coverage directory used by tools like istanbul
|
||||
coverage
|
||||
*.lcov
|
||||
|
||||
# TypeScript v1 declaration files
|
||||
typings/
|
||||
|
||||
# TypeScript cache
|
||||
*.tsbuildinfo
|
||||
|
||||
# Optional npm cache directory
|
||||
.npm
|
||||
|
||||
# Optional eslint cache
|
||||
.eslintcache
|
||||
|
||||
# Optional REPL history
|
||||
.node_repl_history
|
||||
|
||||
# Output of 'npm pack'
|
||||
*.tgz
|
||||
30
deploy-cloudrun/.prettierrc.js
Normal file
30
deploy-cloudrun/.prettierrc.js
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
arrowParens: 'always',
|
||||
bracketSpacing: true,
|
||||
endOfLine: 'auto',
|
||||
jsxBracketSameLine: true,
|
||||
jsxSingleQuote: true,
|
||||
printWidth: 80,
|
||||
quoteProps: 'consistent',
|
||||
semi: true,
|
||||
singleQuote: true,
|
||||
tabWidth: 2,
|
||||
trailingComma: 'all',
|
||||
useTabs: false,
|
||||
};
|
||||
182
deploy-cloudrun/README.md
Normal file
182
deploy-cloudrun/README.md
Normal file
|
|
@ -0,0 +1,182 @@
|
|||
<!--
|
||||
Copyright 2020 Google LLC
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
# deploy-cloudrun
|
||||
|
||||
This action deploys your container image to [Cloud Run][cloud-run] and makes the URL
|
||||
available to later build steps via outputs.
|
||||
|
||||
## Prerequisites
|
||||
|
||||
This action requires:
|
||||
|
||||
- Google Cloud credentials that are authorized to deploy a
|
||||
Cloud Run service. See the Authorization section below for more information.
|
||||
|
||||
- [Enable the Cloud Run API](http://console.cloud.google.com/apis/library/run.googleapis.com?_ga=2.267842766.1374248275.1591025444-475066991.1589991158)
|
||||
|
||||
## Usage
|
||||
|
||||
```yaml
|
||||
steps:
|
||||
- id: deploy
|
||||
uses: GoogleCloudPlatform/github-actions/deploy-appengine@master
|
||||
with:
|
||||
image: gcr.io/cloudrun/hello
|
||||
service: hello-cloud-run
|
||||
credentials: ${{ secrets.gcp_credentials }}
|
||||
|
||||
# Example of using the output
|
||||
- id: test
|
||||
run: curl "${{ steps.deploy.outputs.url }}"
|
||||
```
|
||||
|
||||
## Inputs
|
||||
|
||||
- `image`: Name of the container image to deploy (e.g. gcr.io/cloudrun/hello:latest).
|
||||
Required if not using a service YAML.
|
||||
|
||||
- `service`: ID of the service or fully qualified identifier for the service.
|
||||
Required if not using a service YAML.
|
||||
|
||||
- `region`: Region in which the resource can be found.
|
||||
|
||||
- `credentials`: Service account key to use for authentication. This should be
|
||||
the JSON formatted private key which can be exported from the Cloud Console. The
|
||||
value can be raw or base64-encoded. Required if not using a the
|
||||
`setup-gcloud` action with exported credentials.
|
||||
|
||||
- `env_vars`: List of key-value pairs to set as environment variables in the format:
|
||||
KEY1=VALUE1,KEY2=VALUE2. All existing environment variables will be
|
||||
removed first.
|
||||
|
||||
- `metadata`: YAML serivce description for the Cloud Run service. See
|
||||
[Metadata customizations](#metadata-customizations) for more information.
|
||||
|
||||
- `project_id`: (Optional) ID of the Google Cloud project. If provided, this
|
||||
will override the project configured by gcloud.
|
||||
|
||||
### Metadata customizations
|
||||
|
||||
You can store your service specification in a YAML file. This will allow for
|
||||
further service configuration, such as [memory limits](https://cloud.google.com/run/docs/configuring/memory-limits),
|
||||
[CPU allocation](https://cloud.google.com/run/docs/configuring/cpu),
|
||||
[max instances](https://cloud.google.com/run/docs/configuring/max-instances),
|
||||
and [more.](https://cloud.google.com/sdk/gcloud/reference/run/deploy#OPTIONAL-FLAGS)
|
||||
|
||||
- See [Deploying a new service](https://cloud.google.com/run/docs/deploying#yaml)
|
||||
to create a new YAML service definition, for example:
|
||||
|
||||
```YAML
|
||||
apiVersion: serving.knative.dev/v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: SERVICE
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- image: IMAGE
|
||||
```
|
||||
|
||||
- See [Deploy a new revision of an existing service](https://cloud.google.com/run/docs/deploying#yaml_1)
|
||||
to generated a YAML service specification from an existing service:
|
||||
|
||||
```
|
||||
gcloud run services describe SERVICE --format yaml > service.yaml
|
||||
```
|
||||
## Allow unauthenticated requests
|
||||
|
||||
A Cloud Run product recommendation is that CI/CD systems not set or change
|
||||
settings for allowing unauthenticated invocations. New deployments are
|
||||
automatically private services, while deploying a revision of a public
|
||||
(unauthenticated) service will preserve the IAM setting of public
|
||||
(unauthenticated). For more information, see [Controlling access on an individual service](https://cloud.google.com/run/docs/securing/managing-access).
|
||||
|
||||
## Outputs
|
||||
|
||||
- `url`: The URL of your Cloud Run service.
|
||||
|
||||
## Authorization
|
||||
|
||||
There are a few ways to authenticate this action. A service account will be needed
|
||||
with the following roles:
|
||||
|
||||
- Cloud Run Admin (`roles/run.admin`):
|
||||
- Can create, update, and delete services.
|
||||
- Can get and set IAM policies.
|
||||
|
||||
This service account needs to a member of the `Compute Engine default service account`,
|
||||
`(PROJECT_NUMBER-compute@developer.gserviceaccount.com)`, with role
|
||||
`Service Account User`. To grant a user permissions for a service account, use
|
||||
one of the methods found in [Configuring Ownership and access to a service account](https://cloud.google.com/iam/docs/granting-roles-to-service-accounts#granting_access_to_a_user_for_a_service_account).
|
||||
|
||||
### Used with `setup-gcloud`
|
||||
|
||||
You can provide credentials using the [setup-gcloud][setup-gcloud] action:
|
||||
|
||||
```yaml
|
||||
- uses: GoogleCloudPlatform/github-actions/setup-gcloud@master
|
||||
with:
|
||||
version: '290.0.1'
|
||||
service_account_key: ${{ secrets.GCP_SA_KEY }}
|
||||
export_default_credentials: true
|
||||
- id: Deploy
|
||||
uses: GoogleCloudPlatform/github-actions/deploy-cloudrun@master
|
||||
with:
|
||||
image: gcr.io/cloudrun/hello
|
||||
service: hello-cloud-run
|
||||
```
|
||||
|
||||
### Via Credentials
|
||||
|
||||
You can provide [Google Cloud Service Account JSON][sa] directly to the action
|
||||
by specifying the `credentials` input. First, create a [GitHub
|
||||
Secret][gh-secret] that contains the JSON content, then import it into the
|
||||
action:
|
||||
|
||||
```yaml
|
||||
- id: Deploy
|
||||
uses: GoogleCloudPlatform/github-actions/deploy-cloudrun@master
|
||||
with:
|
||||
credentials: ${{ secrets.GCP_SA_KEY }}
|
||||
image: gcr.io/cloudrun/hello
|
||||
service: hello-cloud-run
|
||||
```
|
||||
|
||||
### Via Application Default Credentials
|
||||
|
||||
If you are hosting your own runners, **and** those runners are on Google Cloud,
|
||||
you can leverage the Application Default Credentials of the instance. This will
|
||||
authenticate requests as the service account attached to the instance. **This
|
||||
only works using a custom runner hosted on GCP.**
|
||||
|
||||
```yaml
|
||||
- id: Deploy
|
||||
uses: GoogleCloudPlatform/github-actions/deploy-cloudrun@master
|
||||
with:
|
||||
image: gcr.io/cloudrun/hello
|
||||
service: hello-cloud-run
|
||||
```
|
||||
|
||||
The action will automatically detect and use the Application Default
|
||||
Credentials.
|
||||
|
||||
[cloud-run]: https://cloud.google.com/run
|
||||
[sm]: https://cloud.google.com/secret-manager
|
||||
[sa]: https://cloud.google.com/iam/docs/creating-managing-service-accounts
|
||||
[gh-runners]: https://help.github.com/en/actions/hosting-your-own-runners/about-self-hosted-runners
|
||||
[gh-secret]: https://help.github.com/en/actions/configuring-and-managing-workflows/creating-and-storing-encrypted-secrets
|
||||
[setup-gcloud]: ../setup-gcloud
|
||||
71
deploy-cloudrun/action.yml
Normal file
71
deploy-cloudrun/action.yml
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
# Copyright 2020 Google LLC
|
||||
#
|
||||
# Licensed under the Apache License, Version 2.0 (the "License");
|
||||
# you may not use this file except in compliance with the License.
|
||||
# You may obtain a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS,
|
||||
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
name: Deploy to Cloud Run
|
||||
author: GoogleCloudPlatform
|
||||
description: |-
|
||||
Cloud Run is a fully managed compute platform that automatically scales your
|
||||
stateless containers. Use this action to deploy a container that has already
|
||||
been uploaded to the Container Registry repository to Cloud Run.
|
||||
|
||||
inputs:
|
||||
image:
|
||||
description: |-
|
||||
Name of the container image to deploy (e.g. gcr.io/cloudrun/hello:latest).
|
||||
Required if not using a service YAML.
|
||||
required: false
|
||||
|
||||
service:
|
||||
description: |-
|
||||
ID of the service or fully qualified identifier for the service.
|
||||
Required if not using a service YAML.
|
||||
required: false
|
||||
|
||||
region:
|
||||
description: |-
|
||||
Region in which the resource can be found.
|
||||
required: false
|
||||
default: us-central1
|
||||
|
||||
env_vars:
|
||||
description: |-
|
||||
List of key-value pairs to set as environment variables in the format:
|
||||
KEY1=VALUE1,KEY2=VALUE2. All existing environment variables will be
|
||||
removed first.
|
||||
required: false
|
||||
|
||||
metadata:
|
||||
description: |-
|
||||
YAML serivce description for the Cloud Run service.
|
||||
required: false
|
||||
|
||||
project_id:
|
||||
description: The GCP project ID. Overrides project ID set by credentials.
|
||||
required: false
|
||||
|
||||
credentials:
|
||||
description: |-
|
||||
Service account key to use for authentication. This should be the JSON
|
||||
formatted private key which can be exported from the Cloud Console. The
|
||||
value can be raw or base64-encoded. Required if not using a the
|
||||
setup-gcloud action with exported credentials.
|
||||
required: false
|
||||
|
||||
outputs:
|
||||
url:
|
||||
description: The URL of your Cloud Run service
|
||||
|
||||
runs:
|
||||
using: node12
|
||||
main: dist/index.js
|
||||
325341
deploy-cloudrun/dist/index.js
vendored
Normal file
325341
deploy-cloudrun/dist/index.js
vendored
Normal file
File diff suppressed because one or more lines are too long
2335
deploy-cloudrun/package-lock.json
generated
Normal file
2335
deploy-cloudrun/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load diff
56
deploy-cloudrun/package.json
Normal file
56
deploy-cloudrun/package.json
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
{
|
||||
"name": "deploy-cloudrun",
|
||||
"version": "0.1.0",
|
||||
"description": "Github Action: Deploy to Google Cloud Run",
|
||||
"main": "dist/index.js",
|
||||
"scripts": {
|
||||
"build": "ncc build src/index.ts",
|
||||
"lint": "eslint . --ext .ts,.tsx",
|
||||
"format": "prettier --write **/*.ts",
|
||||
"test": "mocha -r ts-node/register -t 120s 'tests/unit/*.test.ts'",
|
||||
"e2e-tests": "mocha -r ts-node/register -t 120s 'tests/e2e.test.ts'"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "git+https://github.com/GoogleCloudPlatform/github-actions.git"
|
||||
},
|
||||
"keywords": [
|
||||
"actions",
|
||||
"google",
|
||||
"cloud",
|
||||
"cloud",
|
||||
"run"
|
||||
],
|
||||
"author": "Google LLC",
|
||||
"license": "Apache-2.0",
|
||||
"bugs": {
|
||||
"url": "https://github.com/GoogleCloudPlatform/github-actions/issues"
|
||||
},
|
||||
"homepage": "https://github.com/GoogleCloudPlatform/github-actions#readme",
|
||||
"dependencies": {
|
||||
"@actions/core": "^1.2.4",
|
||||
"fs": "0.0.1-security",
|
||||
"gaxios": "^3.0.3",
|
||||
"googleapis": "^49.0.0",
|
||||
"yaml": "^1.9.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/chai": "^4.2.9",
|
||||
"@types/lodash": "^4.14.150",
|
||||
"@types/mocha": "^7.0.1",
|
||||
"@types/node": "^13.7.4",
|
||||
"@types/uuid": "^7.0.3",
|
||||
"@typescript-eslint/eslint-plugin": "^2.20.0",
|
||||
"@typescript-eslint/parser": "^2.20.0",
|
||||
"@zeit/ncc": "^0.21.0",
|
||||
"chai": "^4.2.0",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-config-prettier": "^6.10.0",
|
||||
"eslint-plugin-prettier": "^3.1.2",
|
||||
"google-auth-library": "^6.0.0",
|
||||
"mocha": "^7.0.1",
|
||||
"prettier": "^1.19.1",
|
||||
"ts-node": "^8.6.2",
|
||||
"typescript": "^3.8.2"
|
||||
}
|
||||
}
|
||||
257
deploy-cloudrun/src/cloudRun.ts
Normal file
257
deploy-cloudrun/src/cloudRun.ts
Normal file
|
|
@ -0,0 +1,257 @@
|
|||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as core from '@actions/core';
|
||||
import { GaxiosResponse } from 'gaxios';
|
||||
import { google, run_v1 } from 'googleapis';
|
||||
import {
|
||||
GoogleAuth,
|
||||
JWT,
|
||||
Compute,
|
||||
UserRefreshClient,
|
||||
} from 'google-auth-library';
|
||||
import { Service } from './service';
|
||||
|
||||
/**
|
||||
* Available options to create the client.
|
||||
*
|
||||
* @param credentials GCP JSON credentials (default uses ADC).
|
||||
* @param endpoint GCP endpoint (useful for testing).
|
||||
*/
|
||||
type ClientOptions = {
|
||||
credentials?: string;
|
||||
projectId?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Wraps interactions with the Google Cloud Run API.
|
||||
*
|
||||
* @param region Region of the GCP resource.
|
||||
* @param opts list of ClientOptions.
|
||||
* @returns CloudRun client.
|
||||
*/
|
||||
export class CloudRun {
|
||||
readonly methodOptions = {
|
||||
userAgentDirectives: [
|
||||
{
|
||||
product: 'github-actions-deploy-cloudrun',
|
||||
version: '0.1.0',
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
private run = google.run('v1');
|
||||
readonly auth: GoogleAuth;
|
||||
readonly parent: string;
|
||||
authClient: any;
|
||||
|
||||
constructor(region: string, opts?: ClientOptions) {
|
||||
let projectId = opts?.projectId;
|
||||
if (
|
||||
!opts?.credentials &&
|
||||
(!process.env.GCLOUD_PROJECT ||
|
||||
!process.env.GOOGLE_APPLICATION_CREDENTIALS)
|
||||
) {
|
||||
throw new Error(
|
||||
'No method for authentication. Set credentials in this action or export credentials from the setup-gcloud action',
|
||||
);
|
||||
}
|
||||
// Instatiate Auth Client
|
||||
// This method looks for the GCLOUD_PROJECT and GOOGLE_APPLICATION_CREDENTIALS
|
||||
// environment variables.
|
||||
this.auth = new google.auth.GoogleAuth({
|
||||
scopes: ['https://www.googleapis.com/auth/cloud-platform'],
|
||||
});
|
||||
// Set credentials, if any.
|
||||
let jsonContent;
|
||||
if (opts?.credentials) {
|
||||
let creds = opts?.credentials;
|
||||
if (!opts?.credentials.trim().startsWith('{')) {
|
||||
creds = Buffer.from(creds, 'base64').toString('utf8');
|
||||
}
|
||||
jsonContent = JSON.parse(creds);
|
||||
this.auth.jsonContent = jsonContent;
|
||||
}
|
||||
// Set project Id
|
||||
if (!projectId && jsonContent && jsonContent.project_id) {
|
||||
projectId = jsonContent.project_id;
|
||||
core.info('Setting project Id from credentials');
|
||||
} else if (!projectId && process.env.GCLOUD_PROJECT) {
|
||||
projectId = process.env.GCLOUD_PROJECT;
|
||||
core.info('Setting project Id from $GCLOUD_PROJECT');
|
||||
} else if (!projectId) {
|
||||
throw new Error('No project Id found. Set project Id in this action.');
|
||||
}
|
||||
|
||||
this.parent = `projects/${projectId}/locations/${region}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves the auth client for authenticating requests.
|
||||
*
|
||||
* @returns JWT | Compute | UserRefreshClient.
|
||||
*/
|
||||
async getAuthClient(): Promise<JWT | Compute | UserRefreshClient> {
|
||||
if (!this.authClient) {
|
||||
this.authClient = await this.auth.getClient();
|
||||
}
|
||||
return this.authClient;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generates full resource name.
|
||||
*
|
||||
* @param service Service object.
|
||||
* @returns full resource name.
|
||||
*/
|
||||
getResource(service: Service): string {
|
||||
return `${this.parent}/services/${service.name}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a Cloud Run services.
|
||||
*
|
||||
* @returns a Cloud Run service.
|
||||
*/
|
||||
async getService(service: Service): Promise<run_v1.Schema$Service> {
|
||||
const authClient = await this.getAuthClient();
|
||||
const getRequest: run_v1.Params$Resource$Projects$Locations$Services$Get = {
|
||||
name: this.getResource(service),
|
||||
auth: authClient,
|
||||
};
|
||||
const serviceResponse: GaxiosResponse<run_v1.Schema$Service> = await this.run.projects.locations.services.get(
|
||||
getRequest,
|
||||
this.methodOptions,
|
||||
);
|
||||
return serviceResponse.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieves a list of Cloud Run services.
|
||||
*
|
||||
* @returns list of Cloud Run services.
|
||||
*/
|
||||
async listServices(): Promise<string[]> {
|
||||
const authClient = await this.getAuthClient();
|
||||
const listRequest: run_v1.Params$Resource$Projects$Locations$Services$List = {
|
||||
parent: this.parent,
|
||||
auth: authClient,
|
||||
};
|
||||
const serviceListResponse: GaxiosResponse<run_v1.Schema$ListServicesResponse> = await this.run.projects.locations.services.list(
|
||||
listRequest,
|
||||
this.methodOptions,
|
||||
);
|
||||
const serviceList: run_v1.Schema$ListServicesResponse =
|
||||
serviceListResponse.data;
|
||||
let serviceNames: string[] = [];
|
||||
if (serviceList.items !== undefined) {
|
||||
serviceNames = serviceList.items!.map(
|
||||
(service: run_v1.Schema$Service) => service.metadata!.name as string,
|
||||
);
|
||||
}
|
||||
return serviceNames;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deploy a Cloud Run service.
|
||||
*
|
||||
* @param service Service object.
|
||||
* @returns Service object.
|
||||
*/
|
||||
async deploy(service: Service): Promise<run_v1.Schema$Service> {
|
||||
const authClient = await this.getAuthClient();
|
||||
const serviceNames = await this.listServices();
|
||||
let serviceResponse: GaxiosResponse<run_v1.Schema$Service>;
|
||||
if (serviceNames!.includes(service.name)) {
|
||||
core.info('Creating a service revision...');
|
||||
// Replace service
|
||||
const createServiceRequest: run_v1.Params$Resource$Projects$Locations$Services$Replaceservice = {
|
||||
name: this.getResource(service),
|
||||
auth: authClient,
|
||||
requestBody: service.request,
|
||||
};
|
||||
serviceResponse = await this.run.projects.locations.services.replaceService(
|
||||
createServiceRequest,
|
||||
this.methodOptions,
|
||||
);
|
||||
} else {
|
||||
core.info('Creating a new service...');
|
||||
// Create service
|
||||
const createServiceRequest: run_v1.Params$Resource$Projects$Locations$Services$Create = {
|
||||
parent: this.parent,
|
||||
auth: authClient,
|
||||
requestBody: service.request,
|
||||
};
|
||||
serviceResponse = await this.run.projects.locations.services.create(
|
||||
createServiceRequest,
|
||||
this.methodOptions,
|
||||
);
|
||||
}
|
||||
core.info(`Service ${service.name} has been successfully deployed.`);
|
||||
return serviceResponse.data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Deletes a Cloud Run service.
|
||||
*
|
||||
* @param service Service object.
|
||||
*/
|
||||
async delete(service: Service): Promise<void> {
|
||||
const authClient = await this.getAuthClient();
|
||||
try {
|
||||
const deleteServiceRequest: run_v1.Params$Resource$Projects$Locations$Services$Delete = {
|
||||
name: this.getResource(service),
|
||||
auth: authClient,
|
||||
};
|
||||
await this.run.projects.locations.services.delete(
|
||||
deleteServiceRequest,
|
||||
this.methodOptions,
|
||||
);
|
||||
core.info(`Service ${service.name} has been successfully deleted.`);
|
||||
} catch (e) {
|
||||
core.info(`Error deleting Service ${service.name}: ` + e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set's IAM policy for service (Not Recommended).
|
||||
*
|
||||
* @param service Service object.
|
||||
*/
|
||||
async allowUnauthenticatedRequests(service: Service): Promise<void> {
|
||||
const authClient = await this.getAuthClient();
|
||||
const bindings: run_v1.Schema$Binding[] = [
|
||||
{
|
||||
members: ['allUsers'],
|
||||
role: 'roles/run.invoker',
|
||||
},
|
||||
];
|
||||
const iamPolicy: run_v1.Schema$SetIamPolicyRequest = {
|
||||
policy: {
|
||||
bindings,
|
||||
},
|
||||
};
|
||||
const setIamPolicyRequest: run_v1.Params$Resource$Projects$Locations$Services$Setiampolicy = {
|
||||
resource: this.getResource(service),
|
||||
auth: authClient,
|
||||
requestBody: iamPolicy,
|
||||
};
|
||||
await this.run.projects.locations.services.setIamPolicy(
|
||||
setIamPolicyRequest,
|
||||
this.methodOptions,
|
||||
);
|
||||
}
|
||||
}
|
||||
55
deploy-cloudrun/src/index.ts
Normal file
55
deploy-cloudrun/src/index.ts
Normal file
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import * as core from '@actions/core';
|
||||
import { CloudRun } from './cloudRun';
|
||||
import { Service } from './service';
|
||||
|
||||
/**
|
||||
* Executes the main action. It includes the main business logic and is the
|
||||
* primary entry point. It is documented inline.
|
||||
*/
|
||||
async function run(): Promise<void> {
|
||||
try {
|
||||
// Get inputs
|
||||
const image = core.getInput('image');
|
||||
const name = core.getInput('service');
|
||||
const envVars = core.getInput('env_vars');
|
||||
const yaml = core.getInput('metadata');
|
||||
const credentials = core.getInput('credentials');
|
||||
const projectId = core.getInput('project_id');
|
||||
const region = core.getInput('region') || 'us-central1';
|
||||
|
||||
// Create Cloud Run client
|
||||
const client = new CloudRun(region, { projectId, credentials });
|
||||
|
||||
// Initialize service
|
||||
const service = new Service({ image, name, envVars, yaml });
|
||||
|
||||
// Deploy service
|
||||
let serviceResponse = await client.deploy(service);
|
||||
while (!serviceResponse.status!.url) {
|
||||
serviceResponse = await client.getService(service);
|
||||
}
|
||||
// Set URL as output
|
||||
core.setOutput('url', serviceResponse.status!.url);
|
||||
} catch (error) {
|
||||
core.error(error);
|
||||
core.setFailed(error.message);
|
||||
}
|
||||
}
|
||||
|
||||
run();
|
||||
131
deploy-cloudrun/src/service.ts
Normal file
131
deploy-cloudrun/src/service.ts
Normal file
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { run_v1 } from 'googleapis';
|
||||
import fs from 'fs';
|
||||
import YAML from 'yaml';
|
||||
|
||||
export type EnvVar = {
|
||||
name: string;
|
||||
value: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Available options to create the Service.
|
||||
*
|
||||
* @param image Name of the container image to deploy.
|
||||
* @param name Name of the Cloud Run service.
|
||||
* @param envVars String list of envvars.
|
||||
* @param yaml Path to YAML file.
|
||||
*/
|
||||
export type ServiceOptions = {
|
||||
image?: string;
|
||||
name?: string;
|
||||
envVars?: string;
|
||||
yaml?: string;
|
||||
};
|
||||
|
||||
/**
|
||||
* Construct a Cloud Run Service.
|
||||
*
|
||||
* @param opts ServiceOptions.
|
||||
* @returns Service.
|
||||
*/
|
||||
export class Service {
|
||||
readonly request: run_v1.Schema$Service;
|
||||
readonly name: string;
|
||||
|
||||
constructor(opts: ServiceOptions) {
|
||||
if ((!opts.name || !opts.image) && !opts.yaml) {
|
||||
throw new Error('Provide image and services names or a YAML file.');
|
||||
}
|
||||
|
||||
let request: run_v1.Schema$Service = {
|
||||
apiVersion: 'serving.knative.dev/v1',
|
||||
kind: 'Service',
|
||||
metadata: {},
|
||||
spec: {},
|
||||
};
|
||||
|
||||
// Parse Env Vars
|
||||
let envVars;
|
||||
if (opts?.envVars) {
|
||||
envVars = this.parseEnvVars(opts.envVars);
|
||||
}
|
||||
|
||||
// Parse YAML
|
||||
if (opts.yaml) {
|
||||
const file = fs.readFileSync(opts.yaml, 'utf8');
|
||||
const yaml = YAML.parse(file);
|
||||
request = yaml as run_v1.Schema$Service;
|
||||
}
|
||||
|
||||
// If name is provided, set or override
|
||||
if (opts.name) {
|
||||
if (request.metadata) {
|
||||
request.metadata.name = opts.name;
|
||||
} else {
|
||||
request.metadata = { name: opts.name };
|
||||
}
|
||||
}
|
||||
|
||||
// If image is provided, set or override
|
||||
if (opts.image) {
|
||||
const container: run_v1.Schema$Container = { image: opts.image };
|
||||
if (request.spec?.template) {
|
||||
request.spec.template!.spec!.containers = [container];
|
||||
} else {
|
||||
request.spec = {
|
||||
template: {
|
||||
spec: {
|
||||
containers: [container],
|
||||
},
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
// If Env Vars are provided, set or override
|
||||
if (envVars) {
|
||||
if (request.spec?.template?.spec?.containers) {
|
||||
request.spec!.template!.spec!.containers[0]!.env = envVars;
|
||||
}
|
||||
}
|
||||
|
||||
this.request = request;
|
||||
this.name = request.metadata!.name!;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses a string of the format `KEY1=VALUE1`.
|
||||
*
|
||||
* @param envVarInput Env var string to parse.
|
||||
* @returns EnvVar[].
|
||||
*/
|
||||
protected parseEnvVars(envVarInput: string): EnvVar[] {
|
||||
const envVarList = envVarInput.split(',');
|
||||
const envVars = envVarList.map((envVar) => {
|
||||
if (!envVar.includes('=')) {
|
||||
throw new TypeError(
|
||||
`Env Vars must be in "KEY1=VALUE1,KEY2=VALUE2" format, received ${envVar}`,
|
||||
);
|
||||
}
|
||||
const keyValue = envVar.split('=');
|
||||
return { name: keyValue[0], value: keyValue[1] };
|
||||
});
|
||||
return envVars;
|
||||
}
|
||||
}
|
||||
38
deploy-cloudrun/tests/e2e.test.ts
Normal file
38
deploy-cloudrun/tests/e2e.test.ts
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { expect } from 'chai';
|
||||
import 'mocha';
|
||||
import { GoogleAuth } from 'google-auth-library';
|
||||
|
||||
describe('E2E tests', function() {
|
||||
let URL: string;
|
||||
before(function() {
|
||||
if (process.env.URL) {
|
||||
URL = process.env.URL;
|
||||
} else {
|
||||
throw Error('URL not found.');
|
||||
}
|
||||
});
|
||||
it('can make a request', async function() {
|
||||
// Requires ADC to be set
|
||||
const auth = new GoogleAuth();
|
||||
const client = await auth.getIdTokenClient(URL);
|
||||
const response = await client.request({ url: URL });
|
||||
expect(response.status).to.be.equal(200);
|
||||
expect(response.data).to.include('Congrat');
|
||||
});
|
||||
});
|
||||
63
deploy-cloudrun/tests/unit/cloudRun.test.ts
Normal file
63
deploy-cloudrun/tests/unit/cloudRun.test.ts
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { expect } from 'chai';
|
||||
import 'mocha';
|
||||
|
||||
import { CloudRun } from '../../src/cloudRun';
|
||||
import { Service } from '../../src/service';
|
||||
import { JWT } from 'google-auth-library';
|
||||
|
||||
const credentials = process.env.TEST_DEPLOY_CLOUDRUN_CREDENTIALS;
|
||||
const project = process.env.TEST_DEPLOY_CLOUDRUN_PROJECT;
|
||||
const region = 'us-central1';
|
||||
const image = 'gcr.io/cloudrun/hello';
|
||||
const name = `test-${Math.round(Math.random() * 100000)}`; // Cloud Run currently has name length restrictions
|
||||
const service = new Service({ image, name });
|
||||
|
||||
describe('CloudRun', function() {
|
||||
it('initializes with JSON creds', function() {
|
||||
const client = new CloudRun(region, {
|
||||
credentials: `{"foo":"bar"}`,
|
||||
projectId: 'test',
|
||||
});
|
||||
expect(client.auth.jsonContent).eql({ foo: 'bar' });
|
||||
});
|
||||
|
||||
it('initializes with ADC', async function() {
|
||||
const client = new CloudRun(region);
|
||||
expect(client.auth.jsonContent).eql(null);
|
||||
const auth = (await client.getAuthClient()) as JWT;
|
||||
expect(auth.key).to.not.eql(undefined);
|
||||
});
|
||||
|
||||
it('can deploy service', async function() {
|
||||
if (!credentials) {
|
||||
this.skip();
|
||||
}
|
||||
const client = new CloudRun(region, {
|
||||
credentials: credentials,
|
||||
projectId: project,
|
||||
});
|
||||
let result = await client.deploy(service);
|
||||
while (!result.status!.url) {
|
||||
result = await client.getService(service);
|
||||
}
|
||||
expect(result).to.not.eql(null);
|
||||
expect(result.status!.url).to.include('run.app');
|
||||
await client.delete(service);
|
||||
});
|
||||
});
|
||||
9
deploy-cloudrun/tests/unit/service.basic.yaml
Normal file
9
deploy-cloudrun/tests/unit/service.basic.yaml
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
apiVersion: serving.knative.dev/v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: test-basic-yaml
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containers:
|
||||
- image: gcr.io/cloudrun/hello
|
||||
20
deploy-cloudrun/tests/unit/service.full.yaml
Normal file
20
deploy-cloudrun/tests/unit/service.full.yaml
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
apiVersion: serving.knative.dev/v1
|
||||
kind: Service
|
||||
metadata:
|
||||
name: test-full-yaml
|
||||
spec:
|
||||
template:
|
||||
spec:
|
||||
containerConcurrency: 20
|
||||
containers:
|
||||
- image: gcr.io/cloudrun/hello
|
||||
ports:
|
||||
- containerPort: 8080
|
||||
resources:
|
||||
limits:
|
||||
cpu: '2'
|
||||
memory: 1Gi
|
||||
timeoutSeconds: 300
|
||||
traffic:
|
||||
- latestRevision: true
|
||||
percent: 100
|
||||
104
deploy-cloudrun/tests/unit/service.test.ts
Normal file
104
deploy-cloudrun/tests/unit/service.test.ts
Normal file
|
|
@ -0,0 +1,104 @@
|
|||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the 'License');
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an 'AS IS' BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { expect } from 'chai';
|
||||
import { get } from 'lodash';
|
||||
import 'mocha';
|
||||
|
||||
import { Service, EnvVar } from '../../src/service';
|
||||
|
||||
const image = 'gcr.io/projectId/image';
|
||||
const name = 'serviceName';
|
||||
const yamlImage = 'gcr.io/cloudrun/hello';
|
||||
|
||||
describe('Service', function() {
|
||||
it('creates a service', function() {
|
||||
const service = new Service({ image, name });
|
||||
expect(service.request.metadata!.name).equal(name);
|
||||
expect(service.request).to.have.property('kind');
|
||||
});
|
||||
|
||||
it('parses one env vars', function() {
|
||||
const envVars = 'KEY1=VALUE1';
|
||||
const service = new Service({ image, name, envVars });
|
||||
const containers = get(service, 'request.spec.template.spec.containers');
|
||||
const actual = containers[0]?.env[0];
|
||||
const expected: EnvVar = {
|
||||
name: 'KEY1',
|
||||
value: 'VALUE1',
|
||||
};
|
||||
expect(actual.name).equal(expected.name);
|
||||
});
|
||||
|
||||
it('parses three env vars', function() {
|
||||
const envVars = 'KEY1=VALUE1,KEY2=VALUE2,KEY3=VALUE3';
|
||||
const service = new Service({ image, name, envVars });
|
||||
const containers = get(service, 'request.spec.template.spec.containers');
|
||||
const actual = containers[0]?.env;
|
||||
expect(actual).to.have.lengthOf(3);
|
||||
expect(actual[0]).to.eql({ name: 'KEY1', value: 'VALUE1' });
|
||||
});
|
||||
|
||||
it('throws error with bad env vars', function() {
|
||||
const envVars = 'KEY1,VALUE1';
|
||||
expect(function() {
|
||||
const service = new Service({ image, name, envVars });
|
||||
}).to.throw(
|
||||
'Env Vars must be in "KEY1=VALUE1,KEY2=VALUE2" format, received KEY1',
|
||||
);
|
||||
});
|
||||
|
||||
it('parses yaml', function() {
|
||||
const yaml = './tests/unit/service.basic.yaml';
|
||||
const service = new Service({ image, name, yaml });
|
||||
expect(service.request.metadata!.name).equal(name);
|
||||
const containers = get(service, 'request.spec.template.spec.containers');
|
||||
expect(containers[0]?.image).equal(image);
|
||||
});
|
||||
|
||||
it('creates service from yaml', function() {
|
||||
const yaml = './tests/unit/service.basic.yaml';
|
||||
const service = new Service({ yaml });
|
||||
expect(service.request.metadata!.name).equal('test-basic-yaml');
|
||||
const containers = get(service, 'request.spec.template.spec.containers');
|
||||
expect(containers[0]!.image).equal(yamlImage);
|
||||
expect(service.request).to.have.property('kind');
|
||||
});
|
||||
|
||||
it('parses yaml and env vars', function() {
|
||||
const yaml = './tests/unit/service.basic.yaml';
|
||||
const envVars = 'KEY1=VALUE1';
|
||||
const service = new Service({ yaml, envVars });
|
||||
const containers = get(service, 'request.spec.template.spec.containers');
|
||||
expect(containers).to.be.length(1);
|
||||
expect(containers[0].image).to.equal(yamlImage);
|
||||
expect(containers[0]?.env[0]).to.eql({ name: 'KEY1', value: 'VALUE1' });
|
||||
});
|
||||
|
||||
it('sets args from yaml', function() {
|
||||
const yaml = './tests/unit/service.full.yaml';
|
||||
const service = new Service({ yaml });
|
||||
expect(service.request.metadata!.name!).equal('test-full-yaml');
|
||||
const containers = get(service, 'request.spec.template.spec.containers');
|
||||
expect(containers[0]?.resources?.limits?.cpu).equal('2');
|
||||
expect(containers[0]?.resources?.limits?.memory).equal('1Gi');
|
||||
const concurrency = get(
|
||||
service,
|
||||
'request.spec.template.spec.containerConcurrency',
|
||||
);
|
||||
expect(concurrency).equal(20);
|
||||
});
|
||||
});
|
||||
30
deploy-cloudrun/tsconfig.json
Normal file
30
deploy-cloudrun/tsconfig.json
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
/*
|
||||
* Copyright 2020 Google LLC
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es6",
|
||||
"module": "commonjs",
|
||||
"lib": [
|
||||
"es6"
|
||||
],
|
||||
"outDir": "./dist",
|
||||
"rootDir": "./src",
|
||||
"strict": true,
|
||||
"noImplicitAny": true,
|
||||
"esModuleInterop": true
|
||||
},
|
||||
"exclude": ["node_modules", "**/*.test.ts"]
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue