all: use Workload Identity for gitmirror, move to "prod" namespace

Rather than using a service account key, enable Workload Identity for
gitmirror. This involved moving into a non-default namespace, so the
coordinator's monitoring logic needs updating too.

Change-Id: I196fae0825f806248571aae6fb125d8c0917934d
Reviewed-on: https://go-review.googlesource.com/c/build/+/347410
Trust: Heschi Kreinick <heschi@google.com>
Run-TryBot: Heschi Kreinick <heschi@google.com>
TryBot-Result: Go Bot <gobot@golang.org>
Reviewed-by: Alexander Rakoczy <alex@golang.org>
Reviewed-by: Carlos Amedee <carlos@golang.org>
diff --git a/buildenv/envs.go b/buildenv/envs.go
index 7e586ad..f737feb 100644
--- a/buildenv/envs.go
+++ b/buildenv/envs.go
@@ -44,6 +44,9 @@
 
 	// Name is the name of the Kubernetes cluster that will be created.
 	Name string
+
+	// Namespace is the Kubernetes namespace to use within the cluster.
+	Namespace string
 }
 
 // Environment describes the configuration of the infrastructure for a
@@ -261,6 +264,7 @@
 		MaxNodes:    3,
 		Name:        "go",
 		MachineType: "n1-standard-4",
+		Namespace:   "default",
 	},
 	DashURL:           "https://build-staging.golang.org/",
 	PerfDataURL:       "https://perfdata.golang.org",
@@ -296,6 +300,7 @@
 		MaxNodes:    4,
 		Name:        "go",
 		MachineType: "n1-standard-4",
+		Namespace:   "prod",
 	},
 	DashURL:             "https://build.golang.org/",
 	PerfDataURL:         "https://perfdata.golang.org",
diff --git a/cmd/gitmirror/deployment-mirroring.yaml b/cmd/gitmirror/deployment-mirroring.yaml
index 192e8da..131092c 100644
--- a/cmd/gitmirror/deployment-mirroring.yaml
+++ b/cmd/gitmirror/deployment-mirroring.yaml
@@ -1,6 +1,7 @@
 apiVersion: apps/v1
 kind: Deployment
 metadata:
+  namespace: prod
   name: gitmirror-mirroring-deployment
   labels:
     app: gitmirror-mirroring
@@ -14,6 +15,9 @@
       labels:
         app: gitmirror-mirroring
     spec:
+      serviceAccountName: gitmirror
+      nodeSelector:
+        cloud.google.com/gke-nodepool: workload-identity-pool
       volumes:
       - name: cache-volume
         emptyDir:
diff --git a/cmd/gitmirror/deployment-serving.yaml b/cmd/gitmirror/deployment-serving.yaml
index 0fe6512..27831b9 100644
--- a/cmd/gitmirror/deployment-serving.yaml
+++ b/cmd/gitmirror/deployment-serving.yaml
@@ -1,6 +1,7 @@
 apiVersion: apps/v1
 kind: Deployment
 metadata:
+  namespace: prod
   name: gitmirror-serving-deployment
   labels:
     app: gitmirror-serving
@@ -17,6 +18,9 @@
       labels:
         app: gitmirror-serving
     spec:
+      serviceAccountName: gitmirror
+      nodeSelector:
+        cloud.google.com/gke-nodepool: workload-identity-pool
       volumes:
       - name: cache-volume
         emptyDir:
diff --git a/cmd/gitmirror/gitmirror.go b/cmd/gitmirror/gitmirror.go
index f664aab..84ea914 100644
--- a/cmd/gitmirror/gitmirror.go
+++ b/cmd/gitmirror/gitmirror.go
@@ -143,21 +143,8 @@
 		fmt.Fprintf(sshConfig, "Host github.com\n  IdentityFile %v\n", privKeyPath)
 	}
 
-	// gitmirror service key, used to gcloud auth for CSR writes.
+	// The gitmirror service account should already be available via GKE workload identity.
 	if *flagMirrorCSR {
-		serviceKey, err := retrieveSecret(ctx, secret.NameGitMirrorServiceKey)
-		if err != nil {
-			return fmt.Errorf("reading service key from secret manager: %v", err)
-		}
-		serviceKeyPath := filepath.Join(home, secret.NameGitMirrorServiceKey)
-		if err := ioutil.WriteFile(serviceKeyPath, []byte(serviceKey), 0600); err != nil {
-			return err
-		}
-		gcloud := exec.CommandContext(ctx, "gcloud", "auth", "activate-service-account", "--key-file", serviceKeyPath)
-		gcloud.Env = append(os.Environ(), "HOME="+home)
-		if out, err := gcloud.CombinedOutput(); err != nil {
-			return fmt.Errorf("gcloud auth failed: %v\n%v", err, out)
-		}
 		fmt.Fprintf(gitConfig, "[credential \"https://source.developers.google.com\"]\n  helper=gcloud.sh\n")
 	}
 
diff --git a/cmd/gitmirror/service.yaml b/cmd/gitmirror/service.yaml
index dce13f4..2501098 100644
--- a/cmd/gitmirror/service.yaml
+++ b/cmd/gitmirror/service.yaml
@@ -2,6 +2,7 @@
 kind: Service
 metadata:
   name: gitmirror
+  namespace: prod
 spec:
   ports:
     - port: 8585
diff --git a/internal/coordinator/pool/kube.go b/internal/coordinator/pool/kube.go
index f73201f..32aaf5c 100644
--- a/internal/coordinator/pool/kube.go
+++ b/internal/coordinator/pool/kube.go
@@ -83,6 +83,7 @@
 
 	goKubeClient, err = gke.NewClient(ctx,
 		gceBuildEnv.KubeTools.Name,
+		gke.OptNamespace(gceBuildEnv.KubeTools.Namespace),
 		gke.OptZone(gceBuildEnv.ControlZone),
 		gke.OptProject(gceBuildEnv.ProjectName),
 		gke.OptTokenSource(gce.GCPCredentials().TokenSource))
diff --git a/internal/secret/gcp_secret_manager.go b/internal/secret/gcp_secret_manager.go
index 4b6dc57..f2f2e55 100644
--- a/internal/secret/gcp_secret_manager.go
+++ b/internal/secret/gcp_secret_manager.go
@@ -34,9 +34,6 @@
 	// NameGitHubSSHKey is the secret name for the GitHub SSH private key.
 	NameGitHubSSHKey = "github-ssh-private-key"
 
-	// NameGitMirrorServiceKey is the secret name for the gitmirror service account key.
-	NameGitMirrorServiceKey = "gitmirror-service-key"
-
 	// NameGobotPassword is the secret name for the Gobot password.
 	NameGobotPassword = "gobot-password"
 
diff --git a/kubernetes/client.go b/kubernetes/client.go
index d6a6185..d52e4c6 100644
--- a/kubernetes/client.go
+++ b/kubernetes/client.go
@@ -39,7 +39,10 @@
 // The provided host is an url (scheme://hostname[:port]) of a
 // Kubernetes master without any path.
 // The provided client is an authorized http.Client used to perform requests to the Kubernetes API master.
-func NewClient(baseURL string, client *http.Client) (*Client, error) {
+func NewClient(baseURL, namespace string, client *http.Client) (*Client, error) {
+	if namespace == "" {
+		return nil, fmt.Errorf("must specify Kubernetes namespace")
+	}
 	validURL, err := url.Parse(baseURL)
 	if err != nil {
 		return nil, fmt.Errorf("failed to parse URL %q: %v", baseURL, err)
@@ -47,7 +50,7 @@
 	return &Client{
 		endpointURL: strings.TrimSuffix(validURL.String(), "/") + "/api/v1",
 		httpClient:  client,
-		namespace:   "default",
+		namespace:   namespace,
 	}, nil
 }
 
diff --git a/kubernetes/client_test.go b/kubernetes/client_test.go
index debf7f5..3deabdc 100644
--- a/kubernetes/client_test.go
+++ b/kubernetes/client_test.go
@@ -74,7 +74,7 @@
 	s := httptest.NewServer(&hs)
 	defer s.Close()
 
-	c, err := kubernetes.NewClient(s.URL, http.DefaultClient)
+	c, err := kubernetes.NewClient(s.URL, "default", http.DefaultClient)
 	if err != nil {
 		t.Fatalf("NewClient: %v", err)
 	}
diff --git a/kubernetes/gke/gke.go b/kubernetes/gke/gke.go
index 0811b53..a7ab1ac 100644
--- a/kubernetes/gke/gke.go
+++ b/kubernetes/gke/gke.go
@@ -35,6 +35,7 @@
 	Project     string
 	TokenSource oauth2.TokenSource
 	Zone        string
+	Namespace   string
 }
 
 type clientOptFunc func(*clientOpt)
@@ -69,9 +70,16 @@
 	})
 }
 
+// OptNamespace sets the Kubernetes namespace to look in.
+func OptNamespace(namespace string) ClientOpt {
+	return clientOptFunc(func(o *clientOpt) {
+		o.Namespace = namespace
+	})
+}
+
 // NewClient returns an Kubernetes client to a GKE cluster.
 func NewClient(ctx context.Context, clusterName string, opts ...ClientOpt) (*kubernetes.Client, error) {
-	var opt clientOpt
+	opt := clientOpt{Namespace: "default"}
 	for _, o := range opts {
 		o.modify(&opt)
 	}
@@ -166,7 +174,7 @@
 		},
 	}
 
-	kubeClient, err := kubernetes.NewClient("https://"+cluster.Endpoint, kubeHTTPClient)
+	kubeClient, err := kubernetes.NewClient("https://"+cluster.Endpoint, opt.Namespace, kubeHTTPClient)
 	if err != nil {
 		return nil, fmt.Errorf("kubernetes HTTP client could not be created: %v", err)
 	}