Skip to content

Latest commit

 

History

History
172 lines (119 loc) · 11.2 KB

File metadata and controls

172 lines (119 loc) · 11.2 KB

cloud-run-example

A GCP test-bed for the Kosli snapshot cloud-run and artifact-attestation features. Exercises every deployment pattern a customer might use — Cloud Run services (image- and source-deployed), Cloud Run Jobs (tag- and digest-pinned, scheduled and idle), 2nd-gen Cloud Functions, and App Engine Standard — so the kosli CLI has variety to test against.

Each deployable unit lives in its own top-level directory; pushes to main trigger per-component Cloud Build deploys.

What this test-bed exercises

What kosli snapshot cloud-run reports today, broken down by GCP deploy method:

Deploy method Container? Reported In this repo Notes
Cloud Run service (image-deployed) Yes ✓ Full hello-world, friend-app
Cloud Run service (source / Buildpacks) Yes (built for you) ✓ Full remote-neighbor
Cloud Run Job Yes ✓ Full sandman-job, ghost-job, kosli-reporter
Cloud Run function (Cloud Functions 2nd gen) Yes (Buildpacks) ✓ Full cousin-fn
Cloud Functions 1st gen No (Google packages the source)
App Engine Standard No (gVisor sandbox, not a container) hello-appengine Not currently supported in the CLI.
App Engine Flexible Yes (containers on managed VMs)
GKE (Standard / Autopilot) Yes Use kosli snapshot k8s instead.
Cloud Run for Anthos Yes (knative on GKE)
Compute Engine + Container-Optimized OS Yes (Docker on a VM)

Components

Directory Resource Deploy pattern Logs Notes
hello-world/ Cloud Run service image-deployed with :$COMMIT_SHA -t hello-world Has provenance. Baseline service. Runs a Kosli attest artifact step before deploy (the cloud-run-hello-world flow).
friend-app/ Cloud Run service image-deployed with :$COMMIT_SHA -t friend-app Has provenance. Second service to exercise multi-service snapshots. Runs kosli attest artifact before deploy (the cloud-run-friend-app flow).
remote-neighbor/ Cloud Run service source-deployed via Buildpacks (gcloud run deploy --source) -t remote-neighbor Has provenance. No Dockerfile in the repo; Cloud Build invokes Buildpacks. Image lands in the auto-created cloud-run-source-deploy AR repo. Runs kosli attest artifact after deploy (the cloud-run-remote-neighbor flow).
cousin-fn/ Cloud Run function (2nd gen) source-deployed via Buildpacks -t cousin-fn Has provenance. Backed by a Cloud Run service; image lives in the Google-managed gcf-artifacts repo. Runs kosli attest artifact after deploy (the cloud-run-cousin-fn flow).
sandman-job/ Cloud Run Job digest-pinned (@sha256:...) -t sandman-job Has provenance. Resolves tag → digest at deploy time in cloudbuild.yaml. Scheduled every 5 min via Cloud Scheduler so it generates execution history. Runs kosli attest artifact before deploy (the cloud-run-sandman-job flow).
ghost-job/ Cloud Run Job tag-pinned (:$COMMIT_SHA) -t ghost-job Has provenance. The tag-pinned counterpart to sandman, deliberately exercising the most common customer pattern. Never invoked. Runs kosli attest artifact before deploy (the cloud-run-ghost-job flow).
kosli-reporter/ Cloud Run Job tracks moving :released tag -t kosli-reporter Runs kosli snapshot cloud-run every 5 min via Cloud Scheduler. See "Kosli integration" below.
appengine/ App Engine Standard service source-deployed -t hello-appengine hello-appengine. Not reported by snapshot cloud-run; in the repo to exercise the build pipeline and the GIT_SHA env-var provenance pattern.
appengine-bootstrap/ App Engine Standard default service one-time stub Required because App Engine refuses to create named services before a default exists. Deploy once manually, then ignore.

Tail logs for any component with ./bin/logs_gcp.sh <Logs> hello-world-cli-demo — append -f to follow, -m <minutes> to widen the time window. ./bin/logs_gcp.sh -h for full usage.

Local development

Run the hello-world containerized app locally (builds the image and starts the container in the background):

make demo

Then open http://localhost:8090.

Other targets:

  • make logs_demo — show container logs.
  • make follow_demo — follow container logs (Ctrl+C to detach).
  • make down — stop the running container.
  • make build — build the Docker image only.
  • make run — start an already-built image (detached).

First-time setup

Prerequisites: gcloud, terraform, docker, make.

  1. Authenticate with Google Cloud:

    gcloud auth login
    gcloud auth application-default login
    gcloud config set project hello-world-cli-demo
  2. Connect GitHub to Cloud Build (one-time, manual — Terraform cannot do this):

    In the GCP Console, go to Cloud Build → Triggers → Manage repositories → Connect repository, choose GitHub (Cloud Build GitHub App), authorize the kosli-dev/cloud-run-example repo, and finish the wizard. Region: europe-west1.

  3. Apply Terraform:

    cd terraform
    terraform init
    terraform apply

    The first apply deploys the Cloud Run services with a public placeholder image (gcr.io/cloudrun/hello). Your real apps appear after the first push to main triggers each component's build.

