@@ -27,6 +27,7 @@ import (
27
27
"github.com/coder/envbuilder/constants"
28
28
"github.com/coder/envbuilder/git"
29
29
"github.com/coder/envbuilder/options"
30
+ "github.com/go-git/go-billy/v5"
30
31
31
32
"github.com/GoogleContainerTools/kaniko/pkg/config"
32
33
"github.com/GoogleContainerTools/kaniko/pkg/creds"
@@ -311,26 +312,58 @@ func Run(ctx context.Context, opts options.Options) error {
311
312
}
312
313
313
314
// In order to allow 'resuming' envbuilder, embed the binary into the image
314
- // if it is being pushed
315
+ // if it is being pushed.
316
+ // As these files will be owned by root, it is considerate to clean up
317
+ // after we're done!
318
+ cleanupBuildContext := func () {}
315
319
if opts .PushImage {
316
- exePath , err := os .Executable ()
317
- if err != nil {
318
- return xerrors .Errorf ("get exe path: %w" , err )
320
+ // Add exceptions in Kaniko's ignorelist for these magic files we add.
321
+ if err := util .AddAllowedPathToDefaultIgnoreList (opts .BinaryPath ); err != nil {
322
+ return fmt .Errorf ("add envbuilder binary to ignore list: %w" , err )
323
+ }
324
+ if err := util .AddAllowedPathToDefaultIgnoreList (constants .MagicImage ); err != nil {
325
+ return fmt .Errorf ("add magic image file to ignore list: %w" , err )
319
326
}
320
- // Add an exception for the current running binary in kaniko ignore list
321
- if err := util . AddAllowedPathToDefaultIgnoreList ( exePath ); err != nil {
322
- return xerrors .Errorf ("add exe path to ignore list : %w" , err )
327
+ magicTempDir := filepath . Join ( buildParams . BuildContext , constants . MagicTempDir )
328
+ if err := opts . Filesystem . MkdirAll ( magicTempDir , 0o755 ); err != nil {
329
+ return fmt .Errorf ("create magic temp dir in build context : %w" , err )
323
330
}
331
+ // Add the magic directives that embed the binary into the built image.
332
+ buildParams .DockerfileContent += constants .MagicDirectives
324
333
// Copy the envbuilder binary into the build context.
325
- buildParams .DockerfileContent += fmt .Sprintf (`
326
- COPY --chmod=0755 %s %s
327
- USER root
328
- WORKDIR /
329
- ENTRYPOINT [%q]` , exePath , exePath , exePath )
330
- dst := filepath .Join (buildParams .BuildContext , exePath )
331
- if err := copyFile (exePath , dst ); err != nil {
332
- return xerrors .Errorf ("copy running binary to build context: %w" , err )
334
+ // External callers will need to specify the path to the desired envbuilder binary.
335
+ envbuilderBinDest := filepath .Join (
336
+ magicTempDir ,
337
+ filepath .Base (constants .MagicBinaryLocation ),
338
+ )
339
+ // Also touch the magic file that signifies the image has been built!
340
+ magicImageDest := filepath .Join (
341
+ magicTempDir ,
342
+ filepath .Base (constants .MagicImage ),
343
+ )
344
+ // Clean up after build!
345
+ var cleanupOnce sync.Once
346
+ cleanupBuildContext = func () {
347
+ cleanupOnce .Do (func () {
348
+ for _ , path := range []string {magicImageDest , envbuilderBinDest , magicTempDir } {
349
+ if err := opts .Filesystem .Remove (path ); err != nil {
350
+ opts .Logger (log .LevelWarn , "failed to clean up magic temp dir from build context: %w" , err )
351
+ }
352
+ }
353
+ })
354
+ }
355
+ defer cleanupBuildContext ()
356
+
357
+ opts .Logger (log .LevelDebug , "copying envbuilder binary at %q to build context %q" , opts .BinaryPath , envbuilderBinDest )
358
+ if err := copyFile (opts .Filesystem , opts .BinaryPath , envbuilderBinDest , 0o755 ); err != nil {
359
+ return fmt .Errorf ("copy envbuilder binary to build context: %w" , err )
360
+ }
361
+
362
+ opts .Logger (log .LevelDebug , "touching magic image file at %q in build context %q" , magicImageDest , buildParams .BuildContext )
363
+ if err := touchFile (opts .Filesystem , magicImageDest , 0o755 ); err != nil {
364
+ return fmt .Errorf ("touch magic image file in build context: %w" , err )
333
365
}
366
+
334
367
}
335
368
336
369
// temp move of all ro mounts
@@ -354,8 +387,10 @@ ENTRYPOINT [%q]`, exePath, exePath, exePath)
354
387
stderrWriter , closeStderr := log .Writer (opts .Logger )
355
388
defer closeStderr ()
356
389
build := func () (v1.Image , error ) {
357
- _ , err := opts .Filesystem .Stat (constants .MagicFile )
358
- if err == nil && opts .SkipRebuild {
390
+ defer cleanupBuildContext ()
391
+ _ , alreadyBuiltErr := opts .Filesystem .Stat (constants .MagicFile )
392
+ _ , isImageErr := opts .Filesystem .Stat (constants .MagicImage )
393
+ if (alreadyBuiltErr == nil && opts .SkipRebuild ) || isImageErr == nil {
359
394
endStage := startStage ("🏗️ Skipping build because of cache..." )
360
395
imageRef , err := devcontainer .ImageFromDockerfile (buildParams .DockerfileContent )
361
396
if err != nil {
@@ -381,26 +416,7 @@ ENTRYPOINT [%q]`, exePath, exePath, exePath)
381
416
if err := maybeDeleteFilesystem (opts .Logger , opts .ForceSafe ); err != nil {
382
417
return nil , fmt .Errorf ("delete filesystem: %w" , err )
383
418
}
384
- /*
385
- stdoutReader, stdoutWriter := io.Pipe()
386
- stderrReader, stderrWriter := io.Pipe()
387
- defer stdoutReader.Close()
388
- defer stdoutWriter.Close()
389
- defer stderrReader.Close()
390
- defer stderrWriter.Close()
391
- go func() {
392
- scanner := bufio.NewScanner(stdoutReader)
393
- for scanner.Scan() {
394
- opts.Logger(log.LevelInfo, "%s", scanner.Text())
395
- }
396
- }()
397
- go func() {
398
- scanner := bufio.NewScanner(stderrReader)
399
- for scanner.Scan() {
400
- opts.Logger(log.LevelInfo, "%s", scanner.Text())
401
- }
402
- }()
403
- */
419
+
404
420
cacheTTL := time .Hour * 24 * 7
405
421
if opts .CacheTTLDays != 0 {
406
422
cacheTTL = time .Hour * 24 * time .Duration (opts .CacheTTLDays )
@@ -1064,23 +1080,41 @@ func RunCacheProbe(ctx context.Context, opts options.Options) (v1.Image, error)
1064
1080
// We expect an image built and pushed by envbuilder to have the envbuilder
1065
1081
// binary present at a predefined path. In order to correctly replicate the
1066
1082
// build via executor.RunCacheProbe we need to have the *exact* copy of the
1067
- // envbuilder binary available used to build the image.
1068
- exePath := opts .BinaryPath
1069
- // Add an exception for the current running binary in kaniko ignore list
1070
- if err := util .AddAllowedPathToDefaultIgnoreList (exePath ); err != nil {
1071
- return nil , xerrors .Errorf ("add exe path to ignore list: %w" , err )
1072
- }
1083
+ // envbuilder binary available used to build the image and we also need to
1084
+ // add the magic directives to the Dockerfile content.
1085
+ buildParams .DockerfileContent += constants .MagicDirectives
1086
+ magicTempDir := filepath .Join (buildParams .BuildContext , constants .MagicTempDir )
1087
+ if err := opts .Filesystem .MkdirAll (magicTempDir , 0o755 ); err != nil {
1088
+ return nil , fmt .Errorf ("create magic temp dir in build context: %w" , err )
1089
+ }
1090
+ envbuilderBinDest := filepath .Join (
1091
+ magicTempDir ,
1092
+ filepath .Base (constants .MagicBinaryLocation ),
1093
+ )
1094
+
1073
1095
// Copy the envbuilder binary into the build context.
1074
- buildParams .DockerfileContent += fmt .Sprintf (`
1075
- COPY --chmod=0755 %s %s
1076
- USER root
1077
- WORKDIR /
1078
- ENTRYPOINT [%q]` , exePath , exePath , exePath )
1079
- dst := filepath .Join (buildParams .BuildContext , exePath )
1080
- if err := copyFile (exePath , dst ); err != nil {
1096
+ opts .Logger (log .LevelDebug , "copying envbuilder binary at %q to build context %q" , opts .BinaryPath , buildParams .BuildContext )
1097
+ if err := copyFile (opts .Filesystem , opts .BinaryPath , envbuilderBinDest , 0o755 ); err != nil {
1081
1098
return nil , xerrors .Errorf ("copy envbuilder binary to build context: %w" , err )
1082
1099
}
1083
1100
1101
+ // Also touch the magic file that signifies the image has been built!
1102
+ magicImageDest := filepath .Join (
1103
+ magicTempDir ,
1104
+ filepath .Base (constants .MagicImage ),
1105
+ )
1106
+ if err := touchFile (opts .Filesystem , magicImageDest , 0o755 ); err != nil {
1107
+ return nil , fmt .Errorf ("touch magic image file in build context: %w" , err )
1108
+ }
1109
+ defer func () {
1110
+ // Clean up after we're done!
1111
+ for _ , path := range []string {magicImageDest , envbuilderBinDest , magicTempDir } {
1112
+ if err := opts .Filesystem .Remove (path ); err != nil {
1113
+ opts .Logger (log .LevelWarn , "failed to clean up magic temp dir from build context: %w" , err )
1114
+ }
1115
+ }
1116
+ }()
1117
+
1084
1118
stdoutWriter , closeStdout := log .Writer (opts .Logger )
1085
1119
defer closeStdout ()
1086
1120
stderrWriter , closeStderr := log .Writer (opts .Logger )
@@ -1138,8 +1172,8 @@ ENTRYPOINT [%q]`, exePath, exePath, exePath)
1138
1172
},
1139
1173
SrcContext : buildParams .BuildContext ,
1140
1174
1141
- // For cached image utilization, produce reproducible builds .
1142
- Reproducible : opts . PushImage ,
1175
+ // When performing a cache probe, always perform reproducible snapshots .
1176
+ Reproducible : true ,
1143
1177
}
1144
1178
1145
1179
endStage := startStage ("🏗️ Checking for cached image..." )
@@ -1382,24 +1416,38 @@ func maybeDeleteFilesystem(logger log.Func, force bool) error {
1382
1416
return util .DeleteFilesystem ()
1383
1417
}
1384
1418
1385
- func copyFile (src , dst string ) error {
1386
- content , err := os . ReadFile (src )
1419
+ func copyFile (fs billy. Filesystem , src , dst string , mode fs. FileMode ) error {
1420
+ srcF , err := fs . Open (src )
1387
1421
if err != nil {
1388
- return fmt .Errorf ("read file failed : %w" , err )
1422
+ return fmt .Errorf ("open src file : %w" , err )
1389
1423
}
1424
+ defer srcF .Close ()
1390
1425
1391
- err = os .MkdirAll (filepath .Dir (dst ), 0o755 )
1426
+ err = fs .MkdirAll (filepath .Dir (dst ), mode )
1392
1427
if err != nil {
1393
- return fmt .Errorf ("mkdir all failed: %w" , err )
1428
+ return fmt .Errorf ("create destination dir failed: %w" , err )
1394
1429
}
1395
1430
1396
- err = os . WriteFile (dst , content , 0o644 )
1431
+ dstF , err := fs . OpenFile (dst , os . O_WRONLY | os . O_CREATE | os . O_TRUNC , mode )
1397
1432
if err != nil {
1398
- return fmt .Errorf ("write file failed: %w" , err )
1433
+ return fmt .Errorf ("open dest file for writing: %w" , err )
1434
+ }
1435
+ defer dstF .Close ()
1436
+
1437
+ if _ , err := io .Copy (dstF , srcF ); err != nil {
1438
+ return fmt .Errorf ("copy failed: %w" , err )
1399
1439
}
1400
1440
return nil
1401
1441
}
1402
1442
1443
+ func touchFile (fs billy.Filesystem , dst string , mode fs.FileMode ) error {
1444
+ f , err := fs .OpenFile (dst , os .O_RDWR | os .O_CREATE | os .O_TRUNC , mode )
1445
+ if err != nil {
1446
+ return xerrors .Errorf ("failed to touch file: %w" , err )
1447
+ }
1448
+ return f .Close ()
1449
+ }
1450
+
1403
1451
func initDockerConfigJSON (dockerConfigBase64 string ) (func () error , error ) {
1404
1452
var cleanupOnce sync.Once
1405
1453
noop := func () error { return nil }
0 commit comments