@@ -61,18 +61,22 @@ func (p platformParser) DefaultSpec() platforms.Platform {
6161}
6262
6363func 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 )
6565 if err != nil {
6666 return err
6767 }
68- if cleanup != nil {
69- defer cleanup ()
68+ if buildCtlArgs . Cleanup != nil {
69+ defer buildCtlArgs . Cleanup ()
7070 }
7171
72+ buildctlBinary := buildCtlArgs .BuildctlBinary
73+ buildctlArgs := buildCtlArgs .BuildctlArgs
74+
7275 log .L .Debugf ("running %s %v" , buildctlBinary , buildctlArgs )
7376 buildctlCmd := exec .Command (buildctlBinary , buildctlArgs ... )
7477 buildctlCmd .Env = os .Environ ()
7578
79+ needsLoading := buildCtlArgs .NeedsLoading
7680 var buildctlStdout io.Reader
7781 if needsLoading {
7882 buildctlStdout , err = buildctlCmd .StdoutPipe ()
@@ -95,6 +99,26 @@ func Build(ctx context.Context, client *containerd.Client, options types.Builder
9599 if err != nil {
96100 return err
97101 }
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
98122 if err = loadImage (ctx , buildctlStdout , options .GOptions .Namespace , options .GOptions .Address , options .GOptions .Snapshotter , options .Stdout , platMC , options .Quiet ); err != nil {
99123 return err
100124 }
@@ -105,7 +129,7 @@ func Build(ctx context.Context, client *containerd.Client, options types.Builder
105129 }
106130
107131 if options .IidFile != "" {
108- id , err := getDigestFromMetaFile (metaFile )
132+ id , err := getDigestFromMetaFile (buildCtlArgs . MetaFile )
109133 if err != nil {
110134 return err
111135 }
@@ -114,6 +138,7 @@ func Build(ctx context.Context, client *containerd.Client, options types.Builder
114138 }
115139 }
116140
141+ tags := buildCtlArgs .Tags
117142 if len (tags ) > 1 {
118143 log .L .Debug ("Found more than 1 tag" )
119144 imageService := client .ImageService ()
@@ -160,7 +185,11 @@ func loadImage(ctx context.Context, in io.Reader, namespace, address, snapshotte
160185 client .Close ()
161186 }()
162187 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+ )
164193 if err != nil {
165194 if r .N == 0 {
166195 // Avoid confusing "unrecognized image format"
@@ -192,23 +221,34 @@ func loadImage(ctx context.Context, in io.Reader, namespace, address, snapshotte
192221 return nil
193222}
194223
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+ }
197233
234+ func generateBuildctlArgs (ctx context.Context , client * containerd.Client , options types.BuilderBuildOptions ) (result BuildctlArgsResult , err error ) {
198235 buildctlBinary , err := buildkitutil .BuildctlBinary ()
199236 if err != nil {
200- return "" , nil , false , "" , nil , nil , err
237+ return result , err
201238 }
239+ result .BuildctlBinary = buildctlBinary
240+
241+ var defaultDest string
202242
203243 output := options .Output
204244 if output == "" {
205245 info , err := client .Server (ctx )
206246 if err != nil {
207- return "" , nil , false , "" , nil , nil , err
247+ return result , err
208248 }
209249 sharable , err := isImageSharable (options .BuildKitHost , options .GOptions .Namespace , info .UUID , options .GOptions .Snapshotter , options .Platform )
210250 if err != nil {
211- return "" , nil , false , "" , nil , nil , err
251+ return result , err
212252 }
213253 if sharable {
214254 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
219259 // TODO: consider using type=oci for single-options.Platform build too
220260 output = "type=oci"
221261 }
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 )
223270 }
224271 } else {
225272 if ! strings .Contains (output , "type=" ) {
@@ -229,32 +276,43 @@ func generateBuildctlArgs(ctx context.Context, client *containerd.Client, option
229276 }
230277 if strings .Contains (output , "type=docker" ) || strings .Contains (output , "type=oci" ) {
231278 if ! strings .Contains (output , "dest=" ) {
232- needsLoading = true
279+ result . NeedsLoading = true
233280 }
234281 }
235282 }
283+
284+ var tags []string
236285 if tags = strutil .DedupeStrSlice (options .Tag ); len (tags ) > 0 {
237286 ref := tags [0 ]
238287 parsedReference , err := referenceutil .Parse (ref )
239288 if err != nil {
240- return "" , nil , false , "" , nil , nil , err
289+ return result , err
241290 }
242291 output += ",name=" + parsedReference .String ()
243292
244293 // pick the first tag and add it to output
245294 for idx , tag := range tags {
246295 parsedReference , err = referenceutil .Parse (tag )
247296 if err != nil {
248- return "" , nil , false , "" , nil , nil , err
297+ return result , err
249298 }
250299 tags [idx ] = parsedReference .String ()
251300 }
252301 } else if len (tags ) == 0 {
253302 output = output + ",dangling-name-prefix=<none>"
254303 }
304+ result .Tags = tags
255305
256- buildctlArgs = buildkitutil .BuildctlBaseArgs (options .BuildKitHost )
306+ // Add default destination file to output
307+ output += defaultDest
257308
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 )
258316 buildctlArgs = append (buildctlArgs , []string {
259317 "build" ,
260318 "--progress=" + options .Progress ,
@@ -271,9 +329,9 @@ func generateBuildctlArgs(ctx context.Context, client *containerd.Client, option
271329 var err error
272330 dir , err = buildkitutil .WriteTempDockerfile (options .Stdin )
273331 if err != nil {
274- return "" , nil , false , "" , nil , nil , err
332+ return result , err
275333 }
276- cleanup = func () {
334+ result . Cleanup = func () {
277335 os .RemoveAll (dir )
278336 }
279337 } else {
@@ -286,12 +344,12 @@ func generateBuildctlArgs(ctx context.Context, client *containerd.Client, option
286344 }
287345 dir , file , err = buildkitutil .BuildKitFile (dir , file )
288346 if err != nil {
289- return "" , nil , false , "" , nil , nil , err
347+ return result , err
290348 }
291349
292350 buildCtx , err := parseContextNames (options .ExtendedBuildContext )
293351 if err != nil {
294- return "" , nil , false , "" , nil , nil , err
352+ return result , err
295353 }
296354
297355 for k , v := range buildCtx {
@@ -306,7 +364,7 @@ func generateBuildctlArgs(ctx context.Context, client *containerd.Client, option
306364 if isOCILayout := strings .HasPrefix (v , "oci-layout://" ); isOCILayout {
307365 args , err := parseBuildContextFromOCILayout (k , v )
308366 if err != nil {
309- return "" , nil , false , "" , nil , nil , err
367+ return result , err
310368 }
311369
312370 buildctlArgs = append (buildctlArgs , args ... )
@@ -315,7 +373,7 @@ func generateBuildctlArgs(ctx context.Context, client *containerd.Client, option
315373
316374 path , err := filepath .Abs (v )
317375 if err != nil {
318- return "" , nil , false , "" , nil , nil , err
376+ return result , err
319377 }
320378 buildctlArgs = append (buildctlArgs , fmt .Sprintf ("--local=%s=%s" , k , path ))
321379 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
362420 }
363421 }
364422 } else {
365- return "" , nil , false , "" , nil , nil , fmt .Errorf ("invalid build arg %q" , ba )
423+ return result , fmt .Errorf ("invalid build arg %q" , ba )
366424 }
367425 }
368426
@@ -405,7 +463,7 @@ func generateBuildctlArgs(ctx context.Context, client *containerd.Client, option
405463 optAttestType := strings .TrimPrefix (optAttestType , "type=" )
406464 buildctlArgs = append (buildctlArgs , fmt .Sprintf ("--opt=attest:%s=%s" , optAttestType , optAttestAttrs ))
407465 } else {
408- return "" , nil , false , "" , nil , nil , fmt .Errorf ("attestation type not specified" )
466+ return result , fmt .Errorf ("attestation type not specified" )
409467 }
410468 }
411469
@@ -434,11 +492,11 @@ func generateBuildctlArgs(ctx context.Context, client *containerd.Client, option
434492 if options .IidFile != "" {
435493 file , err := os .CreateTemp ("" , "buildkit-meta-*" )
436494 if err != nil {
437- return "" , nil , false , "" , nil , cleanup , err
495+ return result , err
438496 }
439497 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 )
442500 }
443501
444502 if options .NetworkMode != "" {
@@ -453,7 +511,9 @@ func generateBuildctlArgs(ctx context.Context, client *containerd.Client, option
453511 }
454512 }
455513
456- return buildctlBinary , buildctlArgs , needsLoading , metaFile , tags , cleanup , nil
514+ result .BuildctlArgs = buildctlArgs
515+
516+ return result , nil
457517}
458518
459519func getDigestFromMetaFile (path string ) (string , error ) {
0 commit comments