Skip to content

Commit 35d2760

Browse files
authored
Merge pull request GoogleCloudPlatform#35 from GoogleCloudPlatform/release
Release local builder
2 parents 40d5094 + 759d95d commit 35d2760

File tree

7 files changed

+265
-69
lines changed

7 files changed

+265
-69
lines changed

build/build.go

Lines changed: 19 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,7 @@ type Build struct {
7575
Mu sync.Mutex
7676
Request cb.Build
7777
HasMultipleSteps bool
78-
Tokensource oauth2.TokenSource
78+
TokenSource oauth2.TokenSource
7979
Log *buildlog.BuildLog
8080
Status BuildStatus
8181
imageDigests map[string]string // docker image tag to digest (for built images)
@@ -103,6 +103,7 @@ type Build struct {
103103
hostWorkspaceDir string
104104
local bool
105105
push bool
106+
dryrun bool
106107

107108
// For UpdateDockerAccessToken, previous GCR auth value to replace.
108109
prevGCRAuth string
@@ -114,11 +115,11 @@ type kms interface {
114115

115116
// New constructs a new Build.
116117
func New(r runner.Runner, rq cb.Build, ts oauth2.TokenSource,
117-
bl *buildlog.BuildLog, hostWorkspaceDir string, local, push bool) *Build {
118+
bl *buildlog.BuildLog, hostWorkspaceDir string, local, push, dryrun bool) *Build {
118119
return &Build{
119120
Runner: r,
120121
Request: rq,
121-
Tokensource: ts,
122+
TokenSource: ts,
122123
imageDigests: map[string]string{},
123124
stepDigests: make([]string, len(rq.Steps)),
124125
Log: bl,
@@ -129,6 +130,7 @@ func New(r runner.Runner, rq cb.Build, ts oauth2.TokenSource,
129130
hostWorkspaceDir: hostWorkspaceDir,
130131
local: local,
131132
push: push,
133+
dryrun: dryrun,
132134
}
133135
}
134136

@@ -641,12 +643,17 @@ func (b *Build) getKMSClient() (kms, error) {
641643
return b.kms, nil
642644
}
643645

646+
if b.dryrun {
647+
b.kms = dryRunKMS{}
648+
return b.kms, nil
649+
}
650+
644651

645652
// automatically gets (and refreshes) credentials from the metadata server
646653
// when spoofing metadata works by IP. Until then, we'll just fetch the token
647654
// and pass it to all HTTP requests.
648655
svc, err := cloudkms.New(&http.Client{
649-
Transport: &tokenTransport{b.Tokensource},
656+
Transport: &tokenTransport{b.TokenSource},
650657
})
651658
if err != nil {
652659
return nil, err
@@ -655,6 +662,14 @@ func (b *Build) getKMSClient() (kms, error) {
655662
return b.kms, nil
656663
}
657664

665+
// dryRunKMS always returns a base64-encoded placeholder string instead of real
666+
// decrypted value, for use in dryrun mode.
667+
type dryRunKMS struct{}
668+
669+
func (dryRunKMS) Decrypt(string, string) (string, error) {
670+
return base64.StdEncoding.EncodeToString([]byte("<REDACTED>")), nil
671+
}
672+
658673
type realKMS struct {
659674
svc *cloudkms.Service
660675
}

build/build_test.go

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -399,7 +399,7 @@ func TestFetchBuilder(t *testing.T) {
399399
}}
400400
for _, tc := range testCases {
401401
r := newMockRunner(t, tc.name)
402-
b := New(r, tc.buildRequest, mockTokenSource(), &buildlog.BuildLog{}, "", true, false)
402+
b := New(r, tc.buildRequest, mockTokenSource(), &buildlog.BuildLog{}, "", true, false, false)
403403
var gotErr error
404404
var gotDigest string
405405
wantDigest := ""
@@ -638,7 +638,7 @@ func TestRunBuildSteps(t *testing.T) {
638638
}
639639
return nil
640640
}
641-
b := New(r, tc.buildRequest, mockTokenSource(), &buildlog.BuildLog{}, "", true, false)
641+
b := New(r, tc.buildRequest, mockTokenSource(), &buildlog.BuildLog{}, "", true, false, false)
642642
gotErr := b.runBuildSteps()
643643
if !reflect.DeepEqual(gotErr, tc.wantErr) {
644644
t.Errorf("%s: Wanted error %q, but got %q", tc.name, tc.wantErr, gotErr)
@@ -872,7 +872,7 @@ func TestBuildStepOrder(t *testing.T) {
872872
}
873873
return nil
874874
}
875-
b := New(r, tc.buildRequest, mockTokenSource(), &buildlog.BuildLog{}, "", true, false)
875+
b := New(r, tc.buildRequest, mockTokenSource(), &buildlog.BuildLog{}, "", true, false, false)
876876
errorFromFunction := make(chan error)
877877
go func() {
878878
errorFromFunction <- b.runBuildSteps()
@@ -922,7 +922,7 @@ func TestPushImages(t *testing.T) {
922922
}}
923923
for _, tc := range testCases {
924924
r := newMockRunner(t, tc.name)
925-
b := New(r, tc.buildRequest, mockTokenSource(), &buildlog.BuildLog{}, "", true, true)
925+
b := New(r, tc.buildRequest, mockTokenSource(), &buildlog.BuildLog{}, "", true, true, false)
926926
r.remotePushesFail = tc.remotePushesFail
927927
gotErr := b.pushImages()
928928
if !reflect.DeepEqual(gotErr, tc.wantErr) {
@@ -1013,7 +1013,7 @@ func TestBuildStepConcurrency(t *testing.T) {
10131013
}
10141014

10151015
// Run the build.
1016-
b := New(r, req, mockTokenSource(), &buildlog.BuildLog{}, "", true, false)
1016+
b := New(r, req, mockTokenSource(), &buildlog.BuildLog{}, "", true, false, false)
10171017
ret := make(chan error)
10181018
go func() {
10191019
ret <- b.runBuildSteps()
@@ -1052,7 +1052,7 @@ type fakeRunner struct {
10521052
func (f *fakeRunner) Run(args []string, _ io.Reader, _, _ io.Writer, _ string) error {
10531053
// The "+1" is for the name of the container which is appended to the
10541054
// dockerRunArgs base command.
1055-
b := New(nil, cb.Build{}, nil, &buildlog.BuildLog{}, "", true, false)
1055+
b := New(nil, cb.Build{}, nil, &buildlog.BuildLog{}, "", true, false, false)
10561056
argCount := len(b.dockerRunArgs("", 0)) + 1
10571057
switch {
10581058
case !startsWith(args, "docker", "run"):
@@ -1103,7 +1103,7 @@ func dockerRunString(idx int) string {
11031103
}
11041104

11051105
func dockerRunInStepDir(idx int, stepDir string) string {
1106-
b := New(nil, cb.Build{}, nil, &buildlog.BuildLog{}, "", true, false)
1106+
b := New(nil, cb.Build{}, nil, &buildlog.BuildLog{}, "", true, false, false)
11071107
dockerRunArg := b.dockerRunArgs(stepDir, idx)
11081108
return strings.Join(dockerRunArg, " ")
11091109
}
@@ -1146,7 +1146,7 @@ func TestErrorCollection(t *testing.T) {
11461146
"got an http status: 300: it's a mystery to me",
11471147
"got another http status: 300: it's a double mystery",
11481148
}
1149-
b := New(nil, cb.Build{}, nil, &buildlog.BuildLog{}, "", true, false)
1149+
b := New(nil, cb.Build{}, nil, &buildlog.BuildLog{}, "", true, false, false)
11501150
for _, o := range outputs {
11511151
b.detectPushFailure(o)
11521152
}
@@ -1214,7 +1214,7 @@ func TestEntrypoint(t *testing.T) {
12141214
stepArgs <- strings.Join(args, " ")
12151215
return nil
12161216
}
1217-
b := New(r, tc.buildRequest, mockTokenSource(), &buildlog.BuildLog{}, "", true, false)
1217+
b := New(r, tc.buildRequest, mockTokenSource(), &buildlog.BuildLog{}, "", true, false, false)
12181218
errorFromFunction := make(chan error)
12191219
go func() {
12201220
errorFromFunction <- b.runBuildSteps()
@@ -1305,7 +1305,7 @@ func TestSecrets(t *testing.T) {
13051305
gotCommand = strings.Join(args, " ")
13061306
return nil
13071307
}
1308-
b := New(r, buildRequest, mockTokenSource(), &buildlog.BuildLog{}, "", true, false)
1308+
b := New(r, buildRequest, mockTokenSource(), &buildlog.BuildLog{}, "", true, false, false)
13091309
b.kms = fakeKMS{
13101310
plaintext: c.plaintext,
13111311
err: c.kmsErr,
@@ -1544,7 +1544,7 @@ func TestStart(t *testing.T) {
15441544
r.localImages["gcr.io/build"] = true
15451545
return nil
15461546
}
1547-
b := New(r, tc.buildRequest, mockTokenSource(), &buildlog.BuildLog{}, "", true, tc.push)
1547+
b := New(r, tc.buildRequest, mockTokenSource(), &buildlog.BuildLog{}, "", true, tc.push, false)
15481548
b.Start()
15491549
<-b.Done
15501550

@@ -1566,7 +1566,7 @@ func TestUpdateDockerAccessToken(t *testing.T) {
15661566
t.Parallel()
15671567
r := newMockRunner(t, "TestUpdateDockerAccessToken")
15681568
r.dockerRunHandler = func(args []string, _, _ io.Writer) error { return nil }
1569-
b := New(r, cb.Build{}, nil, nil, "", false, false)
1569+
b := New(r, cb.Build{}, nil, nil, "", false, false, false)
15701570

15711571
// If UpdateDockerAccessToken is called before SetDockerAccessToken, we
15721572
// should get an error.

localbuilder_main.go

Lines changed: 24 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ import (
2626
"time"
2727

2828
computeMetadata "cloud.google.com/go/compute/metadata"
29+
"golang.org/x/oauth2"
2930
"github.com/google/uuid"
3031

3132
"github.com/GoogleCloudPlatform/container-builder-local/build"
@@ -49,7 +50,7 @@ const (
4950
var (
5051
configFile = flag.String("config", "cloudbuild.yaml", "cloud build config file path")
5152
substitutions = flag.String("substitutions", "", `substitutions key=value pairs separated by comma; for example _FOO=bar,_BAZ=baz`)
52-
dryRun = flag.Bool("dryrun", true, "If true, nothing will be run")
53+
dryRun = flag.Bool("dryrun", true, "If true, the config file is linted and the commands printed, but they are not run")
5354
push = flag.Bool("push", false, "If true, the images will be pushed")
5455
noSource = flag.Bool("no-source", false, "Specify that no source should be used for this build.")
5556
help = flag.Bool("help", false, "If true, print the help message")
@@ -167,6 +168,11 @@ func run(source string) error {
167168
return fmt.Errorf("Error applying substitutions: %v", err)
168169
}
169170

171+
// Validate the build after substitutions.
172+
if err := validate.CheckBuildAfterSubstitutions(buildConfig); err != nil {
173+
return fmt.Errorf("Error validating build after substitutions: %v", err)
174+
}
175+
170176
// Create a volume, a helper container to copy the source, and defer cleaning.
171177
volumeName := fmt.Sprintf("%s%s", volumeNamePrefix, uuid.New())
172178
if !*dryRun {
@@ -188,10 +194,22 @@ func run(source string) error {
188194
defer vol.Close()
189195
}
190196

191-
b := build.New(r, *buildConfig, nil, &buildlog.BuildLog{}, volumeName, true, *push)
197+
b := build.New(r, *buildConfig, nil /* TokenSource */, &buildlog.BuildLog{}, volumeName, true, *push, *dryRun)
192198

193199
// Do not run the spoofed metadata server on a dryrun.
194200
if !*dryRun {
201+
// Set initial Docker credentials.
202+
tok, err := gcloud.AccessToken(r)
203+
if err != nil {
204+
return fmt.Errorf("Error getting access token to set docker credentials: %v", err)
205+
}
206+
if err := b.SetDockerAccessToken(tok.AccessToken); err != nil {
207+
return fmt.Errorf("Error setting docker credentials: %v", err)
208+
}
209+
b.TokenSource = oauth2.StaticTokenSource(&oauth2.Token{
210+
AccessToken: tok.AccessToken,
211+
})
212+
195213
// On GCE, do not create a spoofed metadata server, use the existing one.
196214
// The cloudbuild network is still needed, with a private subnet.
197215
if computeMetadata.OnGCE() {
@@ -213,15 +231,6 @@ func run(source string) error {
213231
go supplyTokenToMetadata(metadataUpdater, r, stopchan)
214232
}
215233

216-
// Set initial Docker credentials.
217-
tok, err := gcloud.AccessToken(r)
218-
if err != nil {
219-
return fmt.Errorf("Error getting access token to set docker credentials: %v", err)
220-
}
221-
if err := b.SetDockerAccessToken(tok.AccessToken); err != nil {
222-
return fmt.Errorf("Error setting docker credentials: %v", err)
223-
}
224-
225234
// Write docker credentials for GCR. This writes the initial
226235
// ~/.docker/config.json, which is made available to build steps, and keeps
227236
// a fresh token available. Note that the user could `gcloud auth` to
@@ -246,6 +255,10 @@ func run(source string) error {
246255
if err := b.UpdateDockerAccessToken(tok.AccessToken); err != nil {
247256
log.Printf("Error updating docker credentials: %v", err)
248257
}
258+
259+
b.TokenSource = oauth2.StaticTokenSource(&oauth2.Token{
260+
AccessToken: tok.AccessToken,
261+
})
249262
case <-stopchan:
250263
return
251264
}

subst/subst.go

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,10 @@ import (
2323
)
2424

2525
var (
26-
unbracedKeyRE = `([A-Z_][A-Z0-9_]*)`
27-
bracedKeyRE = fmt.Sprintf(`{%s}`, unbracedKeyRE)
28-
validSubstKeyRE = regexp.MustCompile(`^\$(?:` + bracedKeyRE + `|` + unbracedKeyRE + `)`)
26+
unbracedKeyRE = `([A-Z_][A-Z0-9_]*)`
27+
bracedKeyRE = fmt.Sprintf(`{%s}`, unbracedKeyRE)
28+
validSubstKeyRE = regexp.MustCompile(`^\$(?:` + bracedKeyRE + `|` + unbracedKeyRE + `)`)
29+
maxShortShaLength = 7
2930
)
3031

3132
// SubstituteBuildFields does an in-place string substitution of build parameters.
@@ -38,6 +39,7 @@ func SubstituteBuildFields(b *cb.Build) error {
3839
branchName := ""
3940
tagName := ""
4041
commitSHA := ""
42+
shortSHA := ""
4143

4244
if s := b.GetSource(); s != nil {
4345
if rs := s.GetRepoSource(); rs != nil {
@@ -49,6 +51,11 @@ func SubstituteBuildFields(b *cb.Build) error {
4951
if sp := b.GetSourceProvenance(); sp != nil {
5052
if rrs := sp.GetResolvedRepoSource(); rrs != nil {
5153
commitSHA = rrs.GetCommitSha()
54+
shortSHA = commitSHA
55+
// Length of commit SHA can be less than maxShortShaLength.
56+
if len(shortSHA) > maxShortShaLength {
57+
shortSHA = shortSHA[0:maxShortShaLength]
58+
}
5259
}
5360
}
5461

@@ -61,6 +68,7 @@ func SubstituteBuildFields(b *cb.Build) error {
6168
"TAG_NAME": tagName,
6269
"REVISION_ID": commitSHA,
6370
"COMMIT_SHA": commitSHA,
71+
"SHORT_SHA": shortSHA,
6472
}
6573

6674
// Add user-defined substitutions, overriding built-in substitutions.

0 commit comments

Comments
 (0)