Skip to content

Commit bb491e1

Browse files
authored
Merge pull request #6587 from tonistiigi/fix-push-progress
exporter: fix reporting push progress under export vertex
2 parents eaa4de0 + b333547 commit bb491e1

File tree

5 files changed

+129
-3
lines changed

5 files changed

+129
-3
lines changed

client/client_test.go

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,7 @@ var allTests = []func(t *testing.T, sb integration.Sandbox){
163163
testHostnameLookup,
164164
testHostnameSpecifying,
165165
testPushByDigest,
166+
testPushProgressSameVertex,
166167
testPullWithDigestCheck,
167168
testBasicInlineCacheImportExport,
168169
testExportBusyboxLocal,
@@ -1316,6 +1317,99 @@ func testPushByDigest(t *testing.T, sb integration.Sandbox) {
13161317
require.Greater(t, desc.Size, int64(0))
13171318
}
13181319

1320+
func testPushProgressSameVertex(t *testing.T, sb integration.Sandbox) {
1321+
workers.CheckFeatureCompat(t, sb, workers.FeatureDirectPush)
1322+
requiresLinux(t)
1323+
c, err := New(sb.Context(), sb.Address())
1324+
require.NoError(t, err)
1325+
defer c.Close()
1326+
1327+
registry, err := sb.NewRegistry()
1328+
if errors.Is(err, integration.ErrRequirements) {
1329+
t.Skip(err.Error())
1330+
}
1331+
require.NoError(t, err)
1332+
1333+
st := llb.Scratch().File(llb.Mkfile("foo", 0600, []byte("data")))
1334+
def, err := st.Marshal(sb.Context())
1335+
require.NoError(t, err)
1336+
1337+
imageName := registry + "/foo/bar:latest"
1338+
1339+
var vertexes []*Vertex
1340+
var statuses []*VertexStatus
1341+
ch := make(chan *SolveStatus)
1342+
eg, ctx := errgroup.WithContext(sb.Context())
1343+
eg.Go(func() error {
1344+
for {
1345+
select {
1346+
case <-ctx.Done():
1347+
return context.Cause(ctx)
1348+
case ss, ok := <-ch:
1349+
if !ok {
1350+
return nil
1351+
}
1352+
vertexes = append(vertexes, ss.Vertexes...)
1353+
statuses = append(statuses, ss.Statuses...)
1354+
}
1355+
}
1356+
})
1357+
eg.Go(func() error {
1358+
_, err := c.Solve(ctx, def, SolveOpt{
1359+
Exports: []ExportEntry{
1360+
{
1361+
Type: "image",
1362+
Attrs: map[string]string{
1363+
"name": imageName,
1364+
"push": "true",
1365+
},
1366+
},
1367+
},
1368+
}, ch)
1369+
return err
1370+
})
1371+
require.NoError(t, eg.Wait())
1372+
1373+
exportVertexes := map[digest.Digest]struct{}{}
1374+
pushVertexes := map[digest.Digest]struct{}{}
1375+
pushManifestPrefix := "pushing manifest for " + imageName + "@"
1376+
1377+
for _, v := range vertexes {
1378+
if v.Name == "exporting to image" {
1379+
exportVertexes[v.Digest] = struct{}{}
1380+
}
1381+
}
1382+
1383+
for _, st := range statuses {
1384+
switch {
1385+
case st.ID == "pushing layers":
1386+
pushVertexes[st.Vertex] = struct{}{}
1387+
case strings.HasPrefix(st.ID, pushManifestPrefix):
1388+
pushVertexes[st.Vertex] = struct{}{}
1389+
}
1390+
}
1391+
1392+
require.Len(t, exportVertexes, 1, "expected exactly one export vertex in status stream")
1393+
require.NotEmpty(t, pushVertexes, "expected at least one push status in status stream")
1394+
1395+
var exportVertex digest.Digest
1396+
for v := range exportVertexes {
1397+
exportVertex = v
1398+
}
1399+
for v := range pushVertexes {
1400+
require.Equal(t, exportVertex, v, "push statuses should use the same vertex as exporting to image")
1401+
}
1402+
1403+
hasPushManifest := false
1404+
for _, st := range statuses {
1405+
if strings.HasPrefix(st.ID, pushManifestPrefix) {
1406+
hasPushManifest = true
1407+
require.Equal(t, exportVertex, st.Vertex, "pushing manifest should use the same vertex as exporting to image")
1408+
}
1409+
}
1410+
require.True(t, hasPushManifest, "expected pushing manifest status in status stream")
1411+
}
1412+
13191413
func testPullWithDigestCheck(t *testing.T, sb integration.Sandbox) {
13201414
workers.CheckFeatureCompat(t, sb, workers.FeatureDirectPush)
13211415
requiresLinux(t)

cmd/buildctl/build_test.go

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import (
44
"bytes"
55
"context"
66
"encoding/json"
7+
"errors"
78
"fmt"
89
"io"
910
"os"
@@ -192,6 +193,28 @@ func testBuildMetadataFile(t *testing.T, sb integration.Sandbox) {
192193
}
193194
}
194195

196+
func testBuildPushProgress(t *testing.T, sb integration.Sandbox) {
197+
integration.SkipOnPlatform(t, "windows")
198+
registry, err := sb.NewRegistry()
199+
if errors.Is(err, integration.ErrRequirements) {
200+
t.Skip(err.Error())
201+
}
202+
require.NoError(t, err)
203+
204+
st := llb.Scratch().File(llb.Mkfile("foo", 0600, []byte("data")))
205+
rdr, err := marshal(sb.Context(), st)
206+
require.NoError(t, err)
207+
208+
imageName := registry + "/foo/bar:latest"
209+
cmd := sb.Cmd("build", "--progress=plain", "--output", "type=image,name="+imageName+",push=true")
210+
cmd.Stdin = rdr
211+
212+
dt, err := cmd.CombinedOutput()
213+
require.NoError(t, err, string(dt))
214+
require.Contains(t, string(dt), "pushing layers")
215+
require.Contains(t, string(dt), "pushing manifest for "+imageName+"@")
216+
}
217+
195218
func marshal(ctx context.Context, st llb.State) (io.Reader, error) {
196219
def, err := st.Marshal(ctx)
197220
if err != nil {

cmd/buildctl/buildctl_test.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ func TestCLIIntegration(t *testing.T) {
2323
testBuildLocalExporter,
2424
testBuildContainerdExporter,
2525
testBuildMetadataFile,
26+
testBuildPushProgress,
2627
testPrune,
2728
testUsage,
2829
),

solver/llbsolver/export.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,10 @@ func runInlineCacheExporter(ctx context.Context, e exporter.ExporterInstance, in
152152
return res, done(err)
153153
}
154154

155+
func exporterVertexID(sessionID string, exporterIndex int) string {
156+
return fmt.Sprint(sessionID, "-export-", exporterIndex)
157+
}
158+
155159
func (s *Solver) runExporters(ctx context.Context, ref string, exporters []exporter.ExporterInstance, inlineCacheExporter inlineCacheExporter, job *solver.Job, cached *result.Result[solver.CachedResult], inp *exporter.Source) (exporterResponse map[string]string, finalizers []exporter.FinalizeFunc, descrefs []exporter.DescriptorReference, err error) {
156160
warnings, err := verifier.CheckInvalidPlatforms(ctx, inp)
157161
if err != nil {
@@ -164,8 +168,8 @@ func (s *Solver) runExporters(ctx context.Context, ref string, exporters []expor
164168
descs := make([]exporter.DescriptorReference, len(exporters))
165169
var inlineCacheMu sync.Mutex
166170
for i, exp := range exporters {
171+
id := exporterVertexID(job.SessionID, i)
167172
eg.Go(func() error {
168-
id := fmt.Sprint(job.SessionID, "-export-", i)
169173
return inBuilderContext(ctx, job, exp.Name(), id, func(ctx context.Context, _ solver.JobContext) error {
170174
span, ctx := tracing.StartSpan(ctx, exp.Name())
171175
defer span.End()

solver/llbsolver/solver.go

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -345,12 +345,16 @@ func (s *Solver) Solve(ctx context.Context, id string, sessionID string, req fro
345345
// Image Export has already created layers in the content store,
346346
// so cache exporters can see and reuse them.
347347
eg, egCtx := errgroup.WithContext(ctx)
348-
for _, finalize := range finalizers {
348+
for i, finalize := range finalizers {
349349
if finalize == nil {
350350
continue
351351
}
352+
name := exp.Exporters[i].Name()
353+
id := exporterVertexID(j.SessionID, i)
352354
eg.Go(func() error {
353-
return finalize(egCtx)
355+
return inBuilderContext(egCtx, j, name, id, func(ctx context.Context, _ solver.JobContext) error {
356+
return finalize(ctx)
357+
})
354358
})
355359
}
356360
var cacheExporterResponse map[string]string

0 commit comments

Comments
 (0)