deploy,devtools: add files needed to deploy worker

Change-Id: Ife8b67606d3fcab56d9c220807314e6f88abf705
Reviewed-on: https://go-review.googlesource.com/c/pkgsite-metrics/+/466197
Run-TryBot: Jonathan Amsterdam <jba@google.com>
TryBot-Result: Gopher Robot <gobot@golang.org>
Reviewed-by: Julie Qiu <julieqiu@google.com>
diff --git a/deploy/worker.yaml b/deploy/worker.yaml
new file mode 100644
index 0000000..76ebc12
--- /dev/null
+++ b/deploy/worker.yaml
@@ -0,0 +1,108 @@
+# Copyright 2022 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+# This is a Cloud Build config file for the go-ecosystem worker.
+# Invoke locally from the command line using devtools/deploy.sh.
+# It can also be configured to run from a trigger, by supplying the _ENV
+# substitution.
+
+substitutions:
+  _ENV: ''
+  _BQ_DATASET: ''
+
+steps:
+  - id: Lock
+    name: golang:1.19.4
+    entrypoint: bash
+    args:
+      - -ec
+      - |
+        if [[ "$COMMIT_SHA" = '' ]]; then
+          echo "no COMMIT_SHA, not locking"
+          exit 0
+        fi
+        go run golang.org/x/website/cmd/locktrigger@latest \
+          -project $PROJECT_ID -build $BUILD_ID -repo https://go.googlesource.com/pkgsite-metrics
+
+  - id: Test
+    # Run tests. Do this early, to avoid wasting time if they fail.
+    name: golang:1.19.4
+    entrypoint: bash
+    args:
+      - -ec
+      - go test ./...
+
+  - id: Prepare
+    name: gcr.io/cloud-builders/gcloud
+    entrypoint: bash
+    args:
+      - -ec
+      - |
+        # Determine the image name and save for later steps.
+        if [[ "$SHORT_SHA" = '' ]]; then
+          echo >&2 "missing SHORT_SHA; use --substitutions on command line"
+          exit 1
+        fi
+        if [[ "$_ENV" = '' ]]; then
+          echo >&2 "missing _ENV; use --substitutions on command line"
+          exit 1
+        fi
+        if [[ "$_BQ_DATASET" = '' ]]; then
+          echo >&2 "missing _BQ_DATASET; use --substitutions on command line"
+          exit 1
+        fi
+
+        tag=$(date +%Y%m%dt%H%M%S)-$SHORT_SHA
+        image=gcr.io/$PROJECT_ID/${_ENV}-ecosystem-worker:$tag
+        echo "image is $image"
+        echo $image > /workspace/image.txt
+
+        # Convert the commented config.json file to valid json.
+        sed '/^[ \t]*#/d' config.json.commented > /workspace/config.json
+
+        # Download the vuln DB from its bucket to a local directory, and remember
+        # its last-modified time in a file.
+        gsutil -m -q cp -r gs://go-vulndb /workspace
+        gsutil stat gs://go-vulndb/index.json | \
+                awk '$$1 == "Update" { for (i = 4; i <= NF; i++) printf("%s ", $$i); printf("\n"); }' \
+                > /workspace/go-vulndb/LAST_MODIFIED
+        # Download a tarball of a docker Go image.
+        gsutil cp gs://go-ecosystem/go-image.tar.gz /workspace
+
+  - id: Build
+    # Build the docker image.
+    #
+    # The files we put in /workspace in the previous step need to be
+    # in the repo root so they get uploaded to the Docker daemon.
+    # However it turns out that /workspace is in fact the same directory,
+    # so no copying is necessary.
+    name: gcr.io/cloud-builders/docker
+    entrypoint: bash
+    args:
+      - -ec
+      - |
+        image=$(cat /workspace/image.txt)
+        docker build -t $image -f cmd/worker/Dockerfile . \
+          --build-arg DOCKER_IMAGE=$image \
+          --build-arg BQ_DATASET=${_BQ_DATASET}
+        docker push $image
+
+  - id: Deploy
+    name: gcr.io/cloud-builders/gcloud
+    entrypoint: bash
+    args:
+      - -ec
+      - |
+        image=$(cat /workspace/image.txt)
+        service=${_ENV}-ecosystem-worker
+        args="--project $PROJECT_ID --region us-central1"
+        gcloud beta run deploy $args  $service --image $image --execution-environment=gen2
+        # If there was a rollback, `gcloud run deploy` will create a revision but
+        # not point traffic to it. The following command ensures that the new revision
+        # will get traffic.
+        latestTraffic=$(gcloud run services $args describe $service \
+                        --format='value(status.traffic.latestRevision)')
+        if [[ $latestTraffic != True ]]; then
+          gcloud run services $args update-traffic $service --to-latest
+        fi
diff --git a/devtools/deploy.sh b/devtools/deploy.sh
new file mode 100755
index 0000000..8b95673
--- /dev/null
+++ b/devtools/deploy.sh
@@ -0,0 +1,69 @@
+#!/bin/bash
+
+# Copyright 2022 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+# Deploy the go-ecosystem worker to Cloud Run, using Cloud Build.
+
+set -e
+
+source devtools/lib.sh || { echo "Are you at repo root?"; exit 1; }
+
+usage() {
+  die "usage: $0 [-n] (dev | prod) BIGQUERY_DATASET"
+}
+
+# Report whether the current repo's workspace has no uncommitted files.
+clean_workspace() {
+  [[ $(git status --porcelain) == '' ]]
+}
+
+main() {
+  local prefix=
+  if [[ $1 = '-n' ]]; then
+    prefix='echo dryrun: '
+    shift
+  fi
+
+  local env=$1
+
+  case $env in
+    dev|prod);;
+    *) usage;;
+  esac
+
+  local dataset=$2
+  if [[ $dataset = '' ]]; then
+    usage
+  fi
+
+  if which grants > /dev/null; then
+    local allowed=false
+    while read g _ ok _; do
+      if [[ $ok = OK ]]; then
+        allowed=true
+      fi
+    done < <(grants check $GO_ECOSYSTEM_DEPLOY_GROUPS)
+    if ! $allowed; then
+      die "You need a grant for one of: $GO_ECOSYSTEM_DEPLOY_GROUPS"
+    fi
+  fi
+
+  local project=$(tfvar ${env}_project)
+  if [[ $project = '' ]]; then
+    die "no ${env}_project in terraform.tfvars"
+  fi
+  local commit=$(git rev-parse --short HEAD)
+  local unclean
+  if ! clean_workspace; then
+    unclean="-unclean"
+  fi
+
+  $prefix gcloud builds submit \
+    --project $project \
+    --config deploy/worker.yaml \
+    --substitutions SHORT_SHA=${commit}${unclean},_ENV=$env,_BQ_DATASET=$dataset
+}
+
+main $@
diff --git a/devtools/lib.sh b/devtools/lib.sh
index 09e0e64..ff2f3ac 100644
--- a/devtools/lib.sh
+++ b/devtools/lib.sh
@@ -1,4 +1,4 @@
-# Copyright 2023 The Go Authors. All rights reserved.
+# Copyright 2022 The Go Authors. All rights reserved.
 # Use of this source code is governed by a BSD-style
 # license that can be found in the LICENSE file.
 
@@ -47,25 +47,21 @@
   $@ || err "command failed"
 }
 
-# tfvar NAME returns the value of NAME in the terraform.tfvars file.
+# tfvar NAME returns the value of the terraform variable NAME.
 tfvar() {
-  local name=$1
-  awk '$1 == "'$name'" { print substr($3, 2, length($3)-2) }' terraform/terraform.tfvars
+  local v=TF_VAR_$1
+  echo ${!v}
 }
 
 worker_url() {
   local env=$1
-  case $env in
-    prod) echo https://prod-metrics-worker-7x5g2mdvca-uc.a.run.app;;
-    dev) echo https://dev-metrics-worker-7x5g2mdvca-uc.a.run.app;;
-    *) die "usage: $0 (dev | prod)";;
-  esac
+  echo https://${env}-${GO_ECOSYSTEM_WORKER_URL_SUFFIX}
 }
 
 impersonation_service_account() {
   local env=$1
   case $env in
-    prod|dev) echo impersonate@go-ecosystem.iam.gserviceaccount.com;;
+    prod|dev) echo impersonate@$(tfvar ${env}_project).iam.gserviceaccount.com;;
     *) die "usage: $0 (dev | prod)";;
   esac
 }