GCP for DevOps — Eventarc & Workflows

3 minute read

Eventarc & Workflows

Eventarc provides a unified eventing layer for routing events from Google Cloud services, SaaS, and custom sources to event-driven targets. Workflows orchestrates multi-step serverless workflows with conditional logic and error handling.

Event-Driven Architecture

GCP Event-Driven Architecture:
┌──────────────┐    ┌──────────┐    ┌──────────────────┐
│ Cloud Storage │───▶│          │───▶│ Cloud Functions   │
│ (event)       │    │          │    └──────────────────┘
├──────────────┤    │          │    ┌──────────────────┐
│ Pub/Sub       │───▶│ Eventarc │───▶│ Cloud Run         │
│ (message)     │    │          │    └──────────────────┘
├──────────────┤    │          │    ┌──────────────────┐
│ Audit Logs    │───▶│          │───▶│ Workflows          │
│ (event)       │    │          │    └──────────────────┘
├──────────────┤    │          │    ┌──────────────────┐
│ Custom App    │───▶│          │───▶│ GKE Service        │
│ (CloudEvent)  │    └──────────┘    └──────────────────┘
└──────────────┘

Eventarc

Create an Eventarc Trigger

# Trigger Cloud Run on Cloud Storage events
$ gcloud eventarc triggers create storage-trigger \
    --location=us-central1 \
    --destination-run-service=my-processor \
    --destination-run-region=us-central1 \
    --event-filters="type=google.cloud.storage.object.v1.finalized" \
    --event-filters="bucket=my-data-bucket" \
    --service-account=my-sa@my-project.iam.gserviceaccount.com

# Trigger on Audit Log events
$ gcloud eventarc triggers create audit-trigger \
    --location=us-central1 \
    --destination-run-service=my-audit-handler \
    --destination-run-region=us-central1 \
    --event-filters="type=google.cloud.audit.log.v1.written" \
    --event-filters="serviceName=storage.googleapis.com" \
    --event-filters="methodName=storage.objects.create" \
    --service-account=my-sa@my-project.iam.gserviceaccount.com

# List triggers
$ gcloud eventarc triggers list --location=us-central1

# Describe a trigger
$ gcloud eventarc triggers describe storage-trigger \
    --location=us-central1

CloudEvent Format

Eventarc delivers events in CloudEvent format:

{
  "specversion": "1.0",
  "type": "google.cloud.storage.object.v1.finalized",
  "source": "//storage.googleapis.com/projects/_/buckets/my-bucket",
  "id": "1234567890",
  "time": "2026-03-30T07:00:00.000Z",
  "datacontenttype": "application/json",
  "data": {
    "name": "uploads/report.csv",
    "bucket": "my-data-bucket",
    "contentType": "text/csv",
    "size": "2048"
  }
}

Handle Events in Cloud Run

from flask import Flask, request
import json

app = Flask(__name__)

@app.route("/", methods=["POST"])
def handle_event():
    event = request.get_json()
    event_type = request.headers.get("Ce-Type")
    event_source = request.headers.get("Ce-Source")

    print(f"Event type: {event_type}")
    print(f"Event source: {event_source}")
    print(f"Event data: {json.dumps(event)}")

    # Process the event
    if event_type == "google.cloud.storage.object.v1.finalized":
        bucket = event["bucket"]
        name = event["name"]
        print(f"New file: gs://{bucket}/{name}")

    return "OK", 200

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=8080)

Workflows

Workflow YAML Syntax

main:
  params: [input]
  steps:
    - init:
        assign:
          - project_id: ${sys.get_env("GOOGLE_CLOUD_PROJECT_ID")}
          - bucket: ${input.bucket}
          - filename: ${input.filename}

    - validate_input:
        switch:
          - condition: ${filename == ""}
            raise: "Filename cannot be empty"

    - process_file:
        call: http.get
        args:
          url: ${"https://storage.googleapis.com/storage/v1/b/" + bucket + "/o/" + filename}
          auth:
            type: OAuth2
        result: file_metadata

    - check_size:
        switch:
          - condition: ${int(file_metadata.body.size) > 10485760}
            next: large_file_handler
          - condition: true
            next: small_file_handler

    - large_file_handler:
        call: http.post
        args:
          url: https://my-processor-abc123.a.run.app/process-large
          body:
            bucket: ${bucket}
            filename: ${filename}
          auth:
            type: OIDC
        result: processing_result
        next: return_result

    - small_file_handler:
        call: http.post
        args:
          url: https://my-processor-abc123.a.run.app/process-small
          body:
            bucket: ${bucket}
            filename: ${filename}
          auth:
            type: OIDC
        result: processing_result

    - return_result:
        return: ${processing_result.body}

Deploy and Execute Workflows

# Deploy a workflow
$ gcloud workflows deploy file-processor \
    --location=us-central1 \
    --source=workflow.yaml \
    --service-account=workflow-sa@my-project.iam.gserviceaccount.com

# Execute a workflow
$ gcloud workflows execute file-processor \
    --location=us-central1 \
    --data='{"bucket":"my-bucket","filename":"report.csv"}'

# List workflow executions
$ gcloud workflows executions list file-processor \
    --location=us-central1

# Get execution details
$ gcloud workflows executions describe EXECUTION_ID \
    --workflow=file-processor \
    --location=us-central1

Error Handling in Workflows

main:
  steps:
    - try_step:
        try:
          call: http.post
          args:
            url: https://my-api.example.com/process
            body:
              data: "test"
          result: api_response
        retry:
          predicate: ${default_retry_predicate}
          max_retries: 3
          backoff:
            initial_delay: 1
            max_delay: 60
            multiplier: 2
        except:
          as: e
          steps:
            - log_error:
                call: sys.log
                args:
                  text: ${"Error: " + e.message}
                  severity: "ERROR"
            - return_error:
                return:
                  status: "error"
                  message: ${e.message}

Connect Eventarc to Workflows

# Trigger workflow on Cloud Storage events
$ gcloud eventarc triggers create storage-to-workflow \
    --location=us-central1 \
    --destination-workflow=file-processor \
    --destination-workflow-location=us-central1 \
    --event-filters="type=google.cloud.storage.object.v1.finalized" \
    --event-filters="bucket=my-data-bucket" \
    --service-account=my-sa@my-project.iam.gserviceaccount.com

Common Mistakes

Mistake Why It’s Wrong Fix
No retry logic in Workflows Transient failures cause job failure Use retry with backoff
Missing service account Trigger has no permission Always specify --service-account
Ignoring event ordering Events may arrive out of order Design for idempotency
No dead-letter topic Lost events on failure Configure DLT for Pub/Sub triggers
Hardcoded URLs Breaks across environments Use project variables

Quick Reference

Command Description
gcloud eventarc triggers create Create trigger
gcloud eventarc triggers list List triggers
gcloud eventarc triggers delete Delete trigger
gcloud workflows deploy Deploy workflow
gcloud workflows execute Execute workflow
gcloud workflows executions list List executions

Next → Practice Exercises

Updated: