Skip to content

Commit 230a785

Browse files
heschigopherbot
authored andcommitted
cmd/relui: add Cloud Build triggering support
Build the Google-facing BoringCrypto image as part of publishing the release. I found myself a bit stuck with modeling the step. Currently it's split in two, which means it's unlikely to trigger two builds but we don't have any way to retry a build if it flakes. Merging the creation and the await lets us retry, but creates a risk of publishing the Docker image twice. Splitting the publishing step out would fix that, but I didn't want to have to write more code to do the publishing. I think we'll have to see how it goes. Change-Id: I9ad4e79d05611ea744191a4d9d4a6c8df38c523a Reviewed-on: https://go-review.googlesource.com/c/build/+/504819 Auto-Submit: Heschi Kreinick <[email protected]> Reviewed-by: Carlos Amedee <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]> Run-TryBot: Heschi Kreinick <[email protected]> TryBot-Result: Gopher Robot <[email protected]>
1 parent 388349a commit 230a785

File tree

7 files changed

+150
-22
lines changed

7 files changed

+150
-22
lines changed

cmd/relui/main.go

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"strings"
2424
"time"
2525

26+
cloudbuild "cloud.google.com/go/cloudbuild/apiv1/v2"
2627
"cloud.google.com/go/compute/metadata"
2728
"cloud.google.com/go/storage"
2829
"github.com/google/go-github/github"
@@ -142,6 +143,10 @@ func main() {
142143
if err != nil {
143144
log.Fatalf("Could not connect to GCS: %v", err)
144145
}
146+
cloudbuildClient, err := cloudbuild.NewClient(ctx)
147+
if err != nil {
148+
log.Fatalf("Could not connect to Cloud Build: %v", err)
149+
}
145150
var dbPool db.PGDBTX
146151
dbPool, err = pgxpool.Connect(ctx, *pgConnect)
147152
if err != nil {
@@ -168,17 +173,20 @@ func main() {
168173
signServer := sign.NewServer()
169174
protos.RegisterReleaseServiceServer(grpcServer, signServer)
170175
buildTasks := &relui.BuildReleaseTasks{
171-
GerritClient: gerritClient,
172-
GerritHTTPClient: oauth2.NewClient(ctx, creds.TokenSource),
173-
GerritURL: "https://go.googlesource.com/go",
174-
PrivateGerritURL: "https://team.googlesource.com/golang/go-private",
175-
CreateBuildlet: coordinator.CreateBuildlet,
176-
SignService: signServer,
177-
GCSClient: gcsClient,
178-
ScratchURL: *scratchFilesBase,
179-
ServingURL: *servingFilesBase,
180-
DownloadURL: *edgeCacheURL,
181-
ProxyPrefix: "https://proxy.golang.org/golang.org/toolchain/@v",
176+
GerritClient: gerritClient,
177+
GerritHTTPClient: oauth2.NewClient(ctx, creds.TokenSource),
178+
GerritURL: "https://go.googlesource.com/go",
179+
PrivateGerritURL: "https://team.googlesource.com/golang/go-private",
180+
CreateBuildlet: coordinator.CreateBuildlet,
181+
SignService: signServer,
182+
GCSClient: gcsClient,
183+
ScratchURL: *scratchFilesBase,
184+
ServingURL: *servingFilesBase,
185+
DownloadURL: *edgeCacheURL,
186+
ProxyPrefix: "https://proxy.golang.org/golang.org/toolchain/@v",
187+
CloudBuildClient: &task.RealCloudBuildClient{Client: cloudbuildClient},
188+
BoringBuildProject: "symbolic-datum-552",
189+
BoringBuildTrigger: "golang-publish-internal-boringcrypto",
182190
PublishFile: func(f task.WebsiteFile) error {
183191
return publishFile(*websiteUploadURL, userPassAuth, f)
184192
},

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ go 1.18
44

55
require (
66
cloud.google.com/go/bigquery v1.50.0
7+
cloud.google.com/go/cloudbuild v1.9.0
78
cloud.google.com/go/compute/metadata v0.2.3
89
cloud.google.com/go/datastore v1.11.0
910
cloud.google.com/go/errorreporting v0.3.0

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,8 @@ cloud.google.com/go/bigquery v1.7.0/go.mod h1://okPTzCYNXSlb24MZs83e2Do+h+VXtc4g
3434
cloud.google.com/go/bigquery v1.8.0/go.mod h1:J5hqkt3O0uAFnINi6JXValWIb1v0goeZM77hZzJN/fQ=
3535
cloud.google.com/go/bigquery v1.50.0 h1:RscMV6LbnAmhAzD893Lv9nXXy2WCaJmbxYPWDLbGqNQ=
3636
cloud.google.com/go/bigquery v1.50.0/go.mod h1:YrleYEh2pSEbgTBZYMJ5SuSr0ML3ypjRB1zgf7pvQLU=
37+
cloud.google.com/go/cloudbuild v1.9.0 h1:GHQCjV4WlPPVU/j3Rlpc8vNIDwThhd1U9qSY/NPZdko=
38+
cloud.google.com/go/cloudbuild v1.9.0/go.mod h1:qK1d7s4QlO0VwfYn5YuClDGg2hfmLZEb4wQGAbIgL1s=
3739
cloud.google.com/go/compute v1.19.3 h1:DcTwsFgGev/wV5+q8o2fzgcHOaac+DKGC91ZlvpsQds=
3840
cloud.google.com/go/compute v1.19.3/go.mod h1:qxvISKp/gYnXkSAD1ppcSOveRAmzxicEv/JlizULFrI=
3941
cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY=

internal/relui/buildrelease_test.go

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -211,18 +211,26 @@ esac
211211
},
212212
}
213213

214+
const boringProject, boringTrigger = "boring-build-project", "boring-build-trigger"
215+
214216
buildTasks := &BuildReleaseTasks{
215-
GerritClient: gerrit,
216-
GerritHTTPClient: http.DefaultClient,
217-
GerritURL: fakeGerrit.GerritURL() + "/go",
218-
GCSClient: nil,
219-
ScratchURL: "file://" + filepath.ToSlash(t.TempDir()),
220-
ServingURL: "file://" + filepath.ToSlash(servingDir),
221-
CreateBuildlet: fakeBuildlets.CreateBuildlet,
222-
SignService: task.NewFakeSignService(t),
223-
DownloadURL: dlServer.URL,
224-
ProxyPrefix: dlServer.URL,
225-
PublishFile: publishFile,
217+
GerritClient: gerrit,
218+
GerritHTTPClient: http.DefaultClient,
219+
GerritURL: fakeGerrit.GerritURL() + "/go",
220+
GCSClient: nil,
221+
ScratchURL: "file://" + filepath.ToSlash(t.TempDir()),
222+
ServingURL: "file://" + filepath.ToSlash(servingDir),
223+
CreateBuildlet: fakeBuildlets.CreateBuildlet,
224+
SignService: task.NewFakeSignService(t),
225+
DownloadURL: dlServer.URL,
226+
ProxyPrefix: dlServer.URL,
227+
PublishFile: publishFile,
228+
BoringBuildProject: boringProject,
229+
BoringBuildTrigger: boringTrigger,
230+
CloudBuildClient: &task.FakeCloudBuild{
231+
Project: boringProject,
232+
AllowedBuilds: map[string]map[string]string{boringTrigger: {"_GO_VERSION": wantVersion}},
233+
},
226234
ApproveAction: func(ctx *workflow.TaskContext) error {
227235
if strings.Contains(ctx.TaskName, "Release Coordinator Approval") {
228236
return nil

internal/relui/workflows.go

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,11 @@ func addSingleReleaseWorkflow(
431431
goimportsCommit := wf.Task2(wd, "Wait for goimports CL submission", version.AwaitCL, goimportsCL, wf.Const(""))
432432
wf.Output(wd, "goimports CL submitted", goimportsCommit)
433433
}
434+
435+
boringBuild := wf.Task1(wd, "Start boringcrypto build", build.runBoringCryptoBuild, nextVersion, wf.After(uploaded))
436+
boringResult := wf.Task1(wd, "Await boringcrypto build", build.awaitCloudBuild, boringBuild)
437+
wf.Output(wd, "BoringCrypto Docker image status", boringResult)
438+
434439
wf.Output(wd, "Published to website", published)
435440
return published
436441
}
@@ -569,6 +574,9 @@ type BuildReleaseTasks struct {
569574
PublishFile func(task.WebsiteFile) error
570575
CreateBuildlet func(context.Context, string) (buildlet.RemoteClient, error)
571576
SignService sign.Service
577+
BoringBuildProject string
578+
BoringBuildTrigger string
579+
CloudBuildClient task.CloudBuildClient
572580
ApproveAction func(*wf.TaskContext) error
573581
}
574582

@@ -1373,6 +1381,17 @@ func (tasks *BuildReleaseTasks) publishArtifacts(ctx *wf.TaskContext, version st
13731381
return task.Published{Version: version, Files: files}, nil
13741382
}
13751383

1384+
func (b *BuildReleaseTasks) runBoringCryptoBuild(ctx context.Context, version string) (string, error) {
1385+
return b.CloudBuildClient.RunBuildTrigger(ctx, b.BoringBuildProject, b.BoringBuildTrigger, map[string]string{"_GO_VERSION": version})
1386+
}
1387+
1388+
func (b *BuildReleaseTasks) awaitCloudBuild(ctx *wf.TaskContext, id string) (string, error) {
1389+
detail, err := task.AwaitCondition(ctx, 30*time.Second, func() (string, bool, error) {
1390+
return b.CloudBuildClient.Completed(ctx, b.BoringBuildProject, id)
1391+
})
1392+
return detail, err
1393+
}
1394+
13761395
// cutPrefix returns s without the provided leading prefix string
13771396
// and reports whether it found the prefix.
13781397
// If s doesn't start with prefix, cutPrefix returns s, false.

internal/task/cloudbuild.go

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
// Copyright 2023 The Go Authors. All rights reserved.
2+
// Use of this source code is governed by a BSD-style
3+
// license that can be found in the LICENSE file.
4+
5+
package task
6+
7+
import (
8+
"context"
9+
"fmt"
10+
11+
cloudbuild "cloud.google.com/go/cloudbuild/apiv1/v2"
12+
"cloud.google.com/go/cloudbuild/apiv1/v2/cloudbuildpb"
13+
)
14+
15+
type CloudBuildClient interface {
16+
// RunBuildTrigger runs an existing trigger in project with the given substitutions.
17+
RunBuildTrigger(ctx context.Context, project, trigger string, substitutions map[string]string) (buildID string, _ error)
18+
// Completed reports whether a build has finished, returning an error if it's failed.
19+
// It's suitable for use with AwaitCondition.
20+
Completed(ctx context.Context, project, buildID string) (detail string, completed bool, _ error)
21+
}
22+
23+
type RealCloudBuildClient struct {
24+
Client *cloudbuild.Client
25+
}
26+
27+
func (c *RealCloudBuildClient) RunBuildTrigger(ctx context.Context, project, trigger string, substitutions map[string]string) (string, error) {
28+
op, err := c.Client.RunBuildTrigger(ctx, &cloudbuildpb.RunBuildTriggerRequest{
29+
ProjectId: project,
30+
TriggerId: trigger,
31+
Source: &cloudbuildpb.RepoSource{
32+
Substitutions: substitutions,
33+
},
34+
})
35+
if err != nil {
36+
return "", err
37+
}
38+
if _, err = op.Poll(ctx); err != nil {
39+
return "", err
40+
}
41+
meta, err := op.Metadata()
42+
if err != nil {
43+
return "", err
44+
}
45+
return meta.Build.Id, nil
46+
}
47+
48+
func (c *RealCloudBuildClient) Completed(ctx context.Context, project, buildID string) (string, bool, error) {
49+
build, err := c.Client.GetBuild(ctx, &cloudbuildpb.GetBuildRequest{
50+
ProjectId: project,
51+
Id: buildID,
52+
})
53+
if err != nil {
54+
return "", false, err
55+
}
56+
if build.FinishTime == nil {
57+
return "", false, nil
58+
}
59+
if build.Status != cloudbuildpb.Build_SUCCESS {
60+
return "", false, fmt.Errorf("build %q failed: %v", buildID, build.FailureInfo)
61+
}
62+
return build.StatusDetail, true, nil
63+
}

internal/task/fakes.go

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import (
1919
"os"
2020
"os/exec"
2121
"path/filepath"
22+
"reflect"
2223
"strings"
2324
"sync"
2425
"testing"
@@ -647,3 +648,29 @@ func fakeGPGFile(f string) string {
647648
}
648649
return f + ".asc"
649650
}
651+
652+
var _ CloudBuildClient = (*FakeCloudBuild)(nil)
653+
654+
type FakeCloudBuild struct {
655+
Project string
656+
AllowedBuilds map[string]map[string]string
657+
}
658+
659+
const fakeBuildID = "build-12345"
660+
661+
func (cb *FakeCloudBuild) RunBuildTrigger(ctx context.Context, project string, trigger string, substitutions map[string]string) (string, error) {
662+
if project != cb.Project {
663+
return "", fmt.Errorf("unexpected project %v, want %v", project, cb.Project)
664+
}
665+
if allowedSubs, ok := cb.AllowedBuilds[trigger]; !ok || !reflect.DeepEqual(allowedSubs, substitutions) {
666+
return "", fmt.Errorf("unexpected trigger %v: got params %#v, want %#v", trigger, substitutions, allowedSubs)
667+
}
668+
return fakeBuildID, nil
669+
}
670+
671+
func (cb *FakeCloudBuild) Completed(ctx context.Context, project string, id string) (string, bool, error) {
672+
if project != cb.Project || id != fakeBuildID {
673+
return "", false, fmt.Errorf("unexpected build project/id: got %v %v, want %v %v", project, id, cb.Project, fakeBuildID)
674+
}
675+
return "here's some build detail", true, nil
676+
}

0 commit comments

Comments
 (0)