@@ -19,6 +19,8 @@ import (
1919var historyTests = []func (t * testing.T , sb integration.Sandbox ){
2020 testHistoryExport ,
2121 testHistoryExportFinalize ,
22+ testHistoryExportFinalizeMultiNodeRef ,
23+ testHistoryExportFinalizeMultiNodeAll ,
2224 testHistoryInspect ,
2325 testHistoryLs ,
2426 testHistoryRm ,
@@ -29,21 +31,55 @@ var historyTests = []func(t *testing.T, sb integration.Sandbox){
2931func testHistoryExport (t * testing.T , sb integration.Sandbox ) {
3032 ref := buildTestProject (t , sb )
3133 require .NotEmpty (t , ref .Ref )
34+ requireHistoryRef (t , sb , ref .Ref )
3235
3336 outFile := path .Join (t .TempDir (), "export.dockerbuild" )
3437 cmd := buildxCmd (sb , withArgs ("history" , "export" , ref .Ref , "--output" , outFile ))
35- out , err := cmd .Output ()
38+ out , err := cmd .CombinedOutput ()
3639 require .NoError (t , err , string (out ))
3740 require .FileExists (t , outFile )
3841}
3942
4043func testHistoryExportFinalize (t * testing.T , sb integration.Sandbox ) {
4144 ref := buildTestProject (t , sb )
4245 require .NotEmpty (t , ref .Ref )
46+ requireHistoryRef (t , sb , ref .Ref )
4347
4448 outFile := path .Join (t .TempDir (), "export.dockerbuild" )
4549 cmd := buildxCmd (sb , withArgs ("history" , "export" , ref .Ref , "--finalize" , "--output" , outFile ))
46- out , err := cmd .Output ()
50+ out , err := cmd .CombinedOutput ()
51+ require .NoError (t , err , string (out ))
52+ require .FileExists (t , outFile )
53+ }
54+
55+ func testHistoryExportFinalizeMultiNodeRef (t * testing.T , sb integration.Sandbox ) {
56+ if ! isRemoteMultiNodeWorker (sb ) {
57+ t .Skip ("only testing with remote multi-node worker" )
58+ }
59+
60+ ref := buildTestProject (t , sb , withArgs ("--platform=linux/amd64,linux/arm64" , "--output=type=cacheonly" ))
61+ require .NotEmpty (t , ref .Ref )
62+ requireHistoryRef (t , sb , ref .Ref )
63+
64+ outFile := path .Join (t .TempDir (), "export.dockerbuild" )
65+ cmd := buildxCmd (sb , withArgs ("history" , "export" , ref .Ref , "--finalize" , "--output" , outFile ))
66+ out , err := cmd .CombinedOutput ()
67+ require .NoError (t , err , string (out ))
68+ require .FileExists (t , outFile )
69+ }
70+
71+ func testHistoryExportFinalizeMultiNodeAll (t * testing.T , sb integration.Sandbox ) {
72+ if ! isRemoteMultiNodeWorker (sb ) {
73+ t .Skip ("only testing with remote multi-node worker" )
74+ }
75+
76+ ref := buildTestProject (t , sb , withArgs ("--platform=linux/amd64,linux/arm64" , "--output=type=cacheonly" ))
77+ require .NotEmpty (t , ref .Ref )
78+ requireHistoryRef (t , sb , ref .Ref )
79+
80+ outFile := path .Join (t .TempDir (), "export.dockerbuild" )
81+ cmd := buildxCmd (sb , withArgs ("history" , "export" , "--finalize" , "--all" , "--output" , outFile ))
82+ out , err := cmd .CombinedOutput ()
4783 require .NoError (t , err , string (out ))
4884 require .FileExists (t , outFile )
4985}
@@ -53,7 +89,7 @@ func testHistoryInspect(t *testing.T, sb integration.Sandbox) {
5389 require .NotEmpty (t , ref .Ref )
5490
5591 cmd := buildxCmd (sb , withArgs ("history" , "inspect" , ref .Ref , "--format=json" ))
56- out , err := cmd .Output ()
92+ out , err := cmd .CombinedOutput ()
5793 require .NoError (t , err , string (out ))
5894
5995 type recT struct {
@@ -77,31 +113,10 @@ func testHistoryInspect(t *testing.T, sb integration.Sandbox) {
77113}
78114
79115func testHistoryLs (t * testing.T , sb integration.Sandbox ) {
80- if isRemoteMultiNodeWorker (sb ) {
81- // FIXME: "history ls" fails on multi nodes
82- t .Skip ("fails with multi nodes" )
83- }
84-
85116 ref := buildTestProject (t , sb )
86117 require .NotEmpty (t , ref .Ref )
87118
88- cmd := buildxCmd (sb , withArgs ("history" , "ls" , "--filter=ref=" + ref .Ref , "--format=json" ))
89- out , err := cmd .Output ()
90- require .NoError (t , err , string (out ))
91-
92- type recT struct {
93- Ref string `json:"ref"`
94- Name string `json:"name"`
95- Status string `json:"status"`
96- CreatedAt * time.Time `json:"created_at"`
97- CompletedAt * time.Time `json:"completed_at"`
98- TotalSteps int32 `json:"total_steps"`
99- CompletedSteps int32 `json:"completed_steps"`
100- CachedSteps int32 `json:"cached_steps"`
101- }
102- var rec recT
103- err = json .Unmarshal (out , & rec )
104- require .NoError (t , err )
119+ rec := requireHistoryRecord (t , sb , ref .String ())
105120 require .Equal (t , ref .String (), rec .Ref )
106121 require .NotEmpty (t , rec .Name )
107122}
@@ -146,15 +161,10 @@ func testHistoryLsStoppedBuilder(t *testing.T, sb integration.Sandbox) {
146161}
147162
148163func testHistoryBuildName (t * testing.T , sb integration.Sandbox ) {
149- if isRemoteMultiNodeWorker (sb ) {
150- // FIXME: "history ls" fails on multi nodes
151- t .Skip ("fails with multi nodes" )
152- }
153-
154164 t .Run ("override" , func (t * testing.T ) {
155165 dir := createTestProject (t )
156166 out , err := buildCmd (sb , withArgs ("--build-arg=BUILDKIT_BUILD_NAME=foobar" , "--metadata-file" , filepath .Join (dir , "md.json" ), dir ))
157- require .NoError (t , err , string ( out ) )
167+ require .NoError (t , err , out )
158168
159169 dt , err := os .ReadFile (filepath .Join (dir , "md.json" ))
160170 require .NoError (t , err )
@@ -169,23 +179,7 @@ func testHistoryBuildName(t *testing.T, sb integration.Sandbox) {
169179 refParts := strings .Split (md .BuildRef , "/" )
170180 require .Len (t , refParts , 3 )
171181
172- cmd := buildxCmd (sb , withArgs ("history" , "ls" , "--filter=ref=" + refParts [2 ], "--format=json" ))
173- bout , err := cmd .Output ()
174- require .NoError (t , err , string (bout ))
175-
176- type recT struct {
177- Ref string `json:"ref"`
178- Name string `json:"name"`
179- Status string `json:"status"`
180- CreatedAt * time.Time `json:"created_at"`
181- CompletedAt * time.Time `json:"completed_at"`
182- TotalSteps int32 `json:"total_steps"`
183- CompletedSteps int32 `json:"completed_steps"`
184- CachedSteps int32 `json:"cached_steps"`
185- }
186- var rec recT
187- err = json .Unmarshal (bout , & rec )
188- require .NoError (t , err )
182+ rec := requireHistoryRecord (t , sb , md .BuildRef )
189183 require .Equal (t , md .BuildRef , rec .Ref )
190184 require .Equal (t , "foobar" , rec .Name )
191185 })
@@ -230,23 +224,7 @@ COPY foo /foo
230224 refParts := strings .Split (md .BuildRef , "/" )
231225 require .Len (t , refParts , 3 )
232226
233- cmd := buildxCmd (sb , withArgs ("history" , "ls" , "--filter=ref=" + refParts [2 ], "--format=json" ))
234- bout , err := cmd .Output ()
235- require .NoError (t , err , string (bout ))
236-
237- type recT struct {
238- Ref string `json:"ref"`
239- Name string `json:"name"`
240- Status string `json:"status"`
241- CreatedAt * time.Time `json:"created_at"`
242- CompletedAt * time.Time `json:"completed_at"`
243- TotalSteps int32 `json:"total_steps"`
244- CompletedSteps int32 `json:"completed_steps"`
245- CachedSteps int32 `json:"cached_steps"`
246- }
247- var rec recT
248- err = json .Unmarshal (bout , & rec )
249- require .NoError (t , err )
227+ rec := requireHistoryRecord (t , sb , md .BuildRef )
250228 require .Equal (t , md .BuildRef , rec .Ref )
251229 require .Equal (t , addr + "#main" , rec .Name )
252230 })
296274 refParts := strings .Split (md .Default .BuildRef , "/" )
297275 require .Len (t , refParts , 3 )
298276
299- cmd := buildxCmd (sb , withArgs ("history" , "ls" , "--filter=ref=" + refParts [2 ], "--format=json" ))
300- bout , err := cmd .Output ()
301- require .NoError (t , err , string (bout ))
302-
303- type recT struct {
304- Ref string `json:"ref"`
305- Name string `json:"name"`
306- Status string `json:"status"`
307- CreatedAt * time.Time `json:"created_at"`
308- CompletedAt * time.Time `json:"completed_at"`
309- TotalSteps int32 `json:"total_steps"`
310- CompletedSteps int32 `json:"completed_steps"`
311- CachedSteps int32 `json:"cached_steps"`
312- }
313- var rec recT
314- err = json .Unmarshal (bout , & rec )
315- require .NoError (t , err )
277+ rec := requireHistoryRecord (t , sb , md .Default .BuildRef )
316278 require .Equal (t , md .Default .BuildRef , rec .Ref )
317279 require .Equal (t , addr , rec .Name )
318280 })
@@ -328,10 +290,11 @@ func (b buildRef) String() string {
328290 return b .Builder + "/" + b .Node + "/" + b .Ref
329291}
330292
331- func buildTestProject (t * testing.T , sb integration.Sandbox ) buildRef {
293+ func buildTestProject (t * testing.T , sb integration.Sandbox , opts ... cmdOpt ) buildRef {
332294 dir := createTestProject (t )
333- out , err := buildCmd (sb , withArgs ("--metadata-file" , filepath .Join (dir , "md.json" ), dir ))
334- require .NoError (t , err , string (out ))
295+ opts = append (opts , withArgs ("--metadata-file" , filepath .Join (dir , "md.json" ), dir ))
296+ out , err := buildCmd (sb , opts ... )
297+ require .NoError (t , err , out )
335298
336299 dt , err := os .ReadFile (filepath .Join (dir , "md.json" ))
337300 require .NoError (t , err )
@@ -352,3 +315,52 @@ func buildTestProject(t *testing.T, sb integration.Sandbox) buildRef {
352315 Ref : refParts [2 ],
353316 }
354317}
318+
319+ func requireHistoryRef (t * testing.T , sb integration.Sandbox , ref string ) {
320+ cmd := buildxCmd (sb , withArgs ("history" , "ls" , "--format={{.Ref}}" ))
321+ out , err := cmd .CombinedOutput ()
322+ require .NoError (t , err , string (out ))
323+ var matches int
324+ for line := range strings .SplitSeq (strings .TrimSpace (string (out )), "\n " ) {
325+ if strings .TrimSpace (line ) == ref {
326+ matches ++
327+ }
328+ }
329+ require .GreaterOrEqual (t , matches , 1 )
330+ }
331+
332+ type historyLsRecord struct {
333+ Ref string `json:"ref"`
334+ Name string `json:"name"`
335+ Status string `json:"status"`
336+ CreatedAt * time.Time `json:"created_at"`
337+ CompletedAt * time.Time `json:"completed_at"`
338+ TotalSteps int32 `json:"total_steps"`
339+ CompletedSteps int32 `json:"completed_steps"`
340+ CachedSteps int32 `json:"cached_steps"`
341+ }
342+
343+ func requireHistoryRecord (t * testing.T , sb integration.Sandbox , ref string , opts ... cmdOpt ) historyLsRecord {
344+ cmd := buildxCmd (sb , append (opts , withArgs ("history" , "ls" , "--format=json" ))... )
345+ out , err := cmd .CombinedOutput ()
346+ require .NoError (t , err , string (out ))
347+
348+ for line := range strings .SplitSeq (strings .TrimSpace (string (out )), "\n " ) {
349+ line = strings .TrimSpace (line )
350+ if line == "" {
351+ continue
352+ }
353+ if ! strings .HasPrefix (line , "{" ) {
354+ continue
355+ }
356+ var rec historyLsRecord
357+ err := json .Unmarshal ([]byte (line ), & rec )
358+ require .NoError (t , err )
359+ if rec .Ref == ref {
360+ return rec
361+ }
362+ }
363+
364+ require .Failf (t , "history record not found" , "ref %q was not found in history ls output:\n %s" , ref , string (out ))
365+ return historyLsRecord {}
366+ }
0 commit comments