enable cloudsql #1
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
name: Cloud Run Deploy Advanced (Reusable) | ||
on: | ||
workflow_call: | ||
inputs: | ||
environment: | ||
required: true | ||
type: string | ||
description: "Environment name" | ||
knative_yaml: | ||
required: true | ||
type: string | ||
description: Path to the Knative YAML manifest for deployment | ||
container_images: | ||
required: false | ||
type: string | ||
description: JSON map of container name to image (for multi-container deploys) | ||
container_configs: | ||
required: false | ||
type: string | ||
description: >- | ||
JSON map of container name to base64-encoded config content. | ||
Example: {"api":"base64content","envoy":"base64content","other-sidecar":"base64content"} | ||
The workflow will find the APP_CONFIG_FILE_B64 env var in each container and inject the config. | ||
Special keys for authorization configuration (RBAC/ABAC): | ||
- "auth_model": Injects into AUTH_MODEL_FILE_B64 env var in containers specified by auth_containers input | ||
- "auth_policy": Injects into AUTH_POLICY_FILE_B64 env var in containers specified by auth_containers input | ||
Example with auth: {"api":"base64content","envoy":"base64content","auth_model":"base64content","auth_policy":"base64content"} | ||
auth_containers: | ||
required: false | ||
type: string | ||
description: >- | ||
JSON array of container names that need authorization configuration injection (RBAC/ABAC). | ||
Example: ["api", "store", "custom-service"] | ||
Required when using auth_model or auth_policy in container_configs. | ||
<<<<<<< Updated upstream | ||
======= | ||
cloudsql_instances: | ||
required: false | ||
type: string | ||
description: >- | ||
Comma-separated list of CloudSQL instance connection names to add to the service. | ||
Example: "project:region:instance1,project:region:instance2" | ||
allow_unauthenticated: | ||
required: false | ||
type: boolean | ||
default: false | ||
description: "Whether to allow unauthenticated access to the service" | ||
>>>>>>> Stashed changes | ||
outputs: | ||
url: | ||
description: "The deployed Cloud Run service URL." | ||
value: ${{ jobs.deploy.outputs.url }} | ||
jobs: | ||
deploy: | ||
runs-on: zondax-runners | ||
environment: ${{ inputs.environment }} | ||
outputs: | ||
url: ${{ steps.get-url.outputs.url }} | ||
permissions: | ||
id-token: write # Enables automatic ID token injection for google-github-actions/auth | ||
contents: read # Required for actions/checkout and GitHub API access | ||
pull-requests: write # Required for commenting on PRs | ||
steps: | ||
- name: Environment Logging | ||
run: | | ||
echo "Environment: ${{ inputs.environment }}" | ||
echo "Region: ${{ vars.PULUMI_GAR_LOCATION }}" | ||
echo "WIF Provider: ${{ vars.PULUMI_DEPLOY_WIF_PROVIDER }}" | ||
echo "Service Account: ${{ vars.PULUMI_SA_CLOUDRUN }}" | ||
echo "GCP Project ID: ${{ vars.PULUMI_GCP_PROJECT_ID }}" | ||
echo "GAR Location: ${{ vars.PULUMI_GAR_LOCATION }}" | ||
- uses: actions/checkout@v4 | ||
- name: Authenticate with GCP | ||
id: auth | ||
uses: google-github-actions/auth@v2 | ||
with: | ||
workload_identity_provider: ${{ vars.PULUMI_DEPLOY_WIF_PROVIDER }} | ||
- name: Set up Cloud SDK | ||
uses: google-github-actions/setup-gcloud@v2 | ||
- name: Install go | ||
uses: actions/setup-go@v5 | ||
with: | ||
go-version: 1.24 | ||
- name: Install yq | ||
run: | | ||
go install github.com/mikefarah/yq/v4@latest | ||
- name: Prepare Knative YAML for deploy | ||
id: prepare-yaml | ||
run: | | ||
# 1. Copy the Knative YAML manifest | ||
cp ${{ inputs.knative_yaml }} service.deploy.yaml | ||
# Replace PR_NUMBER placeholder if present (only for knative.yaml file, not config) | ||
sed -i "s#<PR_NUMBER>#${{ github.event.number }}#g" service.deploy.yaml | ||
# Replace <REGION> placeholder if present | ||
sed -i "s#<REGION>#${{ vars.PULUMI_GAR_LOCATION }}#g" service.deploy.yaml | ||
# Process container images if specified | ||
if [ -n "${{ inputs.container_images }}" ]; then | ||
echo "Processing container images..." | ||
echo '${{ inputs.container_images }}' > container_images.json | ||
# Validate JSON before processing | ||
jq empty container_images.json 2>/dev/null || { echo "ERROR: Invalid container_images JSON format"; exit 1; } | ||
# Process each container image | ||
jq -r 'to_entries[] | [.key, .value] | @tsv' container_images.json | while read -r container_name image_url; do | ||
echo "Updating image for container: $container_name → $image_url" | ||
# Try placeholder replacement first | ||
placeholder="<${container_name^^}_IMAGE>" | ||
if grep -q "$placeholder" service.deploy.yaml; then | ||
sed -i "s#$placeholder#$image_url#g" service.deploy.yaml | ||
# Then try direct container name update if it has a placeholder | ||
elif yq e '.spec.template.spec.containers[] | select(.name == "'"$container_name"'") | .image' service.deploy.yaml | grep -q '<.*>'; then | ||
yq e '.spec.template.spec.containers[] |= select(.name == "'"$container_name"'").image = "'"$image_url"'"' -i service.deploy.yaml | ||
else | ||
echo " Skipping container with static image" | ||
fi | ||
done | ||
fi | ||
# Inject configs if specified | ||
if [ -n "${{ inputs.container_configs }}" ]; then | ||
echo "Processing container configs..." | ||
echo '${{ inputs.container_configs }}' > container_configs.json | ||
jq empty container_configs.json 2>/dev/null || { echo "ERROR: Invalid container_configs JSON format"; exit 1; } | ||
# Process each configuration entry | ||
jq -r 'to_entries[] | [.key, .value] | @tsv' container_configs.json | while read -r name config; do | ||
# Replace PR number if needed | ||
[ -n "${{ github.event.number }}" ] && config=$(echo "$config" | base64 -d | sed "s/<github_event_number>/${{ github.event.number }}/g" | base64 -w 0) | ||
case "$name" in | ||
auth_model) | ||
# Inject authorization model into specified containers (RBAC/ABAC) | ||
if [ -z "${{ inputs.auth_containers }}" ]; then | ||
echo "ERROR: auth_containers input is required when using auth_model" | ||
exit 1 | ||
fi | ||
echo '${{ inputs.auth_containers }}' | jq -r '.[]' | while read -r container_name; do | ||
if yq e '.spec.template.spec.containers[] | select(.name == "'"$container_name"'") | .env[] | select(.name == "AUTH_MODEL_FILE_B64")' service.deploy.yaml &>/dev/null; then | ||
yq e '.spec.template.spec.containers[] |= select(.name == "'"$container_name"'").env[] |= select(.name == "AUTH_MODEL_FILE_B64").value = "'"$config"'"' -i service.deploy.yaml | ||
echo "✓ Updated authorization model config for container: $container_name" | ||
else | ||
echo "⚠️ Container '$container_name' does not have AUTH_MODEL_FILE_B64 env var, skipping" | ||
fi | ||
done | ||
;; | ||
auth_policy) | ||
# Inject authorization policy into specified containers (RBAC/ABAC) | ||
if [ -z "${{ inputs.auth_containers }}" ]; then | ||
echo "ERROR: auth_containers input is required when using auth_policy" | ||
exit 1 | ||
fi | ||
echo '${{ inputs.auth_containers }}' | jq -r '.[]' | while read -r container_name; do | ||
if yq e '.spec.template.spec.containers[] | select(.name == "'"$container_name"'") | .env[] | select(.name == "AUTH_POLICY_FILE_B64")' service.deploy.yaml &>/dev/null; then | ||
yq e '.spec.template.spec.containers[] |= select(.name == "'"$container_name"'").env[] |= select(.name == "AUTH_POLICY_FILE_B64").value = "'"$config"'"' -i service.deploy.yaml | ||
echo "✓ Updated authorization policy config for container: $container_name" | ||
else | ||
echo "⚠️ Container '$container_name' does not have AUTH_POLICY_FILE_B64 env var, skipping" | ||
fi | ||
done | ||
;; | ||
*) | ||
# Validate container exists | ||
yq e '.spec.template.spec.containers[] | select(.name == "'"$name"'") | .env[] | select(.name == "APP_CONFIG_FILE_B64")' service.deploy.yaml &>/dev/null || { echo "❌ Container '$name' or APP_CONFIG_FILE_B64 not found"; exit 1; } | ||
yq e '.spec.template.spec.containers[] |= select(.name == "'"$name"'").env[] |= select(.name == "APP_CONFIG_FILE_B64").value = "'"$config"'"' -i service.deploy.yaml | ||
echo "✓ Updated config for $name" | ||
;; | ||
esac | ||
done | ||
fi | ||
# Print deployment summary | ||
echo "\n===== DEPLOYMENT SUMMARY =====" | ||
SERVICE_NAME=$(yq -r '.metadata.name' service.deploy.yaml) | ||
echo "Service: $SERVICE_NAME" | ||
echo "Container images:" | ||
yq '.spec.template.spec.containers[] | [.name, .image] | join(": ")' service.deploy.yaml | ||
echo "=============================" | ||
- name: Extract service name from YAML | ||
id: set-service-name | ||
run: | | ||
SERVICE_NAME=$(yq -r '.metadata.name' service.deploy.yaml) | ||
echo "final_service_name=$SERVICE_NAME" >> $GITHUB_OUTPUT | ||
echo "Deploying service: $SERVICE_NAME" | ||
- name: Deploy to Cloud Run | ||
id: deploy | ||
run: | | ||
# Build the base deployment command | ||
DEPLOY_CMD="gcloud run services replace service.deploy.yaml \ | ||
--region=${{ vars.PULUMI_GAR_LOCATION }} \ | ||
--project=${{ vars.PULUMI_GCP_PROJECT_ID }}" | ||
# Add CloudSQL instances if specified | ||
if [ -n "${{ inputs.cloudsql_instances }}" ]; then | ||
DEPLOY_CMD="$DEPLOY_CMD --add-cloudsql-instances=${{ inputs.cloudsql_instances }}" | ||
echo "Adding CloudSQL instances: ${{ inputs.cloudsql_instances }}" | ||
fi | ||
# Add allow unauthenticated if specified | ||
if [ "${{ inputs.allow_unauthenticated }}" = "true" ]; then | ||
DEPLOY_CMD="$DEPLOY_CMD --allow-unauthenticated" | ||
echo "Allowing unauthenticated access" | ||
fi | ||
echo "Executing deployment command: $DEPLOY_CMD" | ||
eval $DEPLOY_CMD | ||
echo "Service deployed successfully!" | ||
- name: Get service URL | ||
id: get-url | ||
run: | | ||
URL=$(gcloud run services describe ${{ steps.set-service-name.outputs.final_service_name }} \ | ||
--platform=managed \ | ||
--region=${{ vars.PULUMI_GAR_LOCATION }} \ | ||
--project=${{ vars.PULUMI_GCP_PROJECT_ID }} \ | ||
--format='value(status.url)') | ||
echo "url=$URL" >> $GITHUB_OUTPUT | ||
echo "Service URL: $URL" |