Skip to content

Commit eb01b08

Browse files
mafredrijohnstcndannykopping
authored
feat: implement reproducible build and get cached image (#213)
- Adds `--push-image` / `ENVBUILDER_PUSH_IMAGE` option to push image to CACHE_REPO. - Adds `--get-cached-image` / `ENVBUILDER_GET_CACHED_IMAGE` option to only check for presence of image in build cache. Co-authored-by: Cian Johnston <[email protected]> Co-authored-by: Danny Kopping <[email protected]>
1 parent c87f0b5 commit eb01b08

File tree

7 files changed

+329
-25
lines changed

7 files changed

+329
-25
lines changed

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -363,4 +363,6 @@ On MacOS or Windows systems, we recommend either using a VM or the provided `.de
363363
| `--coder-agent-url` | `CODER_AGENT_URL` | | URL of the Coder deployment. If CODER_AGENT_TOKEN is also set, logs from envbuilder will be forwarded here and will be visible in the workspace build logs. |
364364
| `--coder-agent-token` | `CODER_AGENT_TOKEN` | | Authentication token for a Coder agent. If this is set, then CODER_AGENT_URL must also be set. |
365365
| `--coder-agent-subsystem` | `CODER_AGENT_SUBSYSTEM` | | Coder agent subsystems to report when forwarding logs. The envbuilder subsystem is always included. |
366+
| `--push-image` | `ENVBUILDER_PUSH_IMAGE` | | Push the built image to a remote registry. This option forces a reproducible build. |
367+
| `--get-cached-image` | `ENVBUILDER_GET_CACHED_IMAGE` | | Print the digest of the cached image, if available. Exits with an error if not found. |
366368
<!--- END docsgen --->

envbuilder.go

+44-8
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,9 @@ func Run(ctx context.Context, options Options) error {
9898
if options.InitCommand == "" {
9999
options.InitCommand = "/bin/sh"
100100
}
101+
if options.CacheRepo == "" && options.PushImage {
102+
return fmt.Errorf("--cache-repo must be set when using --push-image")
103+
}
101104
// Default to the shell!
102105
initArgs := []string{"-c", options.InitScript}
103106
if options.InitArgs != "" {
@@ -118,11 +121,11 @@ func Run(ctx context.Context, options Options) error {
118121
options.WorkspaceFolder = f
119122
}
120123

121-
stageNumber := 1
124+
stageNumber := 0
122125
startStage := func(format string, args ...any) func(format string, args ...any) {
123126
now := time.Now()
124-
stageNum := stageNumber
125127
stageNumber++
128+
stageNum := stageNumber
126129
options.Logger(notcodersdk.LogLevelInfo, "#%d: %s", stageNum, fmt.Sprintf(format, args...))
127130

128131
return func(format string, args ...any) {
@@ -341,7 +344,7 @@ func Run(ctx context.Context, options Options) error {
341344

342345
HijackLogrus(func(entry *logrus.Entry) {
343346
for _, line := range strings.Split(entry.Message, "\r") {
344-
options.Logger(notcodersdk.LogLevelInfo, "#2: %s", color.HiBlackString(line))
347+
options.Logger(notcodersdk.LogLevelInfo, "#%d: %s", stageNumber, color.HiBlackString(line))
345348
}
346349
})
347350

@@ -471,20 +474,24 @@ func Run(ctx context.Context, options Options) error {
471474
cacheTTL = time.Hour * 24 * time.Duration(options.CacheTTLDays)
472475
}
473476

474-
endStage := startStage("🏗️ Building image...")
475477
// At this point we have all the context, we can now build!
476478
registryMirror := []string{}
477479
if val, ok := os.LookupEnv("KANIKO_REGISTRY_MIRROR"); ok {
478480
registryMirror = strings.Split(val, ";")
479481
}
480-
image, err := executor.DoBuild(&config.KanikoOptions{
482+
var destinations []string
483+
if options.CacheRepo != "" {
484+
destinations = append(destinations, options.CacheRepo)
485+
}
486+
opts := &config.KanikoOptions{
481487
// Boilerplate!
482488
CustomPlatform: platforms.Format(platforms.Normalize(platforms.DefaultSpec())),
483489
SnapshotMode: "redo",
484490
RunV2: true,
485491
RunStdout: stdoutWriter,
486492
RunStderr: stderrWriter,
487-
Destinations: []string{"local"},
493+
Destinations: destinations,
494+
NoPush: !options.PushImage || len(destinations) == 0,
488495
CacheRunLayers: true,
489496
CacheCopyLayers: true,
490497
CompressedCaching: true,
@@ -515,11 +522,40 @@ func Run(ctx context.Context, options Options) error {
515522
RegistryMirrors: registryMirror,
516523
},
517524
SrcContext: buildParams.BuildContext,
518-
})
525+
526+
// For cached image utilization, produce reproducible builds.
527+
Reproducible: options.PushImage,
528+
}
529+
530+
if options.GetCachedImage {
531+
endStage := startStage("🏗️ Checking for cached image...")
532+
image, err := executor.DoCacheProbe(opts)
533+
if err != nil {
534+
return nil, xerrors.Errorf("get cached image: %w", err)
535+
}
536+
digest, err := image.Digest()
537+
if err != nil {
538+
return nil, xerrors.Errorf("get cached image digest: %w", err)
539+
}
540+
endStage("🏗️ Found cached image!")
541+
_, _ = fmt.Fprintf(os.Stdout, "%s@%s\n", options.CacheRepo, digest.String())
542+
os.Exit(0)
543+
}
544+
545+
endStage := startStage("🏗️ Building image...")
546+
image, err := executor.DoBuild(opts)
519547
if err != nil {
520-
return nil, err
548+
return nil, xerrors.Errorf("do build: %w", err)
521549
}
522550
endStage("🏗️ Built image!")
551+
if options.PushImage {
552+
endStage = startStage("🏗️ Pushing image...")
553+
if err := executor.DoPush(image, opts); err != nil {
554+
return nil, xerrors.Errorf("do push: %w", err)
555+
}
556+
endStage("🏗️ Pushed image!")
557+
}
558+
523559
return image, err
524560
}
525561

go.mod

+1-3
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ toolchain go1.22.3
66

77
// There are a few options we need added to Kaniko!
88
// See: https://github.com/GoogleContainerTools/kaniko/compare/main...coder:kaniko:main
9-
replace github.com/GoogleContainerTools/kaniko => github.com/coder/kaniko v0.0.0-20240524082248-9d0d55902c34
9+
replace github.com/GoogleContainerTools/kaniko => github.com/coder/kaniko v0.0.0-20240612094751-9d2f7eaa733c
1010

1111
require (
1212
cdr.dev/slog v1.6.2-0.20240126064726-20367d4aede6
@@ -105,7 +105,6 @@ require (
105105
github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c // indirect
106106
github.com/docker/go-metrics v0.0.1 // indirect
107107
github.com/docker/go-units v0.5.0 // indirect
108-
github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 // indirect
109108
github.com/ePirat/docker-credential-gitlabci v1.0.0 // indirect
110109
github.com/emirpasic/gods v1.18.1 // indirect
111110
github.com/felixge/httpsnoop v1.0.4 // indirect
@@ -118,7 +117,6 @@ require (
118117
github.com/golang-jwt/jwt/v4 v4.5.0 // indirect
119118
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
120119
github.com/golang/protobuf v1.5.4 // indirect
121-
github.com/gomodule/redigo v1.8.9 // indirect
122120
github.com/google/go-cmp v0.6.0 // indirect
123121
github.com/gorilla/handlers v1.5.1 // indirect
124122
github.com/gorilla/mux v1.8.1 // indirect

go.sum

+5-14
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,9 @@ github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6r
9898
github.com/breml/rootcerts v0.2.10 h1:UGVZ193UTSUASpGtg6pbDwzOd7XQP+at0Ssg1/2E4h8=
9999
github.com/breml/rootcerts v0.2.10/go.mod h1:24FDtzYMpqIeYC7QzaE8VPRQaFZU5TIUDlyk8qwjD88=
100100
github.com/bsm/ginkgo/v2 v2.7.0/go.mod h1:AiKlXPm7ItEHNc/2+OkrNG4E0ITzojb9/xWzvQ9XZ9w=
101+
github.com/bsm/ginkgo/v2 v2.9.5 h1:rtVBYPs3+TC5iLUVOis1B9tjLTup7Cj5IfzosKtvTJ0=
102+
github.com/bsm/ginkgo/v2 v2.9.5/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
103+
github.com/bsm/gomega v1.26.0 h1:LhQm+AFcgV2M0WyKroMASzAzCAJVpAxQXv4SaI9a69Y=
101104
github.com/bsm/gomega v1.26.0/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
102105
github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0=
103106
github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM=
@@ -123,8 +126,8 @@ github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGX
123126
github.com/cockroachdb/datadriven v0.0.0-20200714090401-bf6692d28da5/go.mod h1:h6jFvWxBdQXxjopDMZyH2UVceIRfR84bdzbkoKrsWNo=
124127
github.com/cockroachdb/errors v1.2.4/go.mod h1:rQD95gz6FARkaKkQXUksEje/d9a6wBJoCr5oaCLELYA=
125128
github.com/cockroachdb/logtags v0.0.0-20190617123548-eb05cc24525f/go.mod h1:i/u985jwjWRlyHXQbwatDASoW0RMlZ/3i9yJHE2xLkI=
126-
github.com/coder/kaniko v0.0.0-20240524082248-9d0d55902c34 h1:Wm7sMNc1aTN5l0NerYHb3LZdQJVQp4QrW4v83N21sfc=
127-
github.com/coder/kaniko v0.0.0-20240524082248-9d0d55902c34/go.mod h1:YMK7BlxerzLlMwihGxNWUaFoN9LXCij4P+w/8/fNlcM=
129+
github.com/coder/kaniko v0.0.0-20240612094751-9d2f7eaa733c h1:m/cK7QW+IIydq+7zmuGesY1k6CEZlKooSF+KtIcXke8=
130+
github.com/coder/kaniko v0.0.0-20240612094751-9d2f7eaa733c/go.mod h1:YMK7BlxerzLlMwihGxNWUaFoN9LXCij4P+w/8/fNlcM=
128131
github.com/coder/pretty v0.0.0-20230908205945-e89ba86370e0 h1:3A0ES21Ke+FxEM8CXx9n47SZOKOpgSE1bbJzlE4qPVs=
129132
github.com/coder/pretty v0.0.0-20230908205945-e89ba86370e0/go.mod h1:5UuS2Ts+nTToAMeOjNlnHFkPahrtDkmpydBen/3wgZc=
130133
github.com/coder/retry v1.5.1 h1:iWu8YnD8YqHs3XwqrqsjoBTAVqT9ml6z9ViJ2wlMiqc=
@@ -165,12 +168,8 @@ github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/r
165168
github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
166169
github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U=
167170
github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE=
168-
github.com/distribution/distribution/v3 v3.0.0-20230629214736-bac7f02e02a1 h1:yRwt9RluqBtKyDLRY7J0Cf/TVqvG56vKx2Eyndy8qNQ=
169-
github.com/distribution/distribution/v3 v3.0.0-20230629214736-bac7f02e02a1/go.mod h1:+fqBJ4vPYo4Uu1ZE4d+bUtTLRXfdSL3NvCZIZ9GHv58=
170171
github.com/distribution/distribution/v3 v3.0.0-alpha.1 h1:jn7I1gvjOvmLztH1+1cLiUFud7aeJCIQcgzugtwjyJo=
171172
github.com/distribution/distribution/v3 v3.0.0-alpha.1/go.mod h1:LCp4JZp1ZalYg0W/TN05jarCQu+h4w7xc7ZfQF4Y/cY=
172-
github.com/distribution/reference v0.5.0 h1:/FUIFXtfc/x2gpa5/VGfiGLuOIdYa1t65IKK2OFGvA0=
173-
github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
174173
github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk=
175174
github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E=
176175
github.com/docker/cli v26.1.0+incompatible h1:+nwRy8Ocd8cYNQ60mozDDICICD8aoFGtlPXifX/UQ3Y=
@@ -189,8 +188,6 @@ github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQ
189188
github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw=
190189
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
191190
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
192-
github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1 h1:ZClxb8laGDf5arXfYcAtECDFgAgHklGI8CxgjHnXKJ4=
193-
github.com/docker/libtrust v0.0.0-20150114040149-fa567046d9b1/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE=
194191
github.com/ePirat/docker-credential-gitlabci v1.0.0 h1:YRkUSvkON6rT88vtscClAmPEYWhtltGEAuRVYtz1/+Y=
195192
github.com/ePirat/docker-credential-gitlabci v1.0.0/go.mod h1:Ptmh+D0lzBQtgb6+QHjXl9HqOn3T1P8fKUHldiSQQGA=
196193
github.com/elazarl/goproxy v0.0.0-20230808193330-2592e75ae04a h1:mATvB/9r/3gvcejNsXKSkQ6lcIaNec2nyfOdlTBR2lU=
@@ -260,8 +257,6 @@ github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaS
260257
github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY=
261258
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
262259
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
263-
github.com/gomodule/redigo v1.8.9 h1:Sl3u+2BI/kk+VEatbj0scLdrFhjPmbxOc1myhDP41ws=
264-
github.com/gomodule/redigo v1.8.9/go.mod h1:7ArFNvsTjH8GMMzB4uy1snslv2BwmginuMs06a1uzZE=
265260
github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M=
266261
github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
267262
github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU=
@@ -280,8 +275,6 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
280275
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
281276
github.com/gorilla/handlers v1.5.1 h1:9lRY6j8DEeeBT10CvO9hGW0gmky0BprnvDI5vfhUHH4=
282277
github.com/gorilla/handlers v1.5.1/go.mod h1:t8XrUpc4KVXb7HGyJ4/cEnwQiaxrX/hz1Zv/4g96P1Q=
283-
github.com/gorilla/mux v1.8.0 h1:i40aqfkR1h2SlN9hojwV5ZA91wcXFOvkdNIeFDP5koI=
284-
github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So=
285278
github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
286279
github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
287280
github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms=
@@ -393,8 +386,6 @@ github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI=
393386
github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M=
394387
github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U=
395388
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
396-
github.com/opencontainers/image-spec v1.1.0-rc5 h1:Ygwkfw9bpDvs+c9E34SdgGOj41dX/cbdlwvlWt0pnFI=
397-
github.com/opencontainers/image-spec v1.1.0-rc5/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8=
398389
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
399390
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
400391
github.com/opencontainers/runtime-spec v1.1.0 h1:HHUyrt9mwHUjtasSbXSMvs4cyFxh+Bll4AjJ9odEGpg=

0 commit comments

Comments
 (0)