Deploying changes

Push to main:

git push origin main

Cloud Build picks up the push and runs each component's per-path trigger. Triggers are scoped by included_files:

Trigger Watches Action
deploy-hello-world hello-world/** Build → push → attest to Kosli (before deploy) → deploy Cloud Run service
deploy-friend-app friend-app/** Build → push → attest to Kosli (before deploy) → deploy Cloud Run service
deploy-remote-neighbor remote-neighbor/** gcloud run deploy --source (Buildpacks; no docker build/push step) → tag image with :$COMMIT_SHAattest to Kosli (after deploy)
deploy-cousin-fn cousin-fn/** gcloud functions deploy (re-uploads source) → attest to Kosli (after deploy)
deploy-sandman-job sandman-job/** Build → push → attest to Kosli (before deploy) → resolve digest → deploy Job pinned to digest
deploy-ghost-job ghost-job/** Build → push → attest to Kosli (before deploy) → deploy Job pinned to :$COMMIT_SHA
deploy-kosli-reporter kosli-reporter/** Build → push (:$COMMIT_SHA + :released) → update Job to :released
deploy-hello-appengine appengine/** sed $COMMIT_SHA into app.yamlgcloud app deploy
setup-kosli-flows kosli-flow-templates/** kosli create flow for each *.yml template (idempotent; create or update)
setup-kosli-policies kosli-policies/** kosli attach-policy for each policy listed in kosli-policies/cloudbuild.yaml (policies themselves are org-managed)

Watch builds at https://console.cloud.google.com/cloud-build/builds?project=hello-world-cli-demo.

Kosli integration

Two flows are wired up:

Snapshot side — kosli-reporter Job. Runs kosli snapshot cloud-run cloud-run-example --project=hello-world-cli-demo --region=europe-west1 --resolve-names every 5 min via Cloud Scheduler. Reports running Services / Jobs / 2nd-gen Functions to the cloud-run-example environment in the kosli-public org. Required IAM:

  • roles/run.viewer (project-level)
  • roles/secretmanager.secretAccessor on the kosli-api-token secret
  • roles/artifactregistry.reader on the containers repo AND on the Google-managed gcf-artifacts repo (for 2nd-gen function image lookups)

Build side — per-service flows. Six components run kosli begin trail + kosli attest artifact from their cloudbuild.yaml: hello-world, friend-app, remote-neighbor, sandman-job, ghost-job, and cousin-fn. The CLI is curl-downloaded inside the step (no image dependency). The components attest at different points in the build depending on when the image first exists:

  • Before deployhello-world, friend-app, sandman-job, ghost-job. Images are built and pushed by our own docker steps, so the digest is known up front. Build-success guarantees an attestation regardless of deploy outcome.
  • After deployremote-neighbor, cousin-fn. Source/Buildpacks builds happen inside gcloud run deploy --source / gcloud functions deploy; the image doesn't exist until those commands finish. Trade-off: deploy failure means no attestation. Unavoidable with source-deployed components.

Components without attestation: kosli-reporter (meta-service running the snapshots; also filtered out of its own snapshot output via --exclude=kosli-reporter so it doesn't show up as a non-compliant artifact) and appengine (no image to attest, App Engine isn't reported anyway).

Flow naming convention: cloud-run-<service-name> (so cloud-run-hello-world). Flow templates live in kosli-flow-templates/. A dedicated Cloud Build trigger (setup-kosli-flows) iterates over every *.yml in that directory and runs kosli create flow for each (creating or updating idempotently). It fires automatically on push to main whenever any template changes — so "edit + push" is enough to roll out a new or changed flow. To run it on demand without a push:

gcloud builds triggers run setup-kosli-flows --branch=main --region=global

Environment policies are managed at the org level on Kosli (kosli-public); this repo only attaches them to the cloud-run-example environment. The setup-kosli-policies Cloud Build trigger runs kosli attach-policy <name> --environment cloud-run-example for each policy listed in kosli-policies/cloudbuild.yaml. Currently attached:

  • has-provenance — every running artifact must have provenance (be attested in some flow).
  • trail-compliant — the trail the artifact comes from must itself be compliant.

To run it on demand:

gcloud builds triggers run setup-kosli-policies --branch=main --region=global

Testing an unreleased kosli CLI on the reporter

The kosli-reporter Job tracks the :released tag, which Cloud Build keeps current on every push to main. To swap in a locally-built CLI without committing:

  1. Build a linux/amd64 binary into kosli-reporter/kosli (gitignored):

    cd /path/to/cli && GOOS=linux GOARCH=amd64 CGO_ENABLED=0 go build \
      -o /path/to/cloud-run-example/kosli-reporter/kosli .
  2. make deploy-dev-reporter — builds with kosli-reporter/Dockerfile.dev, pushes a :dev-<timestamp> tag, points the Job at it.

  3. gcloud run jobs execute kosli-reporter --region=europe-west1 --wait to run it.

Swap back to the official CLI with make deploy-released-reporter. Per-commit :$COMMIT_SHA images stay in Artifact Registry for rollback to any prior release.

Get the service URL

cd terraform
terraform output service_url

Cleanup

cd terraform
terraform destroy