@@ -61,18 +61,22 @@ func (p platformParser) DefaultSpec() platforms.Platform {
61
61
}
62
62
63
63
func Build (ctx context.Context , client * containerd.Client , options types.BuilderBuildOptions ) error {
64
- buildctlBinary , buildctlArgs , needsLoading , metaFile , tags , cleanup , err := generateBuildctlArgs (ctx , client , options )
64
+ buildCtlArgs , err := generateBuildctlArgs (ctx , client , options )
65
65
if err != nil {
66
66
return err
67
67
}
68
- if cleanup != nil {
69
- defer cleanup ()
68
+ if buildCtlArgs . Cleanup != nil {
69
+ defer buildCtlArgs . Cleanup ()
70
70
}
71
71
72
+ buildctlBinary := buildCtlArgs .BuildctlBinary
73
+ buildctlArgs := buildCtlArgs .BuildctlArgs
74
+
72
75
log .L .Debugf ("running %s %v" , buildctlBinary , buildctlArgs )
73
76
buildctlCmd := exec .Command (buildctlBinary , buildctlArgs ... )
74
77
buildctlCmd .Env = os .Environ ()
75
78
79
+ needsLoading := buildCtlArgs .NeedsLoading
76
80
var buildctlStdout io.Reader
77
81
if needsLoading {
78
82
buildctlStdout , err = buildctlCmd .StdoutPipe ()
@@ -95,6 +99,26 @@ func Build(ctx context.Context, client *containerd.Client, options types.Builder
95
99
if err != nil {
96
100
return err
97
101
}
102
+
103
+ if buildCtlArgs .DestFile == "" {
104
+ log .L .Debug ("no tar file specified" )
105
+ } else {
106
+ // Separate TTY (image loading) buildctl output and tarball output
107
+ // Write buildctl output to stdout
108
+ if _ , err := io .Copy (os .Stdout , buildctlStdout ); err != nil {
109
+ return err
110
+ }
111
+
112
+ // Open the tar file
113
+ reader , err := os .Open (buildCtlArgs .DestFile )
114
+ if err != nil {
115
+ return fmt .Errorf ("failed to open tar file: %v" , err )
116
+ }
117
+ defer reader .Close ()
118
+ buildctlStdout = reader
119
+ }
120
+
121
+ // Load the image into the containerd image store
98
122
if err = loadImage (ctx , buildctlStdout , options .GOptions .Namespace , options .GOptions .Address , options .GOptions .Snapshotter , options .Stdout , platMC , options .Quiet ); err != nil {
99
123
return err
100
124
}
@@ -105,7 +129,7 @@ func Build(ctx context.Context, client *containerd.Client, options types.Builder
105
129
}
106
130
107
131
if options .IidFile != "" {
108
- id , err := getDigestFromMetaFile (metaFile )
132
+ id , err := getDigestFromMetaFile (buildCtlArgs . MetaFile )
109
133
if err != nil {
110
134
return err
111
135
}
@@ -114,6 +138,7 @@ func Build(ctx context.Context, client *containerd.Client, options types.Builder
114
138
}
115
139
}
116
140
141
+ tags := buildCtlArgs .Tags
117
142
if len (tags ) > 1 {
118
143
log .L .Debug ("Found more than 1 tag" )
119
144
imageService := client .ImageService ()
@@ -160,7 +185,11 @@ func loadImage(ctx context.Context, in io.Reader, namespace, address, snapshotte
160
185
client .Close ()
161
186
}()
162
187
r := & readCounter {Reader : in }
163
- imgs , err := client .Import (ctx , r , containerd .WithDigestRef (archive .DigestTranslator (snapshotter )), containerd .WithSkipDigestRef (func (name string ) bool { return name != "" }), containerd .WithImportPlatform (platMC ))
188
+ imgs , err := client .Import (ctx , r ,
189
+ containerd .WithDigestRef (archive .DigestTranslator (snapshotter )),
190
+ containerd .WithSkipDigestRef (func (name string ) bool { return name != "" }),
191
+ containerd .WithImportPlatform (platMC ),
192
+ )
164
193
if err != nil {
165
194
if r .N == 0 {
166
195
// Avoid confusing "unrecognized image format"
@@ -192,23 +221,34 @@ func loadImage(ctx context.Context, in io.Reader, namespace, address, snapshotte
192
221
return nil
193
222
}
194
223
195
- func generateBuildctlArgs (ctx context.Context , client * containerd.Client , options types.BuilderBuildOptions ) (buildCtlBinary string ,
196
- buildctlArgs []string , needsLoading bool , metaFile string , tags []string , cleanup func (), err error ) {
224
+ type BuildctlArgsResult struct {
225
+ BuildctlArgs []string
226
+ BuildctlBinary string
227
+ Cleanup func ()
228
+ DestFile string
229
+ MetaFile string
230
+ NeedsLoading bool // Specifies whether the image needs to be loaded into the containerd image store
231
+ Tags []string
232
+ }
197
233
234
+ func generateBuildctlArgs (ctx context.Context , client * containerd.Client , options types.BuilderBuildOptions ) (result BuildctlArgsResult , err error ) {
198
235
buildctlBinary , err := buildkitutil .BuildctlBinary ()
199
236
if err != nil {
200
- return "" , nil , false , "" , nil , nil , err
237
+ return result , err
201
238
}
239
+ result .BuildctlBinary = buildctlBinary
240
+
241
+ var defaultDest string
202
242
203
243
output := options .Output
204
244
if output == "" {
205
245
info , err := client .Server (ctx )
206
246
if err != nil {
207
- return "" , nil , false , "" , nil , nil , err
247
+ return result , err
208
248
}
209
249
sharable , err := isImageSharable (options .BuildKitHost , options .GOptions .Namespace , info .UUID , options .GOptions .Snapshotter , options .Platform )
210
250
if err != nil {
211
- return "" , nil , false , "" , nil , nil , err
251
+ return result , err
212
252
}
213
253
if sharable {
214
254
output = "type=image,unpack=true" // ensure the target stage is unlazied (needed for any snapshotters)
@@ -219,7 +259,14 @@ func generateBuildctlArgs(ctx context.Context, client *containerd.Client, option
219
259
// TODO: consider using type=oci for single-options.Platform build too
220
260
output = "type=oci"
221
261
}
222
- needsLoading = true
262
+ result .NeedsLoading = true
263
+
264
+ // Set the default destination file
265
+ defaultDestFile , err := filepath .Abs ("output.tar" )
266
+ if err != nil {
267
+ return result , fmt .Errorf ("failed to set the default destination file path: %v" , err )
268
+ }
269
+ defaultDest = fmt .Sprintf (",dest=%s" , defaultDestFile )
223
270
}
224
271
} else {
225
272
if ! strings .Contains (output , "type=" ) {
@@ -229,32 +276,43 @@ func generateBuildctlArgs(ctx context.Context, client *containerd.Client, option
229
276
}
230
277
if strings .Contains (output , "type=docker" ) || strings .Contains (output , "type=oci" ) {
231
278
if ! strings .Contains (output , "dest=" ) {
232
- needsLoading = true
279
+ result . NeedsLoading = true
233
280
}
234
281
}
235
282
}
283
+
284
+ var tags []string
236
285
if tags = strutil .DedupeStrSlice (options .Tag ); len (tags ) > 0 {
237
286
ref := tags [0 ]
238
287
parsedReference , err := referenceutil .Parse (ref )
239
288
if err != nil {
240
- return "" , nil , false , "" , nil , nil , err
289
+ return result , err
241
290
}
242
291
output += ",name=" + parsedReference .String ()
243
292
244
293
// pick the first tag and add it to output
245
294
for idx , tag := range tags {
246
295
parsedReference , err = referenceutil .Parse (tag )
247
296
if err != nil {
248
- return "" , nil , false , "" , nil , nil , err
297
+ return result , err
249
298
}
250
299
tags [idx ] = parsedReference .String ()
251
300
}
252
301
} else if len (tags ) == 0 {
253
302
output = output + ",dangling-name-prefix=<none>"
254
303
}
304
+ result .Tags = tags
255
305
256
- buildctlArgs = buildkitutil .BuildctlBaseArgs (options .BuildKitHost )
306
+ // Add default destination file to output
307
+ output += defaultDest
257
308
309
+ // Extract destination file from output
310
+ if strings .Contains (output , "dest=" ) {
311
+ _ , destFilePath , _ := strings .Cut (output , "dest=" )
312
+ result .DestFile = destFilePath
313
+ }
314
+
315
+ buildctlArgs := buildkitutil .BuildctlBaseArgs (options .BuildKitHost )
258
316
buildctlArgs = append (buildctlArgs , []string {
259
317
"build" ,
260
318
"--progress=" + options .Progress ,
@@ -271,9 +329,9 @@ func generateBuildctlArgs(ctx context.Context, client *containerd.Client, option
271
329
var err error
272
330
dir , err = buildkitutil .WriteTempDockerfile (options .Stdin )
273
331
if err != nil {
274
- return "" , nil , false , "" , nil , nil , err
332
+ return result , err
275
333
}
276
- cleanup = func () {
334
+ result . Cleanup = func () {
277
335
os .RemoveAll (dir )
278
336
}
279
337
} else {
@@ -286,12 +344,12 @@ func generateBuildctlArgs(ctx context.Context, client *containerd.Client, option
286
344
}
287
345
dir , file , err = buildkitutil .BuildKitFile (dir , file )
288
346
if err != nil {
289
- return "" , nil , false , "" , nil , nil , err
347
+ return result , err
290
348
}
291
349
292
350
buildCtx , err := parseContextNames (options .ExtendedBuildContext )
293
351
if err != nil {
294
- return "" , nil , false , "" , nil , nil , err
352
+ return result , err
295
353
}
296
354
297
355
for k , v := range buildCtx {
@@ -306,7 +364,7 @@ func generateBuildctlArgs(ctx context.Context, client *containerd.Client, option
306
364
if isOCILayout := strings .HasPrefix (v , "oci-layout://" ); isOCILayout {
307
365
args , err := parseBuildContextFromOCILayout (k , v )
308
366
if err != nil {
309
- return "" , nil , false , "" , nil , nil , err
367
+ return result , err
310
368
}
311
369
312
370
buildctlArgs = append (buildctlArgs , args ... )
@@ -315,7 +373,7 @@ func generateBuildctlArgs(ctx context.Context, client *containerd.Client, option
315
373
316
374
path , err := filepath .Abs (v )
317
375
if err != nil {
318
- return "" , nil , false , "" , nil , nil , err
376
+ return result , err
319
377
}
320
378
buildctlArgs = append (buildctlArgs , fmt .Sprintf ("--local=%s=%s" , k , path ))
321
379
buildctlArgs = append (buildctlArgs , fmt .Sprintf ("--opt=context:%s=local:%s" , k , k ))
@@ -362,7 +420,7 @@ func generateBuildctlArgs(ctx context.Context, client *containerd.Client, option
362
420
}
363
421
}
364
422
} else {
365
- return "" , nil , false , "" , nil , nil , fmt .Errorf ("invalid build arg %q" , ba )
423
+ return result , fmt .Errorf ("invalid build arg %q" , ba )
366
424
}
367
425
}
368
426
@@ -405,7 +463,7 @@ func generateBuildctlArgs(ctx context.Context, client *containerd.Client, option
405
463
optAttestType := strings .TrimPrefix (optAttestType , "type=" )
406
464
buildctlArgs = append (buildctlArgs , fmt .Sprintf ("--opt=attest:%s=%s" , optAttestType , optAttestAttrs ))
407
465
} else {
408
- return "" , nil , false , "" , nil , nil , fmt .Errorf ("attestation type not specified" )
466
+ return result , fmt .Errorf ("attestation type not specified" )
409
467
}
410
468
}
411
469
@@ -434,11 +492,11 @@ func generateBuildctlArgs(ctx context.Context, client *containerd.Client, option
434
492
if options .IidFile != "" {
435
493
file , err := os .CreateTemp ("" , "buildkit-meta-*" )
436
494
if err != nil {
437
- return "" , nil , false , "" , nil , cleanup , err
495
+ return result , err
438
496
}
439
497
defer file .Close ()
440
- metaFile = file .Name ()
441
- buildctlArgs = append (buildctlArgs , "--metadata-file=" + metaFile )
498
+ result . MetaFile = file .Name ()
499
+ buildctlArgs = append (buildctlArgs , "--metadata-file=" + result . MetaFile )
442
500
}
443
501
444
502
if options .NetworkMode != "" {
@@ -453,7 +511,9 @@ func generateBuildctlArgs(ctx context.Context, client *containerd.Client, option
453
511
}
454
512
}
455
513
456
- return buildctlBinary , buildctlArgs , needsLoading , metaFile , tags , cleanup , nil
514
+ result .BuildctlArgs = buildctlArgs
515
+
516
+ return result , nil
457
517
}
458
518
459
519
func getDigestFromMetaFile (path string ) (string , error ) {
0 commit comments