From 553d3eafda99758ee2a45094752dbb29ee1d4677 Mon Sep 17 00:00:00 2001 From: Erik Sipsma Date: Tue, 1 Feb 2022 13:47:56 -0800 Subject: [PATCH 1/5] Use parentheses for merge+diff vertex names. This is easier to read than quotes, which result in lots of escaping in progress output. Signed-off-by: Erik Sipsma --- solver/llbsolver/vertex.go | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/solver/llbsolver/vertex.go b/solver/llbsolver/vertex.go index 721410c8be57..0c9460f969bd 100644 --- a/solver/llbsolver/vertex.go +++ b/solver/llbsolver/vertex.go @@ -2,7 +2,6 @@ package llbsolver import ( "fmt" - "strconv" "strings" "github.com/containerd/containerd/platforms" @@ -271,9 +270,9 @@ func llbOpName(pbOp *pb.Op, load func(digest.Digest) (solver.Vertex, error)) (st if err != nil { return "", err } - subnames[i] = strconv.Quote(subvtx.Name()) + subnames[i] = subvtx.Name() } - return "merge " + strings.Join(subnames, " + "), nil + return "merge " + fmt.Sprintf("(%s)", strings.Join(subnames, ", ")), nil case *pb.Op_Diff: var lowerName string if op.Diff.Lower.Input == -1 { @@ -283,7 +282,7 @@ func llbOpName(pbOp *pb.Op, load func(digest.Digest) (solver.Vertex, error)) (st if err != nil { return "", err } - lowerName = strconv.Quote(lowerVtx.Name()) + lowerName = fmt.Sprintf("(%s)", lowerVtx.Name()) } var upperName string if op.Diff.Upper.Input == -1 { @@ -293,7 +292,7 @@ func llbOpName(pbOp *pb.Op, load func(digest.Digest) (solver.Vertex, error)) (st if err != nil { return "", err } - upperName = strconv.Quote(upperVtx.Name()) + upperName = fmt.Sprintf("(%s)", upperVtx.Name()) } return "diff " + lowerName + " -> " + upperName, nil default: From bb09f3c03274a113a5f14f10d6fa9a99ae1138cc Mon Sep 17 00:00:00 2001 From: Erik Sipsma Date: Thu, 3 Feb 2022 10:54:24 -0800 Subject: [PATCH 2/5] Improve progress output for merge+diff ops. Now, when a merge or diff ref is unlazied, the progress will show up under the vertex for the merge/diff ref. Additionally, any ancestors of the op that also need to be unlazied as part of unlazying the merge/diff will show status updates under its vertex in the progress. Signed-off-by: Erik Sipsma --- cache/contenthash/checksum_test.go | 4 +- cache/manager.go | 61 +++++++++++++++--------------- cache/manager_test.go | 40 ++++++++++---------- cache/opts.go | 2 + cache/refs.go | 60 ++++++++++++++++++++--------- solver/cacheopts.go | 10 ++--- solver/llbsolver/ops/diff.go | 26 +++++++------ solver/llbsolver/ops/merge.go | 22 +++++++---- solver/llbsolver/solver.go | 2 +- source/git/gitsource.go | 2 +- source/http/httpsource.go | 2 +- worker/base/worker.go | 18 +++++++-- 12 files changed, 150 insertions(+), 99 deletions(-) diff --git a/cache/contenthash/checksum_test.go b/cache/contenthash/checksum_test.go index 08dca5c15903..248a99ca6802 100644 --- a/cache/contenthash/checksum_test.go +++ b/cache/contenthash/checksum_test.go @@ -1142,7 +1142,7 @@ func TestPersistence(t *testing.T) { err = ref.Release(context.TODO()) require.NoError(t, err) - ref, err = cm.Get(context.TODO(), id) + ref, err = cm.Get(context.TODO(), id, nil) require.NoError(t, err) dgst, err = Checksum(context.TODO(), ref, "foo", ChecksumOpts{FollowLinks: true}, nil) @@ -1162,7 +1162,7 @@ func TestPersistence(t *testing.T) { defer closeBolt() defer cm.Close() - ref, err = cm.Get(context.TODO(), id) + ref, err = cm.Get(context.TODO(), id, nil) require.NoError(t, err) dgst, err = Checksum(context.TODO(), ref, "foo", ChecksumOpts{FollowLinks: true}, nil) diff --git a/cache/manager.go b/cache/manager.go index b56cda53aa69..cadc32f10cf7 100644 --- a/cache/manager.go +++ b/cache/manager.go @@ -22,6 +22,7 @@ import ( "github.com/moby/buildkit/snapshot" "github.com/moby/buildkit/util/bklog" "github.com/moby/buildkit/util/flightcontrol" + "github.com/moby/buildkit/util/progress" digest "github.com/opencontainers/go-digest" imagespecidentity "github.com/opencontainers/image-spec/identity" ocispecs "github.com/opencontainers/image-spec/specs-go/v1" @@ -51,13 +52,13 @@ type Accessor interface { MetadataStore GetByBlob(ctx context.Context, desc ocispecs.Descriptor, parent ImmutableRef, opts ...RefOption) (ImmutableRef, error) - Get(ctx context.Context, id string, opts ...RefOption) (ImmutableRef, error) + Get(ctx context.Context, id string, pg progress.Controller, opts ...RefOption) (ImmutableRef, error) New(ctx context.Context, parent ImmutableRef, s session.Group, opts ...RefOption) (MutableRef, error) GetMutable(ctx context.Context, id string, opts ...RefOption) (MutableRef, error) // Rebase? IdentityMapping() *idtools.IdentityMapping - Merge(ctx context.Context, parents []ImmutableRef, opts ...RefOption) (ImmutableRef, error) - Diff(ctx context.Context, lower, upper ImmutableRef, opts ...RefOption) (ImmutableRef, error) + Merge(ctx context.Context, parents []ImmutableRef, pg progress.Controller, opts ...RefOption) (ImmutableRef, error) + Diff(ctx context.Context, lower, upper ImmutableRef, pg progress.Controller, opts ...RefOption) (ImmutableRef, error) } type Controller interface { @@ -134,7 +135,7 @@ func (cm *cacheManager) GetByBlob(ctx context.Context, desc ocispecs.Descriptor, var p *immutableRef if parent != nil { - p2, err := cm.Get(ctx, parent.ID(), NoUpdateLastUsed, descHandlers) + p2, err := cm.Get(ctx, parent.ID(), nil, NoUpdateLastUsed, descHandlers) if err != nil { return nil, err } @@ -169,7 +170,7 @@ func (cm *cacheManager) GetByBlob(ctx context.Context, desc ocispecs.Descriptor, } for _, si := range sis { - ref, err := cm.get(ctx, si.ID(), opts...) + ref, err := cm.get(ctx, si.ID(), nil, opts...) if err != nil { if errors.As(err, &NeedsRemoteProviderError{}) { // This shouldn't happen and indicates that blobchain IDs are being set incorrectly, @@ -199,7 +200,7 @@ func (cm *cacheManager) GetByBlob(ctx context.Context, desc ocispecs.Descriptor, var link *immutableRef for _, si := range sis { - ref, err := cm.get(ctx, si.ID(), opts...) + ref, err := cm.get(ctx, si.ID(), nil, opts...) // if the error was NotFound or NeedsRemoteProvider, we can't re-use the snapshot from the blob so just skip it if err != nil && !IsNotFound(err) && !errors.As(err, &NeedsRemoteProviderError{}) { return nil, errors.Wrapf(err, "failed to get record %s by chainid", si.ID()) @@ -290,7 +291,7 @@ func (cm *cacheManager) GetByBlob(ctx context.Context, desc ocispecs.Descriptor, cm.records[id] = rec - return rec.ref(true, descHandlers), nil + return rec.ref(true, descHandlers, nil), nil } // init loads all snapshots from metadata state and tries to load the records @@ -324,14 +325,14 @@ func (cm *cacheManager) Close() error { } // Get returns an immutable snapshot reference for ID -func (cm *cacheManager) Get(ctx context.Context, id string, opts ...RefOption) (ImmutableRef, error) { +func (cm *cacheManager) Get(ctx context.Context, id string, pg progress.Controller, opts ...RefOption) (ImmutableRef, error) { cm.mu.Lock() defer cm.mu.Unlock() - return cm.get(ctx, id, opts...) + return cm.get(ctx, id, pg, opts...) } // get requires manager lock to be taken -func (cm *cacheManager) get(ctx context.Context, id string, opts ...RefOption) (*immutableRef, error) { +func (cm *cacheManager) get(ctx context.Context, id string, pg progress.Controller, opts ...RefOption) (*immutableRef, error) { rec, err := cm.getRecord(ctx, id, opts...) if err != nil { return nil, err @@ -353,12 +354,12 @@ func (cm *cacheManager) get(ctx context.Context, id string, opts ...RefOption) ( return nil, errors.Wrapf(ErrLocked, "%s is locked", id) } if rec.equalImmutable != nil { - return rec.equalImmutable.ref(triggerUpdate, descHandlers), nil + return rec.equalImmutable.ref(triggerUpdate, descHandlers, pg), nil } return rec.mref(triggerUpdate, descHandlers).commit(ctx) } - return rec.ref(triggerUpdate, descHandlers), nil + return rec.ref(triggerUpdate, descHandlers, pg), nil } // getRecord returns record for id. Requires manager lock. @@ -486,7 +487,7 @@ func (cm *cacheManager) getRecord(ctx context.Context, id string, opts ...RefOpt func (cm *cacheManager) parentsOf(ctx context.Context, md *cacheMetadata, opts ...RefOption) (ps parentRefs, rerr error) { if parentID := md.getParent(); parentID != "" { - p, err := cm.get(ctx, parentID, append(opts, NoUpdateLastUsed)) + p, err := cm.get(ctx, parentID, nil, append(opts, NoUpdateLastUsed)) if err != nil { return ps, err } @@ -494,14 +495,14 @@ func (cm *cacheManager) parentsOf(ctx context.Context, md *cacheMetadata, opts . return ps, nil } for _, parentID := range md.getMergeParents() { - p, err := cm.get(ctx, parentID, append(opts, NoUpdateLastUsed)) + p, err := cm.get(ctx, parentID, nil, append(opts, NoUpdateLastUsed)) if err != nil { return ps, err } ps.mergeParents = append(ps.mergeParents, p) } if lowerParentID := md.getLowerDiffParent(); lowerParentID != "" { - p, err := cm.get(ctx, lowerParentID, append(opts, NoUpdateLastUsed)) + p, err := cm.get(ctx, lowerParentID, nil, append(opts, NoUpdateLastUsed)) if err != nil { return ps, err } @@ -511,7 +512,7 @@ func (cm *cacheManager) parentsOf(ctx context.Context, md *cacheMetadata, opts . ps.diffParents.lower = p } if upperParentID := md.getUpperDiffParent(); upperParentID != "" { - p, err := cm.get(ctx, upperParentID, append(opts, NoUpdateLastUsed)) + p, err := cm.get(ctx, upperParentID, nil, append(opts, NoUpdateLastUsed)) if err != nil { return ps, err } @@ -532,7 +533,7 @@ func (cm *cacheManager) New(ctx context.Context, s ImmutableRef, sess session.Gr if _, ok := s.(*immutableRef); ok { parent = s.Clone().(*immutableRef) } else { - p, err := cm.Get(ctx, s.ID(), append(opts, NoUpdateLastUsed)...) + p, err := cm.Get(ctx, s.ID(), nil, append(opts, NoUpdateLastUsed)...) if err != nil { return nil, err } @@ -661,7 +662,7 @@ func (cm *cacheManager) GetMutable(ctx context.Context, id string, opts ...RefOp return rec.mref(true, descHandlersOf(opts...)), nil } -func (cm *cacheManager) Merge(ctx context.Context, inputParents []ImmutableRef, opts ...RefOption) (ir ImmutableRef, rerr error) { +func (cm *cacheManager) Merge(ctx context.Context, inputParents []ImmutableRef, pg progress.Controller, opts ...RefOption) (ir ImmutableRef, rerr error) { // TODO:(sipsma) optimize merge further by // * Removing repeated occurrences of input layers (only leaving the uppermost) // * Reusing existing merges that are equivalent to this one @@ -687,7 +688,7 @@ func (cm *cacheManager) Merge(ctx context.Context, inputParents []ImmutableRef, } else { // inputParent implements ImmutableRef but isn't our internal struct, get an instance of the internal struct // by calling Get on its ID. - p, err := cm.Get(ctx, inputParent.ID(), append(opts, NoUpdateLastUsed)...) + p, err := cm.Get(ctx, inputParent.ID(), nil, append(opts, NoUpdateLastUsed)...) if err != nil { return nil, err } @@ -710,14 +711,14 @@ func (cm *cacheManager) Merge(ctx context.Context, inputParents []ImmutableRef, } // On success, createMergeRef takes ownership of parents - mergeRef, err := cm.createMergeRef(ctx, parents, dhs, opts...) + mergeRef, err := cm.createMergeRef(ctx, parents, dhs, pg, opts...) if err != nil { return nil, err } return mergeRef, nil } -func (cm *cacheManager) createMergeRef(ctx context.Context, parents parentRefs, dhs DescHandlers, opts ...RefOption) (ir *immutableRef, rerr error) { +func (cm *cacheManager) createMergeRef(ctx context.Context, parents parentRefs, dhs DescHandlers, pg progress.Controller, opts ...RefOption) (ir *immutableRef, rerr error) { if len(parents.mergeParents) == 0 { // merge of nothing is nothing return nil, nil @@ -789,10 +790,10 @@ func (cm *cacheManager) createMergeRef(ctx context.Context, parents parentRefs, cm.records[id] = rec - return rec.ref(true, dhs), nil + return rec.ref(true, dhs, pg), nil } -func (cm *cacheManager) Diff(ctx context.Context, lower, upper ImmutableRef, opts ...RefOption) (ir ImmutableRef, rerr error) { +func (cm *cacheManager) Diff(ctx context.Context, lower, upper ImmutableRef, pg progress.Controller, opts ...RefOption) (ir ImmutableRef, rerr error) { if lower == nil { return nil, errors.New("lower ref for diff cannot be nil") } @@ -815,7 +816,7 @@ func (cm *cacheManager) Diff(ctx context.Context, lower, upper ImmutableRef, opt } else { // inputParent implements ImmutableRef but isn't our internal struct, get an instance of the internal struct // by calling Get on its ID. - p, err := cm.Get(ctx, inputParent.ID(), append(opts, NoUpdateLastUsed)...) + p, err := cm.Get(ctx, inputParent.ID(), nil, append(opts, NoUpdateLastUsed)...) if err != nil { return nil, err } @@ -868,7 +869,7 @@ func (cm *cacheManager) Diff(ctx context.Context, lower, upper ImmutableRef, opt mergeParents.mergeParents[i-len(lowerLayers)] = subUpper.clone() } else { subParents := parentRefs{diffParents: &diffParents{lower: subLower.clone(), upper: subUpper.clone()}} - diffRef, err := cm.createDiffRef(ctx, subParents, subUpper.descHandlers, + diffRef, err := cm.createDiffRef(ctx, subParents, subUpper.descHandlers, pg, WithDescription(fmt.Sprintf("diff %q -> %q", subLower.ID(), subUpper.ID()))) if err != nil { subParents.release(context.TODO()) @@ -878,7 +879,7 @@ func (cm *cacheManager) Diff(ctx context.Context, lower, upper ImmutableRef, opt } } // On success, createMergeRef takes ownership of mergeParents - mergeRef, err := cm.createMergeRef(ctx, mergeParents, dhs) + mergeRef, err := cm.createMergeRef(ctx, mergeParents, dhs, pg) if err != nil { return nil, err } @@ -888,14 +889,14 @@ func (cm *cacheManager) Diff(ctx context.Context, lower, upper ImmutableRef, opt } // On success, createDiffRef takes ownership of parents - diffRef, err := cm.createDiffRef(ctx, parents, dhs, opts...) + diffRef, err := cm.createDiffRef(ctx, parents, dhs, pg, opts...) if err != nil { return nil, err } return diffRef, nil } -func (cm *cacheManager) createDiffRef(ctx context.Context, parents parentRefs, dhs DescHandlers, opts ...RefOption) (ir *immutableRef, rerr error) { +func (cm *cacheManager) createDiffRef(ctx context.Context, parents parentRefs, dhs DescHandlers, pg progress.Controller, opts ...RefOption) (ir *immutableRef, rerr error) { dps := parents.diffParents if err := dps.lower.Finalize(ctx); err != nil { return nil, errors.Wrapf(err, "failed to finalize lower parent during diff") @@ -963,7 +964,7 @@ func (cm *cacheManager) createDiffRef(ctx context.Context, parents parentRefs, d cm.records[id] = rec - return rec.ref(true, dhs), nil + return rec.ref(true, dhs, pg), nil } func (cm *cacheManager) Prune(ctx context.Context, ch chan client.UsageInfo, opts ...client.PruneInfo) error { @@ -1390,7 +1391,7 @@ func (cm *cacheManager) DiskUsage(ctx context.Context, opt client.DiskUsageInfo) func(d *client.UsageInfo) { eg.Go(func() error { cm.mu.Lock() - ref, err := cm.get(ctx, d.ID, NoUpdateLastUsed) + ref, err := cm.get(ctx, d.ID, nil, NoUpdateLastUsed) cm.mu.Unlock() if err != nil { d.Size = 0 diff --git a/cache/manager_test.go b/cache/manager_test.go index 8445b00db720..8e0ee205dfb4 100644 --- a/cache/manager_test.go +++ b/cache/manager_test.go @@ -181,7 +181,7 @@ func TestManager(t *testing.T) { defer cleanup() cm := co.manager - _, err = cm.Get(ctx, "foobar") + _, err = cm.Get(ctx, "foobar", nil) require.Error(t, err) checkDiskUsage(ctx, t, cm, 0, 0) @@ -247,10 +247,10 @@ func TestManager(t *testing.T) { require.Error(t, err) require.Equal(t, true, errors.Is(err, errInvalid)) - snap, err = cm.Get(ctx, snap.ID()) + snap, err = cm.Get(ctx, snap.ID(), nil) require.NoError(t, err) - snap2, err := cm.Get(ctx, snap.ID()) + snap2, err := cm.Get(ctx, snap.ID(), nil) require.NoError(t, err) checkDiskUsage(ctx, t, cm, 1, 0) @@ -383,7 +383,7 @@ func TestMergeBlobchainID(t *testing.T) { mergeInputs = append(mergeInputs, curBlob.Clone()) } - mergeRef, err := cm.Merge(ctx, mergeInputs) + mergeRef, err := cm.Merge(ctx, mergeInputs, nil) require.NoError(t, err) _, err = mergeRef.GetRemotes(ctx, true, compression.New(compression.Default), false, nil) @@ -505,7 +505,7 @@ func TestSnapshotExtract(t *testing.T) { require.NoError(t, err) require.Equal(t, 2, len(dirs)) - snap, err = cm.Get(ctx, id) + snap, err = cm.Get(ctx, id, nil) require.NoError(t, err) checkDiskUsage(ctx, t, cm, 2, 0) @@ -987,7 +987,7 @@ func TestLazyCommit(t *testing.T) { require.Equal(t, true, errors.Is(err, ErrLocked)) // immutable refs still work - snap2, err := cm.Get(ctx, snap.ID()) + snap2, err := cm.Get(ctx, snap.ID(), nil) require.NoError(t, err) require.Equal(t, snap.ID(), snap2.ID()) @@ -998,7 +998,7 @@ func TestLazyCommit(t *testing.T) { require.NoError(t, err) // immutable work after final release as well - snap, err = cm.Get(ctx, snap.ID()) + snap, err = cm.Get(ctx, snap.ID(), nil) require.NoError(t, err) require.Equal(t, snap.ID(), snap2.ID()) @@ -1016,7 +1016,7 @@ func TestLazyCommit(t *testing.T) { require.Equal(t, active2.ID(), active.ID()) // because ref was took mutable old immutable are cleared - _, err = cm.Get(ctx, snap.ID()) + _, err = cm.Get(ctx, snap.ID(), nil) require.Error(t, err) require.Equal(t, true, errors.Is(err, errNotFound)) @@ -1036,7 +1036,7 @@ func TestLazyCommit(t *testing.T) { require.Equal(t, true, errors.Is(err, errNotFound)) // immutable still works - snap2, err = cm.Get(ctx, snap.ID()) + snap2, err = cm.Get(ctx, snap.ID(), nil) require.NoError(t, err) require.Equal(t, snap.ID(), snap2.ID()) @@ -1065,7 +1065,7 @@ func TestLazyCommit(t *testing.T) { require.NoError(t, err) cm = co.manager - snap2, err = cm.Get(ctx, snap.ID()) + snap2, err = cm.Get(ctx, snap.ID(), nil) require.NoError(t, err) err = snap2.Release(ctx) @@ -1074,7 +1074,7 @@ func TestLazyCommit(t *testing.T) { active, err = cm.GetMutable(ctx, active.ID()) require.NoError(t, err) - _, err = cm.Get(ctx, snap.ID()) + _, err = cm.Get(ctx, snap.ID(), nil) require.Error(t, err) require.Equal(t, true, errors.Is(err, errNotFound)) @@ -1095,7 +1095,7 @@ func TestLazyCommit(t *testing.T) { defer cleanup() cm = co.manager - snap2, err = cm.Get(ctx, snap.ID()) + snap2, err = cm.Get(ctx, snap.ID(), nil) require.NoError(t, err) err = snap2.Finalize(ctx) @@ -1593,7 +1593,7 @@ func TestMergeOp(t *testing.T) { defer cleanup() cm := co.manager - emptyMerge, err := cm.Merge(ctx, nil) + emptyMerge, err := cm.Merge(ctx, nil, nil) require.NoError(t, err) require.Nil(t, emptyMerge) @@ -1620,7 +1620,7 @@ func TestMergeOp(t *testing.T) { require.EqualValues(t, 8192, size) } - singleMerge, err := cm.Merge(ctx, baseRefs[:1]) + singleMerge, err := cm.Merge(ctx, baseRefs[:1], nil) require.NoError(t, err) m, err := singleMerge.Mount(ctx, true, nil) require.NoError(t, err) @@ -1640,7 +1640,7 @@ func TestMergeOp(t *testing.T) { }}) require.NoError(t, err) - merge1, err := cm.Merge(ctx, baseRefs[:3]) + merge1, err := cm.Merge(ctx, baseRefs[:3], nil) require.NoError(t, err) _, err = merge1.Mount(ctx, true, nil) require.NoError(t, err) @@ -1649,7 +1649,7 @@ func TestMergeOp(t *testing.T) { require.EqualValues(t, 4096, size1) // hardlinking means all but the first snapshot doesn't take up space checkDiskUsage(ctx, t, cm, 7, 0) - merge2, err := cm.Merge(ctx, baseRefs[3:]) + merge2, err := cm.Merge(ctx, baseRefs[3:], nil) require.NoError(t, err) _, err = merge2.Mount(ctx, true, nil) require.NoError(t, err) @@ -1664,7 +1664,7 @@ func TestMergeOp(t *testing.T) { checkDiskUsage(ctx, t, cm, 8, 0) // should still be able to use merges based on released refs - merge3, err := cm.Merge(ctx, []ImmutableRef{merge1, merge2}) + merge3, err := cm.Merge(ctx, []ImmutableRef{merge1, merge2}, nil) require.NoError(t, err) require.NoError(t, merge1.Release(ctx)) require.NoError(t, merge2.Release(ctx)) @@ -1719,7 +1719,7 @@ func TestDiffOp(t *testing.T) { require.NoError(t, err) // verify that releasing parents does not invalidate a diff ref until it is released - diff, err := cm.Diff(ctx, lowerA, upperA) + diff, err := cm.Diff(ctx, lowerA, upperA, nil) require.NoError(t, err) checkDiskUsage(ctx, t, cm, 3, 0) require.NoError(t, lowerA.Release(ctx)) @@ -1756,7 +1756,7 @@ func TestDiffOp(t *testing.T) { e, err := newRef.Commit(ctx) require.NoError(t, err) - diff, err = cm.Diff(ctx, c, e) + diff, err = cm.Diff(ctx, c, e, nil) require.NoError(t, err) checkDiskUsage(ctx, t, cm, 8, 0) // 5 base refs + 2 diffs + 1 merge require.NoError(t, a.Release(ctx)) @@ -1847,7 +1847,7 @@ func TestLoadHalfFinalizedRef(t *testing.T) { _, err = cm.GetMutable(ctx, mutRef.ID()) require.ErrorIs(t, err, errNotFound) - iref, err = cm.Get(ctx, immutRef.ID()) + iref, err = cm.Get(ctx, immutRef.ID(), nil) require.NoError(t, err) require.NoError(t, iref.Finalize(ctx)) immutRef = iref.(*immutableRef) diff --git a/cache/opts.go b/cache/opts.go index 5f2a4b767587..92df9989d928 100644 --- a/cache/opts.go +++ b/cache/opts.go @@ -35,3 +35,5 @@ type NeedsRemoteProviderError []digest.Digest //nolint:errname func (m NeedsRemoteProviderError) Error() string { return fmt.Sprintf("missing descriptor handlers for lazy blobs %+v", []digest.Digest(m)) } + +type ProgressKey struct{} diff --git a/cache/refs.go b/cache/refs.go index e33dc35ff3da..a846dc3335c7 100644 --- a/cache/refs.go +++ b/cache/refs.go @@ -21,6 +21,7 @@ import ( "github.com/moby/buildkit/util/compression" "github.com/moby/buildkit/util/flightcontrol" "github.com/moby/buildkit/util/leaseutil" + "github.com/moby/buildkit/util/progress" "github.com/moby/buildkit/util/winlayers" digest "github.com/opencontainers/go-digest" ocispecs "github.com/opencontainers/image-spec/specs-go/v1" @@ -87,11 +88,12 @@ type cacheRecord struct { } // hold ref lock before calling -func (cr *cacheRecord) ref(triggerLastUsed bool, descHandlers DescHandlers) *immutableRef { +func (cr *cacheRecord) ref(triggerLastUsed bool, descHandlers DescHandlers, pg progress.Controller) *immutableRef { ref := &immutableRef{ cacheRecord: cr, triggerLastUsed: triggerLastUsed, descHandlers: descHandlers, + progress: pg, } cr.refs[ref] = struct{}{} return ref @@ -446,6 +448,8 @@ type immutableRef struct { *cacheRecord triggerLastUsed bool descHandlers DescHandlers + // TODO:(sipsma) de-dupe progress with the same field inside descHandlers? + progress progress.Controller } // Order is from parent->child, sr will be at end of slice. Refs should not @@ -576,7 +580,7 @@ func (sr *mutableRef) DescHandler(dgst digest.Digest) *DescHandler { func (sr *immutableRef) clone() *immutableRef { sr.mu.Lock() - ref := sr.ref(false, sr.descHandlers) + ref := sr.ref(false, sr.descHandlers, sr.progress) sr.mu.Unlock() return ref } @@ -811,14 +815,14 @@ func (sr *immutableRef) Extract(ctx context.Context, s session.Group) (rerr erro if rerr = sr.prepareRemoteSnapshotsStargzMode(ctx, s); rerr != nil { return } - rerr = sr.unlazy(ctx, sr.descHandlers, s) + rerr = sr.unlazy(ctx, sr.descHandlers, sr.progress, s, true) }); err != nil { return err } return rerr } - return sr.unlazy(ctx, sr.descHandlers, s) + return sr.unlazy(ctx, sr.descHandlers, sr.progress, s, true) } func (sr *immutableRef) withRemoteSnapshotLabelsStargzMode(ctx context.Context, s session.Group, f func()) error { @@ -949,7 +953,7 @@ func makeTmpLabelsStargzMode(labels map[string]string, s session.Group) (fields return } -func (sr *immutableRef) unlazy(ctx context.Context, dhs DescHandlers, s session.Group) error { +func (sr *immutableRef) unlazy(ctx context.Context, dhs DescHandlers, pg progress.Controller, s session.Group, topLevel bool) error { _, err := sr.sizeG.Do(ctx, sr.ID()+"-unlazy", func(ctx context.Context) (_ interface{}, rerr error) { if _, err := sr.cm.Snapshotter.Stat(ctx, sr.getSnapshotID()); err == nil { return nil, nil @@ -957,9 +961,9 @@ func (sr *immutableRef) unlazy(ctx context.Context, dhs DescHandlers, s session. switch sr.kind() { case Merge, Diff: - return nil, sr.unlazyDiffMerge(ctx, dhs, s) + return nil, sr.unlazyDiffMerge(ctx, dhs, pg, s, topLevel) case Layer, BaseLayer: - return nil, sr.unlazyLayer(ctx, dhs, s) + return nil, sr.unlazyLayer(ctx, dhs, pg, s) } return nil, nil }) @@ -967,7 +971,7 @@ func (sr *immutableRef) unlazy(ctx context.Context, dhs DescHandlers, s session. } // should be called within sizeG.Do call for this ref's ID -func (sr *immutableRef) unlazyDiffMerge(ctx context.Context, dhs DescHandlers, s session.Group) error { +func (sr *immutableRef) unlazyDiffMerge(ctx context.Context, dhs DescHandlers, pg progress.Controller, s session.Group, topLevel bool) (rerr error) { eg, egctx := errgroup.WithContext(ctx) var diffs []snapshot.Diff sr.layerWalk(func(sr *immutableRef) { @@ -977,13 +981,13 @@ func (sr *immutableRef) unlazyDiffMerge(ctx context.Context, dhs DescHandlers, s if sr.diffParents.lower != nil { diff.Lower = sr.diffParents.lower.getSnapshotID() eg.Go(func() error { - return sr.diffParents.lower.unlazy(egctx, dhs, s) + return sr.diffParents.lower.unlazy(egctx, dhs, pg, s, false) }) } if sr.diffParents.upper != nil { diff.Upper = sr.diffParents.upper.getSnapshotID() eg.Go(func() error { - return sr.diffParents.upper.unlazy(egctx, dhs, s) + return sr.diffParents.upper.unlazy(egctx, dhs, pg, s, false) }) } case Layer: @@ -992,7 +996,7 @@ func (sr *immutableRef) unlazyDiffMerge(ctx context.Context, dhs DescHandlers, s case BaseLayer: diff.Upper = sr.getSnapshotID() eg.Go(func() error { - return sr.unlazy(egctx, dhs, s) + return sr.unlazy(egctx, dhs, pg, s, false) }) } diffs = append(diffs, diff) @@ -1000,11 +1004,30 @@ func (sr *immutableRef) unlazyDiffMerge(ctx context.Context, dhs DescHandlers, s if err := eg.Wait(); err != nil { return err } + + if pg != nil { + action := "merging" + if sr.kind() == Diff { + action = "diffing" + } + progressID := sr.GetDescription() + if topLevel { + progressID = action + } + if progressID == "" { + progressID = fmt.Sprintf("%s %s", action, sr.ID()) + } + _, stopProgress := pg.Start(ctx) + defer stopProgress(rerr) + statusDone := pg.Status(progressID, action) + defer statusDone() + } + return sr.cm.Snapshotter.Merge(ctx, sr.getSnapshotID(), diffs) } // should be called within sizeG.Do call for this ref's ID -func (sr *immutableRef) unlazyLayer(ctx context.Context, dhs DescHandlers, s session.Group) (rerr error) { +func (sr *immutableRef) unlazyLayer(ctx context.Context, dhs DescHandlers, pg progress.Controller, s session.Group) (rerr error) { if !sr.getBlobOnly() { return nil } @@ -1031,7 +1054,7 @@ func (sr *immutableRef) unlazyLayer(ctx context.Context, dhs DescHandlers, s ses parentID := "" if sr.layerParent != nil { eg.Go(func() error { - if err := sr.layerParent.unlazy(egctx, dhs, s); err != nil { + if err := sr.layerParent.unlazy(egctx, dhs, pg, s, false); err != nil { return err } parentID = sr.layerParent.getSnapshotID() @@ -1059,10 +1082,13 @@ func (sr *immutableRef) unlazyLayer(ctx context.Context, dhs DescHandlers, s ses return err } - if dh != nil && dh.Progress != nil { - _, stopProgress := dh.Progress.Start(ctx) + if pg == nil && dh != nil { + pg = dh.Progress + } + if pg != nil { + _, stopProgress := pg.Start(ctx) defer stopProgress(rerr) - statusDone := dh.Progress.Status("extracting "+desc.Digest.String(), "extracting") + statusDone := pg.Status("extracting "+desc.Digest.String(), "extracting") defer statusDone() } @@ -1251,7 +1277,7 @@ func (sr *mutableRef) commit(ctx context.Context) (_ *immutableRef, rerr error) return nil, err } - ref := rec.ref(true, sr.descHandlers) + ref := rec.ref(true, sr.descHandlers, nil) sr.equalImmutable = ref return ref, nil } diff --git a/solver/cacheopts.go b/solver/cacheopts.go index c997c3493fcd..d5821b4e9134 100644 --- a/solver/cacheopts.go +++ b/solver/cacheopts.go @@ -12,21 +12,21 @@ type CacheOpts map[interface{}]interface{} type cacheOptGetterKey struct{} -func CacheOptGetterOf(ctx context.Context) func(keys ...interface{}) map[interface{}]interface{} { +func CacheOptGetterOf(ctx context.Context) func(includeAncestors bool, keys ...interface{}) map[interface{}]interface{} { if v := ctx.Value(cacheOptGetterKey{}); v != nil { - if getter, ok := v.(func(keys ...interface{}) map[interface{}]interface{}); ok { + if getter, ok := v.(func(includeAncestors bool, keys ...interface{}) map[interface{}]interface{}); ok { return getter } } return nil } -func WithCacheOptGetter(ctx context.Context, getter func(keys ...interface{}) map[interface{}]interface{}) context.Context { +func WithCacheOptGetter(ctx context.Context, getter func(includeAncestors bool, keys ...interface{}) map[interface{}]interface{}) context.Context { return context.WithValue(ctx, cacheOptGetterKey{}, getter) } func withAncestorCacheOpts(ctx context.Context, start *state) context.Context { - return WithCacheOptGetter(ctx, func(keys ...interface{}) map[interface{}]interface{} { + return WithCacheOptGetter(ctx, func(includeAncestors bool, keys ...interface{}) map[interface{}]interface{} { keySet := make(map[interface{}]struct{}) for _, k := range keys { keySet[k] = struct{}{} @@ -51,7 +51,7 @@ func withAncestorCacheOpts(ctx context.Context, start *state) context.Context { } } } - return false + return !includeAncestors // stop after the first state unless includeAncestors is true }) return values }) diff --git a/solver/llbsolver/ops/diff.go b/solver/llbsolver/ops/diff.go index 99dd676022ed..a2f47b915967 100644 --- a/solver/llbsolver/ops/diff.go +++ b/solver/llbsolver/ops/diff.go @@ -3,8 +3,9 @@ package ops import ( "context" "encoding/json" - "fmt" + "github.com/moby/buildkit/util/progress" + "github.com/moby/buildkit/util/progress/controller" "github.com/moby/buildkit/worker" "github.com/pkg/errors" @@ -21,6 +22,8 @@ const diffCacheType = "buildkit.diff.v0" type diffOp struct { op *pb.DiffOp worker worker.Worker + vtx solver.Vertex + pg progress.Controller } func NewDiffOp(v solver.Vertex, op *pb.Op_Diff, w worker.Worker) (solver.Op, error) { @@ -30,6 +33,7 @@ func NewDiffOp(v solver.Vertex, op *pb.Op_Diff, w worker.Worker) (solver.Op, err return &diffOp{ op: op.Diff, worker: w, + vtx: v, }, nil } @@ -60,8 +64,16 @@ func (d *diffOp) CacheMap(ctx context.Context, group session.Group, index int) ( ComputeDigestFunc solver.ResultBasedCacheFunc PreprocessFunc solver.PreprocessFunc }, depCount), + Opts: solver.CacheOpts(make(map[interface{}]interface{})), } + d.pg = &controller.Controller{ + WriterFactory: progress.FromContext(ctx), + Digest: d.vtx.Digest(), + Name: d.vtx.Name(), + } + cm.Opts[cache.ProgressKey{}] = d.pg + return cm, true, nil } @@ -69,7 +81,6 @@ func (d *diffOp) Exec(ctx context.Context, g session.Group, inputs []solver.Resu var curInput int var lowerRef cache.ImmutableRef - var lowerRefID string if d.op.Lower.Input != pb.Empty { if lowerInp := inputs[curInput]; lowerInp != nil { wref, ok := lowerInp.Sys().(*worker.WorkerRef) @@ -77,9 +88,6 @@ func (d *diffOp) Exec(ctx context.Context, g session.Group, inputs []solver.Resu return nil, errors.Errorf("invalid lower reference for diff op %T", lowerInp.Sys()) } lowerRef = wref.ImmutableRef - if lowerRef != nil { - lowerRefID = wref.ImmutableRef.ID() - } } else { return nil, errors.New("invalid nil lower input for diff op") } @@ -87,7 +95,6 @@ func (d *diffOp) Exec(ctx context.Context, g session.Group, inputs []solver.Resu } var upperRef cache.ImmutableRef - var upperRefID string if d.op.Upper.Input != pb.Empty { if upperInp := inputs[curInput]; upperInp != nil { wref, ok := upperInp.Sys().(*worker.WorkerRef) @@ -95,9 +102,6 @@ func (d *diffOp) Exec(ctx context.Context, g session.Group, inputs []solver.Resu return nil, errors.Errorf("invalid upper reference for diff op %T", upperInp.Sys()) } upperRef = wref.ImmutableRef - if upperRef != nil { - upperRefID = wref.ImmutableRef.ID() - } } else { return nil, errors.New("invalid nil upper input for diff op") } @@ -116,8 +120,8 @@ func (d *diffOp) Exec(ctx context.Context, g session.Group, inputs []solver.Resu return []solver.Result{worker.NewWorkerRefResult(nil, d.worker)}, nil } - diffRef, err := d.worker.CacheManager().Diff(ctx, lowerRef, upperRef, - cache.WithDescription(fmt.Sprintf("diff %q -> %q", lowerRefID, upperRefID))) + diffRef, err := d.worker.CacheManager().Diff(ctx, lowerRef, upperRef, d.pg, + cache.WithDescription(d.vtx.Name())) if err != nil { return nil, err } diff --git a/solver/llbsolver/ops/merge.go b/solver/llbsolver/ops/merge.go index a2a1fedfeace..aee4bee47073 100644 --- a/solver/llbsolver/ops/merge.go +++ b/solver/llbsolver/ops/merge.go @@ -3,9 +3,9 @@ package ops import ( "context" "encoding/json" - "fmt" - "strings" + "github.com/moby/buildkit/util/progress" + "github.com/moby/buildkit/util/progress/controller" "github.com/moby/buildkit/worker" "github.com/pkg/errors" @@ -22,6 +22,8 @@ const mergeCacheType = "buildkit.merge.v0" type mergeOp struct { op *pb.MergeOp worker worker.Worker + vtx solver.Vertex + pg progress.Controller } func NewMergeOp(v solver.Vertex, op *pb.Op_Merge, w worker.Worker) (solver.Op, error) { @@ -31,6 +33,7 @@ func NewMergeOp(v solver.Vertex, op *pb.Op_Merge, w worker.Worker) (solver.Op, e return &mergeOp{ op: op.Merge, worker: w, + vtx: v, }, nil } @@ -53,14 +56,21 @@ func (m *mergeOp) CacheMap(ctx context.Context, group session.Group, index int) ComputeDigestFunc solver.ResultBasedCacheFunc PreprocessFunc solver.PreprocessFunc }, len(m.op.Inputs)), + Opts: solver.CacheOpts(make(map[interface{}]interface{})), } + m.pg = &controller.Controller{ + WriterFactory: progress.FromContext(ctx), + Digest: m.vtx.Digest(), + Name: m.vtx.Name(), + } + cm.Opts[cache.ProgressKey{}] = m.pg + return cm, true, nil } func (m *mergeOp) Exec(ctx context.Context, g session.Group, inputs []solver.Result) ([]solver.Result, error) { refs := make([]cache.ImmutableRef, len(inputs)) - ids := make([]string, len(inputs)) var index int for _, inp := range inputs { if inp == nil { @@ -74,18 +84,16 @@ func (m *mergeOp) Exec(ctx context.Context, g session.Group, inputs []solver.Res continue } refs[index] = wref.ImmutableRef - ids[index] = wref.ImmutableRef.ID() index++ } refs = refs[:index] - ids = ids[:index] if len(refs) == 0 { return nil, nil } - mergedRef, err := m.worker.CacheManager().Merge(ctx, refs, - cache.WithDescription(fmt.Sprintf("merge %s", strings.Join(ids, ";")))) + mergedRef, err := m.worker.CacheManager().Merge(ctx, refs, m.pg, + cache.WithDescription(m.vtx.Name())) if err != nil { return nil, err } diff --git a/solver/llbsolver/solver.go b/solver/llbsolver/solver.go index 282e13306398..d4a868d23516 100644 --- a/solver/llbsolver/solver.go +++ b/solver/llbsolver/solver.go @@ -329,7 +329,7 @@ func inlineCache(ctx context.Context, e remotecache.Exporter, res solver.CachedR } func withDescHandlerCacheOpts(ctx context.Context, ref cache.ImmutableRef) context.Context { - return solver.WithCacheOptGetter(ctx, func(keys ...interface{}) map[interface{}]interface{} { + return solver.WithCacheOptGetter(ctx, func(includeAncestors bool, keys ...interface{}) map[interface{}]interface{} { vals := make(map[interface{}]interface{}) for _, k := range keys { if key, ok := k.(cache.DescHandlerKey); ok { diff --git a/source/git/gitsource.go b/source/git/gitsource.go index 627ac04c88c8..9169992f7ad9 100644 --- a/source/git/gitsource.go +++ b/source/git/gitsource.go @@ -382,7 +382,7 @@ func (gs *gitSourceHandler) Snapshot(ctx context.Context, g session.Group) (out return nil, errors.Wrapf(err, "failed to search metadata for %s", snapshotKey) } if len(sis) > 0 { - return gs.cache.Get(ctx, sis[0].ID()) + return gs.cache.Get(ctx, sis[0].ID(), nil) } gs.locker.Lock(gs.src.Remote) diff --git a/source/http/httpsource.go b/source/http/httpsource.go index 7139af35b6f2..bd54c91e0e39 100644 --- a/source/http/httpsource.go +++ b/source/http/httpsource.go @@ -373,7 +373,7 @@ func (hs *httpSourceHandler) save(ctx context.Context, resp *http.Response, s se func (hs *httpSourceHandler) Snapshot(ctx context.Context, g session.Group) (cache.ImmutableRef, error) { if hs.refID != "" { - ref, err := hs.cache.Get(ctx, hs.refID) + ref, err := hs.cache.Get(ctx, hs.refID, nil) if err == nil { return ref, nil } diff --git a/worker/base/worker.go b/worker/base/worker.go index 2f2e9d794ec4..153d82ce3e68 100644 --- a/worker/base/worker.go +++ b/worker/base/worker.go @@ -227,16 +227,26 @@ func (w *Worker) LoadRef(ctx context.Context, id string, hidden bool) (cache.Imm return nil, nil } - ref, err := w.CacheMgr.Get(ctx, id, opts...) + var pg progress.Controller + optGetter := solver.CacheOptGetterOf(ctx) + if optGetter != nil { + if kv := optGetter(false, cache.ProgressKey{}); kv != nil { + if v, ok := kv[cache.ProgressKey{}].(progress.Controller); ok { + pg = v + } + } + } + + ref, err := w.CacheMgr.Get(ctx, id, pg, opts...) var needsRemoteProviders cache.NeedsRemoteProviderError if errors.As(err, &needsRemoteProviders) { - if optGetter := solver.CacheOptGetterOf(ctx); optGetter != nil { + if optGetter != nil { var keys []interface{} for _, dgst := range needsRemoteProviders { keys = append(keys, cache.DescHandlerKey(dgst)) } descHandlers := cache.DescHandlers(make(map[digest.Digest]*cache.DescHandler)) - for k, v := range optGetter(keys...) { + for k, v := range optGetter(true, keys...) { if key, ok := k.(cache.DescHandlerKey); ok { if handler, ok := v.(*cache.DescHandler); ok { descHandlers[digest.Digest(key)] = handler @@ -244,7 +254,7 @@ func (w *Worker) LoadRef(ctx context.Context, id string, hidden bool) (cache.Imm } } opts = append(opts, descHandlers) - ref, err = w.CacheMgr.Get(ctx, id, opts...) + ref, err = w.CacheMgr.Get(ctx, id, pg, opts...) } } if err != nil { From 0566b9a3455a8c19508529b931eba7659f61a293 Mon Sep 17 00:00:00 2001 From: Erik Sipsma Date: Thu, 3 Feb 2022 10:54:42 -0800 Subject: [PATCH 3/5] Add support for progress groups. This allows clients to specify that LLB states should be grouped in progress output under a custom name. Status updates for all vertexes in the group will show up under a single vertex in the output. The intended use cases are for Dockerfile COPY's that use MergeOp as a backend and for grouping some other internal vertexes during frontend builds. Signed-off-by: Erik Sipsma --- api/services/control/control.pb.go | 315 ++++++++----- api/services/control/control.proto | 1 + cache/manager.go | 1 + client/graph.go | 15 +- client/llb/state.go | 10 + client/solve.go | 15 +- control/control.go | 15 +- solver/jobs.go | 7 +- solver/llbsolver/ops/diff.go | 1 + solver/llbsolver/ops/merge.go | 1 + solver/llbsolver/vertex.go | 1 + solver/pb/ops.pb.go | 628 ++++++++++++++++++------- solver/pb/ops.proto | 7 + solver/types.go | 1 + source/containerimage/pull.go | 1 + util/progress/controller/controller.go | 20 +- util/progress/progressui/display.go | 86 ++++ 17 files changed, 792 insertions(+), 333 deletions(-) diff --git a/api/services/control/control.pb.go b/api/services/control/control.pb.go index 706606a66d78..939f2c2ca7d8 100644 --- a/api/services/control/control.pb.go +++ b/api/services/control/control.pb.go @@ -770,6 +770,7 @@ type Vertex struct { Started *time.Time `protobuf:"bytes,5,opt,name=started,proto3,stdtime" json:"started,omitempty"` Completed *time.Time `protobuf:"bytes,6,opt,name=completed,proto3,stdtime" json:"completed,omitempty"` Error string `protobuf:"bytes,7,opt,name=error,proto3" json:"error,omitempty"` + ProgressGroup *pb.ProgressGroup `protobuf:"bytes,8,opt,name=progressGroup,proto3" json:"progressGroup,omitempty"` XXX_NoUnkeyedLiteral struct{} `json:"-"` XXX_unrecognized []byte `json:"-"` XXX_sizecache int32 `json:"-"` @@ -843,6 +844,13 @@ func (m *Vertex) GetError() string { return "" } +func (m *Vertex) GetProgressGroup() *pb.ProgressGroup { + if m != nil { + return m.ProgressGroup + } + return nil +} + type VertexStatus struct { ID string `protobuf:"bytes,1,opt,name=ID,proto3" json:"ID,omitempty"` Vertex github_com_opencontainers_go_digest.Digest `protobuf:"bytes,2,opt,name=vertex,proto3,customtype=github.com/opencontainers/go-digest.Digest" json:"vertex"` @@ -1261,103 +1269,104 @@ func init() { func init() { proto.RegisterFile("control.proto", fileDescriptor_0c5120591600887d) } var fileDescriptor_0c5120591600887d = []byte{ - // 1521 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x57, 0x4b, 0x6f, 0xdb, 0xc6, - 0x16, 0x0e, 0x25, 0xeb, 0x75, 0x2c, 0x1b, 0xce, 0x24, 0x37, 0x20, 0x78, 0x71, 0x6d, 0x87, 0xc9, - 0x05, 0x84, 0x20, 0xa1, 0x1c, 0xb5, 0x29, 0x52, 0xf7, 0x81, 0x44, 0x56, 0x8a, 0x38, 0x88, 0xd1, - 0x74, 0x94, 0x34, 0x40, 0x16, 0x05, 0x28, 0x69, 0x24, 0x13, 0xa6, 0x38, 0xec, 0xcc, 0xd0, 0x89, - 0xfa, 0x03, 0xba, 0xee, 0xae, 0x3f, 0xa0, 0x8b, 0xae, 0xba, 0xee, 0x2f, 0x28, 0x90, 0x65, 0xd7, - 0x59, 0xb8, 0x45, 0xf6, 0x2d, 0xba, 0xec, 0xb2, 0x98, 0x07, 0x65, 0xca, 0x92, 0xfc, 0x4a, 0x57, - 0x9a, 0x33, 0x3c, 0xdf, 0xa7, 0xf3, 0x9c, 0x99, 0x03, 0x4b, 0x5d, 0x1a, 0x09, 0x46, 0x43, 0x2f, - 0x66, 0x54, 0x50, 0xb4, 0x32, 0xa4, 0x9d, 0x91, 0xd7, 0x49, 0x82, 0xb0, 0xb7, 0x17, 0x08, 0x6f, - 0xff, 0xb6, 0x73, 0x6b, 0x10, 0x88, 0xdd, 0xa4, 0xe3, 0x75, 0xe9, 0xb0, 0x3e, 0xa0, 0x03, 0x5a, - 0x57, 0x8a, 0x9d, 0xa4, 0xaf, 0x24, 0x25, 0xa8, 0x95, 0x26, 0x70, 0xd6, 0x06, 0x94, 0x0e, 0x42, - 0x72, 0xa8, 0x25, 0x82, 0x21, 0xe1, 0xc2, 0x1f, 0xc6, 0x46, 0xe1, 0x66, 0x86, 0x4f, 0xfe, 0x59, - 0x3d, 0xfd, 0xb3, 0x3a, 0xa7, 0xe1, 0x3e, 0x61, 0xf5, 0xb8, 0x53, 0xa7, 0x31, 0x37, 0xda, 0xf5, - 0xb9, 0xda, 0x7e, 0x1c, 0xd4, 0xc5, 0x28, 0x26, 0xbc, 0xfe, 0x92, 0xb2, 0x3d, 0xc2, 0x34, 0xc0, - 0xfd, 0xd6, 0x82, 0xea, 0x13, 0x96, 0x44, 0x04, 0x93, 0xaf, 0x13, 0xc2, 0x05, 0xba, 0x02, 0xc5, - 0x7e, 0x10, 0x0a, 0xc2, 0x6c, 0x6b, 0x3d, 0x5f, 0xab, 0x60, 0x23, 0xa1, 0x15, 0xc8, 0xfb, 0x61, - 0x68, 0xe7, 0xd6, 0xad, 0x5a, 0x19, 0xcb, 0x25, 0xaa, 0x41, 0x75, 0x8f, 0x90, 0xb8, 0x95, 0x30, - 0x5f, 0x04, 0x34, 0xb2, 0xf3, 0xeb, 0x56, 0x2d, 0xdf, 0x5c, 0x78, 0x7d, 0xb0, 0x66, 0xe1, 0x89, - 0x2f, 0xc8, 0x85, 0x8a, 0x94, 0x9b, 0x23, 0x41, 0xb8, 0xbd, 0x90, 0x51, 0x3b, 0xdc, 0x76, 0x6f, - 0xc0, 0x4a, 0x2b, 0xe0, 0x7b, 0xcf, 0xb8, 0x3f, 0x38, 0xc9, 0x16, 0xf7, 0x11, 0x5c, 0xcc, 0xe8, - 0xf2, 0x98, 0x46, 0x9c, 0xa0, 0x3b, 0x50, 0x64, 0xa4, 0x4b, 0x59, 0x4f, 0x29, 0x2f, 0x36, 0xfe, - 0xe7, 0x1d, 0xcd, 0x8d, 0x67, 0x00, 0x52, 0x09, 0x1b, 0x65, 0xf7, 0xfb, 0x3c, 0x2c, 0x66, 0xf6, - 0xd1, 0x32, 0xe4, 0xb6, 0x5b, 0xb6, 0xb5, 0x6e, 0xd5, 0x2a, 0x38, 0xb7, 0xdd, 0x42, 0x36, 0x94, - 0x76, 0x12, 0xe1, 0x77, 0x42, 0x62, 0x7c, 0x4f, 0x45, 0x74, 0x19, 0x0a, 0xdb, 0xd1, 0x33, 0x4e, - 0x94, 0xe3, 0x65, 0xac, 0x05, 0x84, 0x60, 0xa1, 0x1d, 0x7c, 0x43, 0xb4, 0x9b, 0x58, 0xad, 0x91, - 0x03, 0xc5, 0x27, 0x3e, 0x23, 0x91, 0xb0, 0x0b, 0x92, 0xb7, 0x99, 0xb3, 0x2d, 0x6c, 0x76, 0x50, - 0x13, 0x2a, 0x5b, 0x8c, 0xf8, 0x82, 0xf4, 0xee, 0x0b, 0xbb, 0xb8, 0x6e, 0xd5, 0x16, 0x1b, 0x8e, - 0xa7, 0x8b, 0xc2, 0x4b, 0x8b, 0xc2, 0x7b, 0x9a, 0x16, 0x45, 0xb3, 0xfc, 0xfa, 0x60, 0xed, 0xc2, - 0x77, 0xbf, 0xc9, 0xd8, 0x8d, 0x61, 0xe8, 0x1e, 0xc0, 0x63, 0x9f, 0x8b, 0x67, 0x5c, 0x91, 0x94, - 0x4e, 0x24, 0x59, 0x50, 0x04, 0x19, 0x0c, 0x5a, 0x05, 0x50, 0x41, 0xd8, 0xa2, 0x49, 0x24, 0xec, - 0xb2, 0xb2, 0x3d, 0xb3, 0x83, 0xd6, 0x61, 0xb1, 0x45, 0x78, 0x97, 0x05, 0xb1, 0x4a, 0x75, 0x45, - 0x85, 0x27, 0xbb, 0x25, 0x19, 0x74, 0x04, 0x9f, 0x8e, 0x62, 0x62, 0x83, 0x52, 0xc8, 0xec, 0xc8, - 0x5c, 0xb6, 0x77, 0x7d, 0x46, 0x7a, 0xf6, 0xa2, 0x0a, 0x97, 0x91, 0x64, 0x7c, 0x75, 0x24, 0xb8, - 0x5d, 0x55, 0x49, 0x4e, 0x45, 0xf7, 0x87, 0x22, 0x54, 0xdb, 0xb2, 0xc6, 0xd3, 0x72, 0x58, 0x81, - 0x3c, 0x26, 0x7d, 0x93, 0x1b, 0xb9, 0x44, 0x1e, 0x40, 0x8b, 0xf4, 0x83, 0x28, 0x50, 0x56, 0xe5, - 0x94, 0xe3, 0xcb, 0x5e, 0xdc, 0xf1, 0x0e, 0x77, 0x71, 0x46, 0x03, 0x39, 0x50, 0x7e, 0xf0, 0x2a, - 0xa6, 0x4c, 0x96, 0x54, 0x5e, 0xd1, 0x8c, 0x65, 0xf4, 0x1c, 0x96, 0xd2, 0xf5, 0x7d, 0x21, 0x98, - 0x2c, 0x54, 0x59, 0x46, 0xb7, 0xa7, 0xcb, 0x28, 0x6b, 0x94, 0x37, 0x81, 0x79, 0x10, 0x09, 0x36, - 0xc2, 0x93, 0x3c, 0xd2, 0xc3, 0x36, 0xe1, 0x5c, 0x5a, 0xa8, 0xd2, 0x8f, 0x53, 0x51, 0x9a, 0xf3, - 0x19, 0xa3, 0x91, 0x20, 0x51, 0x4f, 0xa5, 0xbe, 0x82, 0xc7, 0xb2, 0x34, 0x27, 0x5d, 0x6b, 0x73, - 0x4a, 0xa7, 0x32, 0x67, 0x02, 0x63, 0xcc, 0x99, 0xd8, 0x43, 0x9b, 0x50, 0xd8, 0xf2, 0xbb, 0xbb, - 0x44, 0x65, 0x79, 0xb1, 0xb1, 0x3a, 0x4d, 0xa8, 0x3e, 0x7f, 0xae, 0xd2, 0xca, 0x55, 0xa3, 0x5e, - 0xc0, 0x1a, 0x82, 0xbe, 0x82, 0xea, 0x83, 0x48, 0x04, 0x22, 0x24, 0x43, 0x95, 0xb1, 0x8a, 0xcc, - 0x58, 0x73, 0xf3, 0xcd, 0xc1, 0xda, 0x07, 0x73, 0x0f, 0x9e, 0x44, 0x04, 0x61, 0x9d, 0x64, 0x50, - 0x5e, 0x86, 0x02, 0x4f, 0xf0, 0xa1, 0x17, 0xb0, 0x9c, 0x1a, 0xbb, 0x1d, 0xc5, 0x89, 0xe0, 0x36, - 0x28, 0xaf, 0x1b, 0xa7, 0xf4, 0x5a, 0x83, 0xb4, 0xdb, 0x47, 0x98, 0x9c, 0x7b, 0x80, 0xa6, 0x73, - 0x25, 0x6b, 0x6a, 0x8f, 0x8c, 0xd2, 0x9a, 0xda, 0x23, 0x23, 0xd9, 0xd6, 0xfb, 0x7e, 0x98, 0xe8, - 0x76, 0xaf, 0x60, 0x2d, 0x6c, 0xe6, 0xee, 0x5a, 0x92, 0x61, 0x3a, 0xbc, 0x67, 0x62, 0xf8, 0x02, - 0x2e, 0xcd, 0x30, 0x75, 0x06, 0xc5, 0xf5, 0x2c, 0xc5, 0x74, 0x4d, 0x1f, 0x52, 0xba, 0x3f, 0xe5, - 0xa1, 0x9a, 0x4d, 0x18, 0xda, 0x80, 0x4b, 0xda, 0x4f, 0x4c, 0xfa, 0x2d, 0x12, 0x33, 0xd2, 0x95, - 0xa7, 0x84, 0x21, 0x9f, 0xf5, 0x09, 0x35, 0xe0, 0xf2, 0xf6, 0xd0, 0x6c, 0xf3, 0x0c, 0x24, 0xa7, - 0xfa, 0x71, 0xe6, 0x37, 0x44, 0xe1, 0x3f, 0x9a, 0x4a, 0x45, 0x22, 0x03, 0xca, 0xab, 0x84, 0x7d, - 0x78, 0x7c, 0x55, 0x79, 0x33, 0xb1, 0x3a, 0x6f, 0xb3, 0x79, 0xd1, 0x27, 0x50, 0xd2, 0x1f, 0xd2, - 0xc6, 0xbc, 0x76, 0xfc, 0x5f, 0x68, 0xb2, 0x14, 0x23, 0xe1, 0xda, 0x0f, 0x6e, 0x17, 0xce, 0x00, - 0x37, 0x18, 0xe7, 0x21, 0x38, 0xf3, 0x4d, 0x3e, 0x4b, 0x09, 0xb8, 0x3f, 0x5a, 0x70, 0x71, 0xea, - 0x8f, 0xe4, 0xad, 0xa1, 0xce, 0x4d, 0x4d, 0xa1, 0xd6, 0xa8, 0x05, 0x05, 0xdd, 0xf9, 0x39, 0x65, - 0xb0, 0x77, 0x0a, 0x83, 0xbd, 0x4c, 0xdb, 0x6b, 0xb0, 0x73, 0x17, 0xe0, 0x7c, 0xc5, 0xea, 0xfe, - 0x6c, 0xc1, 0x92, 0xe9, 0x32, 0x73, 0xc5, 0xfa, 0xb0, 0x92, 0xb6, 0x50, 0xba, 0x67, 0x2e, 0xdb, - 0x3b, 0x73, 0x1b, 0x54, 0xab, 0x79, 0x47, 0x71, 0xda, 0xc6, 0x29, 0x3a, 0x67, 0x2b, 0xad, 0xab, - 0x23, 0xaa, 0x67, 0xb2, 0xfc, 0x2a, 0x2c, 0xb5, 0x85, 0x2f, 0x12, 0x3e, 0xf7, 0xe6, 0x70, 0xff, - 0xb2, 0x60, 0x39, 0xd5, 0x31, 0xde, 0xbd, 0x0f, 0xe5, 0x7d, 0xc2, 0x04, 0x79, 0x45, 0xb8, 0xf1, - 0xca, 0x9e, 0xf6, 0xea, 0x4b, 0xa5, 0x81, 0xc7, 0x9a, 0x68, 0x13, 0xca, 0x5c, 0xf1, 0x90, 0x34, - 0x51, 0xab, 0xf3, 0x50, 0xe6, 0xff, 0xc6, 0xfa, 0xa8, 0x0e, 0x0b, 0x21, 0x1d, 0x70, 0xd3, 0x33, - 0xff, 0x9d, 0x87, 0x7b, 0x4c, 0x07, 0x58, 0x29, 0xa2, 0x8f, 0xa0, 0xfc, 0xd2, 0x67, 0x51, 0x10, - 0x0d, 0xd2, 0x2e, 0x58, 0x9b, 0x07, 0x7a, 0xae, 0xf5, 0xf0, 0x18, 0xe0, 0x1e, 0xe4, 0xa0, 0xa8, - 0xbf, 0xa1, 0x47, 0x50, 0xec, 0x05, 0x03, 0xc2, 0x85, 0x0e, 0x49, 0xb3, 0x21, 0x0f, 0xf9, 0x37, - 0x07, 0x6b, 0x37, 0x32, 0xa7, 0x38, 0x8d, 0x49, 0x24, 0x1f, 0xbb, 0x7e, 0x10, 0x11, 0xc6, 0xeb, - 0x03, 0x7a, 0x4b, 0x43, 0xbc, 0x96, 0xfa, 0xc1, 0x86, 0x41, 0x72, 0x05, 0xfa, 0xac, 0x56, 0xe7, - 0xc5, 0xf9, 0xb8, 0x34, 0x83, 0x6c, 0x83, 0xc8, 0x1f, 0x12, 0x73, 0x37, 0xab, 0xb5, 0x7c, 0x38, - 0x74, 0x65, 0x9d, 0xf7, 0xd4, 0x93, 0xaa, 0x8c, 0x8d, 0x84, 0x36, 0xa1, 0xc4, 0x85, 0xcf, 0xe4, - 0x99, 0x53, 0x38, 0xe5, 0x8b, 0x27, 0x05, 0xa0, 0x4f, 0xa1, 0xd2, 0xa5, 0xc3, 0x38, 0x24, 0x12, - 0x5d, 0x3c, 0x25, 0xfa, 0x10, 0x22, 0x4b, 0x8f, 0x30, 0x46, 0x99, 0x7a, 0x6b, 0x55, 0xb0, 0x16, - 0xdc, 0x3f, 0x73, 0x50, 0xcd, 0x66, 0x7a, 0xea, 0x2d, 0xf9, 0x08, 0x8a, 0xba, 0x6e, 0x74, 0xc9, - 0x9e, 0x2f, 0x54, 0x9a, 0x61, 0x66, 0xa8, 0x6c, 0x28, 0x75, 0x13, 0xa6, 0x1e, 0x9a, 0xfa, 0xf9, - 0x99, 0x8a, 0xd2, 0x60, 0x41, 0x85, 0x1f, 0xaa, 0x50, 0xe5, 0xb1, 0x16, 0xe4, 0xdb, 0x73, 0x3c, - 0x6e, 0x9c, 0xed, 0xed, 0x39, 0x86, 0x65, 0xd3, 0x50, 0x7a, 0xa7, 0x34, 0x94, 0xcf, 0x9c, 0x06, - 0xf7, 0x17, 0x0b, 0x2a, 0xe3, 0x16, 0xc9, 0x44, 0xd7, 0x7a, 0xe7, 0xe8, 0x4e, 0x44, 0x26, 0x77, - 0xbe, 0xc8, 0x5c, 0x81, 0x22, 0x17, 0x8c, 0xf8, 0x43, 0x3d, 0x19, 0x61, 0x23, 0xc9, 0xc3, 0x68, - 0xc8, 0x07, 0x2a, 0x43, 0x55, 0x2c, 0x97, 0xee, 0xdf, 0x16, 0x2c, 0x4d, 0x74, 0xed, 0xbf, 0xea, - 0xcb, 0x65, 0x28, 0x84, 0x64, 0x9f, 0xe8, 0xd9, 0x2d, 0x8f, 0xb5, 0x20, 0x77, 0xf9, 0x2e, 0x65, - 0x42, 0x19, 0x57, 0xc5, 0x5a, 0x90, 0x36, 0xf7, 0x88, 0xf0, 0x83, 0x50, 0x1d, 0x2f, 0x55, 0x6c, - 0x24, 0x69, 0x73, 0xc2, 0x42, 0xf3, 0x7e, 0x95, 0x4b, 0xe4, 0xc2, 0x42, 0x10, 0xf5, 0xa9, 0x29, - 0x1b, 0xf5, 0x40, 0x69, 0xd3, 0x84, 0x75, 0xc9, 0x76, 0xd4, 0xa7, 0x58, 0x7d, 0x43, 0x57, 0xa1, - 0xc8, 0xfc, 0x68, 0x40, 0xd2, 0xc7, 0x6b, 0x45, 0x6a, 0x61, 0xb9, 0x83, 0xcd, 0x07, 0xd7, 0x85, - 0xaa, 0x9a, 0xff, 0x76, 0x08, 0x97, 0xd3, 0x86, 0x2c, 0xeb, 0x9e, 0x2f, 0x7c, 0xe5, 0x76, 0x15, - 0xab, 0xb5, 0x7b, 0x13, 0xd0, 0xe3, 0x80, 0x8b, 0xe7, 0x6a, 0x6e, 0xe5, 0x27, 0x0d, 0x87, 0x6d, - 0xb8, 0x34, 0xa1, 0x6d, 0x4e, 0xf7, 0x8f, 0x8f, 0x8c, 0x87, 0xd7, 0xa7, 0x0f, 0x4e, 0x35, 0x1e, - 0x7b, 0x1a, 0x38, 0x39, 0x25, 0x36, 0xfe, 0xc8, 0x43, 0x69, 0x4b, 0x4f, 0xfe, 0xe8, 0x29, 0x54, - 0xc6, 0xd3, 0x27, 0x72, 0xa7, 0x69, 0x8e, 0x8e, 0xb1, 0xce, 0xb5, 0x63, 0x75, 0x8c, 0x7d, 0x0f, - 0xa1, 0xa0, 0xe6, 0x70, 0x34, 0xe3, 0xfa, 0xc8, 0x0e, 0xe8, 0xce, 0xf1, 0x73, 0xed, 0x86, 0x25, - 0x99, 0xd4, 0xdd, 0x3b, 0x8b, 0x29, 0xfb, 0x6a, 0x76, 0xd6, 0x4e, 0xb8, 0xb4, 0xd1, 0x0e, 0x14, - 0xcd, 0x49, 0x36, 0x4b, 0x35, 0x7b, 0xc3, 0x3a, 0xeb, 0xf3, 0x15, 0x34, 0xd9, 0x86, 0x85, 0x76, - 0xc6, 0x83, 0xd0, 0x2c, 0xd3, 0xb2, 0x65, 0xe0, 0x9c, 0xf0, 0xbd, 0x66, 0x6d, 0x58, 0xe8, 0x05, - 0x2c, 0x66, 0x12, 0x8d, 0x66, 0x24, 0x74, 0xba, 0x6a, 0x9c, 0xff, 0x9f, 0xa0, 0xa5, 0x8d, 0x6d, - 0x56, 0x5f, 0xbf, 0x5d, 0xb5, 0x7e, 0x7d, 0xbb, 0x6a, 0xfd, 0xfe, 0x76, 0xd5, 0xea, 0x14, 0x55, - 0xcb, 0xbf, 0xf7, 0x4f, 0x00, 0x00, 0x00, 0xff, 0xff, 0x8f, 0x2a, 0x40, 0x92, 0xfd, 0x11, 0x00, - 0x00, + // 1543 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x58, 0xcd, 0x6f, 0x1b, 0x45, + 0x14, 0xef, 0xda, 0xf1, 0xd7, 0x8b, 0x13, 0xa5, 0xd3, 0x52, 0xad, 0x16, 0x91, 0xa4, 0xdb, 0x22, + 0x45, 0x55, 0xbb, 0x4e, 0x03, 0x85, 0x12, 0x3e, 0xd4, 0x3a, 0x2e, 0x34, 0x55, 0x23, 0xca, 0xa4, + 0xa5, 0x52, 0x0f, 0x48, 0x6b, 0x7b, 0xbc, 0x59, 0x65, 0xbd, 0xb3, 0xcc, 0xcc, 0xa6, 0x35, 0x7f, + 0x00, 0x67, 0x6e, 0xfc, 0x01, 0x1c, 0x38, 0x71, 0xe6, 0x2f, 0x40, 0xea, 0x91, 0x73, 0x0f, 0x01, + 0xf5, 0x0e, 0xe2, 0xc8, 0x11, 0xcd, 0xc7, 0x3a, 0xeb, 0xd8, 0xce, 0x57, 0x39, 0x79, 0xde, 0xcc, + 0x7b, 0xbf, 0x7d, 0x9f, 0x33, 0xef, 0x19, 0xe6, 0x3a, 0x34, 0x16, 0x8c, 0x46, 0x5e, 0xc2, 0xa8, + 0xa0, 0x68, 0xa1, 0x4f, 0xdb, 0x03, 0xaf, 0x9d, 0x86, 0x51, 0x77, 0x37, 0x14, 0xde, 0xde, 0x4d, + 0xe7, 0x46, 0x10, 0x8a, 0x9d, 0xb4, 0xed, 0x75, 0x68, 0xbf, 0x11, 0xd0, 0x80, 0x36, 0x14, 0x63, + 0x3b, 0xed, 0x29, 0x4a, 0x11, 0x6a, 0xa5, 0x01, 0x9c, 0xa5, 0x80, 0xd2, 0x20, 0x22, 0x07, 0x5c, + 0x22, 0xec, 0x13, 0x2e, 0xfc, 0x7e, 0x62, 0x18, 0xae, 0xe7, 0xf0, 0xe4, 0xc7, 0x1a, 0xd9, 0xc7, + 0x1a, 0x9c, 0x46, 0x7b, 0x84, 0x35, 0x92, 0x76, 0x83, 0x26, 0xdc, 0x70, 0x37, 0xa6, 0x72, 0xfb, + 0x49, 0xd8, 0x10, 0x83, 0x84, 0xf0, 0xc6, 0x73, 0xca, 0x76, 0x09, 0xd3, 0x02, 0xee, 0xf7, 0x16, + 0xd4, 0x1f, 0xb1, 0x34, 0x26, 0x98, 0x7c, 0x9b, 0x12, 0x2e, 0xd0, 0x25, 0x28, 0xf7, 0xc2, 0x48, + 0x10, 0x66, 0x5b, 0xcb, 0xc5, 0x95, 0x1a, 0x36, 0x14, 0x5a, 0x80, 0xa2, 0x1f, 0x45, 0x76, 0x61, + 0xd9, 0x5a, 0xa9, 0x62, 0xb9, 0x44, 0x2b, 0x50, 0xdf, 0x25, 0x24, 0x69, 0xa5, 0xcc, 0x17, 0x21, + 0x8d, 0xed, 0xe2, 0xb2, 0xb5, 0x52, 0x6c, 0xce, 0xbc, 0xdc, 0x5f, 0xb2, 0xf0, 0xc8, 0x09, 0x72, + 0xa1, 0x26, 0xe9, 0xe6, 0x40, 0x10, 0x6e, 0xcf, 0xe4, 0xd8, 0x0e, 0xb6, 0xdd, 0x6b, 0xb0, 0xd0, + 0x0a, 0xf9, 0xee, 0x13, 0xee, 0x07, 0xc7, 0xe9, 0xe2, 0x3e, 0x80, 0xf3, 0x39, 0x5e, 0x9e, 0xd0, + 0x98, 0x13, 0x74, 0x0b, 0xca, 0x8c, 0x74, 0x28, 0xeb, 0x2a, 0xe6, 0xd9, 0xb5, 0x77, 0xbc, 0xc3, + 0xb1, 0xf1, 0x8c, 0x80, 0x64, 0xc2, 0x86, 0xd9, 0xfd, 0xb1, 0x08, 0xb3, 0xb9, 0x7d, 0x34, 0x0f, + 0x85, 0xcd, 0x96, 0x6d, 0x2d, 0x5b, 0x2b, 0x35, 0x5c, 0xd8, 0x6c, 0x21, 0x1b, 0x2a, 0x5b, 0xa9, + 0xf0, 0xdb, 0x11, 0x31, 0xb6, 0x67, 0x24, 0xba, 0x08, 0xa5, 0xcd, 0xf8, 0x09, 0x27, 0xca, 0xf0, + 0x2a, 0xd6, 0x04, 0x42, 0x30, 0xb3, 0x1d, 0x7e, 0x47, 0xb4, 0x99, 0x58, 0xad, 0x91, 0x03, 0xe5, + 0x47, 0x3e, 0x23, 0xb1, 0xb0, 0x4b, 0x12, 0xb7, 0x59, 0xb0, 0x2d, 0x6c, 0x76, 0x50, 0x13, 0x6a, + 0x1b, 0x8c, 0xf8, 0x82, 0x74, 0xef, 0x0a, 0xbb, 0xbc, 0x6c, 0xad, 0xcc, 0xae, 0x39, 0x9e, 0x4e, + 0x0a, 0x2f, 0x4b, 0x0a, 0xef, 0x71, 0x96, 0x14, 0xcd, 0xea, 0xcb, 0xfd, 0xa5, 0x73, 0x3f, 0xfc, + 0x21, 0x7d, 0x37, 0x14, 0x43, 0x77, 0x00, 0x1e, 0xfa, 0x5c, 0x3c, 0xe1, 0x0a, 0xa4, 0x72, 0x2c, + 0xc8, 0x8c, 0x02, 0xc8, 0xc9, 0xa0, 0x45, 0x00, 0xe5, 0x84, 0x0d, 0x9a, 0xc6, 0xc2, 0xae, 0x2a, + 0xdd, 0x73, 0x3b, 0x68, 0x19, 0x66, 0x5b, 0x84, 0x77, 0x58, 0x98, 0xa8, 0x50, 0xd7, 0x94, 0x7b, + 0xf2, 0x5b, 0x12, 0x41, 0x7b, 0xf0, 0xf1, 0x20, 0x21, 0x36, 0x28, 0x86, 0xdc, 0x8e, 0x8c, 0xe5, + 0xf6, 0x8e, 0xcf, 0x48, 0xd7, 0x9e, 0x55, 0xee, 0x32, 0x94, 0xf4, 0xaf, 0xf6, 0x04, 0xb7, 0xeb, + 0x2a, 0xc8, 0x19, 0xe9, 0xfe, 0x54, 0x86, 0xfa, 0xb6, 0xcc, 0xf1, 0x2c, 0x1d, 0x16, 0xa0, 0x88, + 0x49, 0xcf, 0xc4, 0x46, 0x2e, 0x91, 0x07, 0xd0, 0x22, 0xbd, 0x30, 0x0e, 0x95, 0x56, 0x05, 0x65, + 0xf8, 0xbc, 0x97, 0xb4, 0xbd, 0x83, 0x5d, 0x9c, 0xe3, 0x40, 0x0e, 0x54, 0xef, 0xbd, 0x48, 0x28, + 0x93, 0x29, 0x55, 0x54, 0x30, 0x43, 0x1a, 0x3d, 0x85, 0xb9, 0x6c, 0x7d, 0x57, 0x08, 0x26, 0x13, + 0x55, 0xa6, 0xd1, 0xcd, 0xf1, 0x34, 0xca, 0x2b, 0xe5, 0x8d, 0xc8, 0xdc, 0x8b, 0x05, 0x1b, 0xe0, + 0x51, 0x1c, 0x69, 0xe1, 0x36, 0xe1, 0x5c, 0x6a, 0xa8, 0xc2, 0x8f, 0x33, 0x52, 0xaa, 0xf3, 0x39, + 0xa3, 0xb1, 0x20, 0x71, 0x57, 0x85, 0xbe, 0x86, 0x87, 0xb4, 0x54, 0x27, 0x5b, 0x6b, 0x75, 0x2a, + 0x27, 0x52, 0x67, 0x44, 0xc6, 0xa8, 0x33, 0xb2, 0x87, 0xd6, 0xa1, 0xb4, 0xe1, 0x77, 0x76, 0x88, + 0x8a, 0xf2, 0xec, 0xda, 0xe2, 0x38, 0xa0, 0x3a, 0xfe, 0x52, 0x85, 0x95, 0xab, 0x42, 0x3d, 0x87, + 0xb5, 0x08, 0xfa, 0x06, 0xea, 0xf7, 0x62, 0x11, 0x8a, 0x88, 0xf4, 0x55, 0xc4, 0x6a, 0x32, 0x62, + 0xcd, 0xf5, 0x57, 0xfb, 0x4b, 0x1f, 0x4c, 0xbd, 0x78, 0x52, 0x11, 0x46, 0x0d, 0x92, 0x93, 0xf2, + 0x72, 0x10, 0x78, 0x04, 0x0f, 0x3d, 0x83, 0xf9, 0x4c, 0xd9, 0xcd, 0x38, 0x49, 0x05, 0xb7, 0x41, + 0x59, 0xbd, 0x76, 0x42, 0xab, 0xb5, 0x90, 0x36, 0xfb, 0x10, 0x92, 0x73, 0x07, 0xd0, 0x78, 0xac, + 0x64, 0x4e, 0xed, 0x92, 0x41, 0x96, 0x53, 0xbb, 0x64, 0x20, 0xcb, 0x7a, 0xcf, 0x8f, 0x52, 0x5d, + 0xee, 0x35, 0xac, 0x89, 0xf5, 0xc2, 0x6d, 0x4b, 0x22, 0x8c, 0xbb, 0xf7, 0x54, 0x08, 0x5f, 0xc1, + 0x85, 0x09, 0xaa, 0x4e, 0x80, 0xb8, 0x9a, 0x87, 0x18, 0xcf, 0xe9, 0x03, 0x48, 0xf7, 0x97, 0x22, + 0xd4, 0xf3, 0x01, 0x43, 0xab, 0x70, 0x41, 0xdb, 0x89, 0x49, 0xaf, 0x45, 0x12, 0x46, 0x3a, 0xf2, + 0x96, 0x30, 0xe0, 0x93, 0x8e, 0xd0, 0x1a, 0x5c, 0xdc, 0xec, 0x9b, 0x6d, 0x9e, 0x13, 0x29, 0xa8, + 0x7a, 0x9c, 0x78, 0x86, 0x28, 0xbc, 0xa5, 0xa1, 0x94, 0x27, 0x72, 0x42, 0x45, 0x15, 0xb0, 0x8f, + 0x8e, 0xce, 0x2a, 0x6f, 0xa2, 0xac, 0x8e, 0xdb, 0x64, 0x5c, 0xf4, 0x29, 0x54, 0xf4, 0x41, 0x56, + 0x98, 0x57, 0x8e, 0xfe, 0x84, 0x06, 0xcb, 0x64, 0xa4, 0xb8, 0xb6, 0x83, 0xdb, 0xa5, 0x53, 0x88, + 0x1b, 0x19, 0xe7, 0x3e, 0x38, 0xd3, 0x55, 0x3e, 0x4d, 0x0a, 0xb8, 0x3f, 0x5b, 0x70, 0x7e, 0xec, + 0x43, 0xf2, 0xd5, 0x50, 0xf7, 0xa6, 0x86, 0x50, 0x6b, 0xd4, 0x82, 0x92, 0xae, 0xfc, 0x82, 0x52, + 0xd8, 0x3b, 0x81, 0xc2, 0x5e, 0xae, 0xec, 0xb5, 0xb0, 0x73, 0x1b, 0xe0, 0x6c, 0xc9, 0xea, 0xfe, + 0x6a, 0xc1, 0x9c, 0xa9, 0x32, 0xf3, 0xc4, 0xfa, 0xb0, 0x90, 0x95, 0x50, 0xb6, 0x67, 0x1e, 0xdb, + 0x5b, 0x53, 0x0b, 0x54, 0xb3, 0x79, 0x87, 0xe5, 0xb4, 0x8e, 0x63, 0x70, 0xce, 0x46, 0x96, 0x57, + 0x87, 0x58, 0x4f, 0xa5, 0xf9, 0x65, 0x98, 0xdb, 0x16, 0xbe, 0x48, 0xf9, 0xd4, 0x97, 0xc3, 0xfd, + 0xc7, 0x82, 0xf9, 0x8c, 0xc7, 0x58, 0xf7, 0x3e, 0x54, 0xf7, 0x08, 0x13, 0xe4, 0x05, 0xe1, 0xc6, + 0x2a, 0x7b, 0xdc, 0xaa, 0xaf, 0x15, 0x07, 0x1e, 0x72, 0xa2, 0x75, 0xa8, 0x72, 0x85, 0x43, 0xb2, + 0x40, 0x2d, 0x4e, 0x93, 0x32, 0xdf, 0x1b, 0xf2, 0xa3, 0x06, 0xcc, 0x44, 0x34, 0xe0, 0xa6, 0x66, + 0xde, 0x9e, 0x26, 0xf7, 0x90, 0x06, 0x58, 0x31, 0xa2, 0x8f, 0xa1, 0xfa, 0xdc, 0x67, 0x71, 0x18, + 0x07, 0x59, 0x15, 0x2c, 0x4d, 0x13, 0x7a, 0xaa, 0xf9, 0xf0, 0x50, 0x40, 0x76, 0x3a, 0x65, 0x7d, + 0x86, 0x1e, 0x40, 0xb9, 0x1b, 0x06, 0x84, 0x0b, 0xed, 0x92, 0xe6, 0x9a, 0xbc, 0xe4, 0x5f, 0xed, + 0x2f, 0x5d, 0xcb, 0xdd, 0xe2, 0x34, 0x21, 0xb1, 0x6c, 0x76, 0xfd, 0x30, 0x26, 0x8c, 0x37, 0x02, + 0x7a, 0x43, 0x8b, 0x78, 0x2d, 0xf5, 0x83, 0x0d, 0x82, 0xc4, 0x0a, 0xf5, 0x5d, 0xad, 0xee, 0x8b, + 0xb3, 0x61, 0x69, 0x04, 0x59, 0x06, 0xb1, 0xdf, 0x27, 0xe6, 0x6d, 0x56, 0x6b, 0xd9, 0x38, 0x74, + 0x64, 0x9e, 0x77, 0x55, 0x4b, 0x55, 0xc5, 0x86, 0x42, 0xeb, 0x50, 0xe1, 0xc2, 0x67, 0xf2, 0xce, + 0x29, 0x9d, 0xb0, 0xe3, 0xc9, 0x04, 0xd0, 0x67, 0x50, 0xeb, 0xd0, 0x7e, 0x12, 0x11, 0x29, 0x5d, + 0x3e, 0xa1, 0xf4, 0x81, 0x88, 0x4c, 0x3d, 0xc2, 0x18, 0x65, 0xaa, 0xd7, 0xaa, 0x61, 0x4d, 0xa0, + 0x0f, 0x61, 0x2e, 0x61, 0x34, 0x60, 0x84, 0xf3, 0x2f, 0x18, 0x4d, 0x13, 0xf3, 0xc2, 0x9e, 0x97, + 0x97, 0xf7, 0xa3, 0xfc, 0x01, 0x1e, 0xe5, 0x73, 0xff, 0x2e, 0x40, 0x3d, 0x9f, 0x22, 0x63, 0x4d, + 0xe8, 0x03, 0x28, 0xeb, 0x84, 0xd3, 0xb9, 0x7e, 0x36, 0x1f, 0x6b, 0x84, 0x89, 0x3e, 0xb6, 0xa1, + 0xd2, 0x49, 0x99, 0xea, 0x50, 0x75, 0xdf, 0x9a, 0x91, 0xd2, 0x52, 0x41, 0x85, 0x1f, 0x29, 0x1f, + 0x17, 0xb1, 0x26, 0x64, 0xd3, 0x3a, 0x9c, 0x53, 0x4e, 0xd7, 0xb4, 0x0e, 0xc5, 0xf2, 0xf1, 0xab, + 0xbc, 0x51, 0xfc, 0xaa, 0xa7, 0x8e, 0x9f, 0xfb, 0x9b, 0x05, 0xb5, 0x61, 0x6d, 0xe5, 0xbc, 0x6b, + 0xbd, 0xb1, 0x77, 0x47, 0x3c, 0x53, 0x38, 0x9b, 0x67, 0x2e, 0x41, 0x99, 0x0b, 0x46, 0xfc, 0xbe, + 0x1e, 0xa9, 0xb0, 0xa1, 0xe4, 0x2d, 0xd6, 0xe7, 0x81, 0x8a, 0x50, 0x1d, 0xcb, 0xa5, 0xfb, 0xaf, + 0x05, 0x73, 0x23, 0xe5, 0xfe, 0xbf, 0xda, 0x72, 0x11, 0x4a, 0x11, 0xd9, 0x23, 0x7a, 0xe8, 0x2b, + 0x62, 0x4d, 0xc8, 0x5d, 0xbe, 0x43, 0x99, 0x50, 0xca, 0xd5, 0xb1, 0x26, 0xa4, 0xce, 0x5d, 0x22, + 0xfc, 0x30, 0x52, 0xf7, 0x52, 0x1d, 0x1b, 0x4a, 0xea, 0x9c, 0xb2, 0xc8, 0x34, 0xbe, 0x72, 0x89, + 0x5c, 0x98, 0x09, 0xe3, 0x1e, 0x35, 0x69, 0xa3, 0x3a, 0x9b, 0x6d, 0x9a, 0xb2, 0x0e, 0xd9, 0x8c, + 0x7b, 0x14, 0xab, 0x33, 0x74, 0x19, 0xca, 0xcc, 0x8f, 0x03, 0x92, 0x75, 0xbd, 0x35, 0xc9, 0x85, + 0xe5, 0x0e, 0x36, 0x07, 0xae, 0x0b, 0x75, 0x35, 0x38, 0x6e, 0x11, 0x2e, 0xc7, 0x14, 0x99, 0xd6, + 0x5d, 0x5f, 0xf8, 0xca, 0xec, 0x3a, 0x56, 0x6b, 0xf7, 0x3a, 0xa0, 0x87, 0x21, 0x17, 0x4f, 0xd5, + 0xc0, 0xcb, 0x8f, 0x9b, 0x2a, 0xb7, 0xe1, 0xc2, 0x08, 0xb7, 0x79, 0x16, 0x3e, 0x39, 0x34, 0x57, + 0x5e, 0x1d, 0xbf, 0x71, 0xd5, 0x5c, 0xed, 0x69, 0xc1, 0xd1, 0xf1, 0x72, 0xed, 0xaf, 0x22, 0x54, + 0x36, 0xf4, 0x5f, 0x06, 0xe8, 0x31, 0xd4, 0x86, 0x63, 0x2b, 0x72, 0xc7, 0x61, 0x0e, 0xcf, 0xbf, + 0xce, 0x95, 0x23, 0x79, 0x8c, 0x7e, 0xf7, 0xa1, 0xa4, 0x06, 0x78, 0x34, 0xe1, 0xdd, 0xc9, 0x4f, + 0xf6, 0xce, 0xd1, 0x03, 0xf1, 0xaa, 0x25, 0x91, 0xd4, 0xa3, 0x3d, 0x09, 0x29, 0xdf, 0x6e, 0x3b, + 0x4b, 0xc7, 0xbc, 0xf6, 0x68, 0x0b, 0xca, 0xe6, 0x26, 0x9b, 0xc4, 0x9a, 0x7f, 0x9a, 0x9d, 0xe5, + 0xe9, 0x0c, 0x1a, 0x6c, 0xd5, 0x42, 0x5b, 0xc3, 0x09, 0x6a, 0x92, 0x6a, 0xf9, 0x34, 0x70, 0x8e, + 0x39, 0x5f, 0xb1, 0x56, 0x2d, 0xf4, 0x0c, 0x66, 0x73, 0x81, 0x46, 0x13, 0x02, 0x3a, 0x9e, 0x35, + 0xce, 0xbb, 0xc7, 0x70, 0x69, 0x65, 0x9b, 0xf5, 0x97, 0xaf, 0x17, 0xad, 0xdf, 0x5f, 0x2f, 0x5a, + 0x7f, 0xbe, 0x5e, 0xb4, 0xda, 0x65, 0x55, 0xf2, 0xef, 0xfd, 0x17, 0x00, 0x00, 0xff, 0xff, 0x54, + 0x8e, 0x72, 0x11, 0x36, 0x12, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -2442,6 +2451,18 @@ func (m *Vertex) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if m.ProgressGroup != nil { + { + size, err := m.ProgressGroup.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x42 + } if len(m.Error) > 0 { i -= len(m.Error) copy(dAtA[i:], m.Error) @@ -2450,22 +2471,22 @@ func (m *Vertex) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x3a } if m.Completed != nil { - n6, err6 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.Completed, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.Completed):]) - if err6 != nil { - return 0, err6 + n7, err7 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.Completed, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.Completed):]) + if err7 != nil { + return 0, err7 } - i -= n6 - i = encodeVarintControl(dAtA, i, uint64(n6)) + i -= n7 + i = encodeVarintControl(dAtA, i, uint64(n7)) i-- dAtA[i] = 0x32 } if m.Started != nil { - n7, err7 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.Started, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.Started):]) - if err7 != nil { - return 0, err7 + n8, err8 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.Started, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.Started):]) + if err8 != nil { + return 0, err8 } - i -= n7 - i = encodeVarintControl(dAtA, i, uint64(n7)) + i -= n8 + i = encodeVarintControl(dAtA, i, uint64(n8)) i-- dAtA[i] = 0x2a } @@ -2530,31 +2551,31 @@ func (m *VertexStatus) MarshalToSizedBuffer(dAtA []byte) (int, error) { copy(dAtA[i:], m.XXX_unrecognized) } if m.Completed != nil { - n8, err8 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.Completed, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.Completed):]) - if err8 != nil { - return 0, err8 + n9, err9 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.Completed, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.Completed):]) + if err9 != nil { + return 0, err9 } - i -= n8 - i = encodeVarintControl(dAtA, i, uint64(n8)) + i -= n9 + i = encodeVarintControl(dAtA, i, uint64(n9)) i-- dAtA[i] = 0x42 } if m.Started != nil { - n9, err9 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.Started, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.Started):]) - if err9 != nil { - return 0, err9 + n10, err10 := github_com_gogo_protobuf_types.StdTimeMarshalTo(*m.Started, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(*m.Started):]) + if err10 != nil { + return 0, err10 } - i -= n9 - i = encodeVarintControl(dAtA, i, uint64(n9)) + i -= n10 + i = encodeVarintControl(dAtA, i, uint64(n10)) i-- dAtA[i] = 0x3a } - n10, err10 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Timestamp, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Timestamp):]) - if err10 != nil { - return 0, err10 + n11, err11 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Timestamp, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Timestamp):]) + if err11 != nil { + return 0, err11 } - i -= n10 - i = encodeVarintControl(dAtA, i, uint64(n10)) + i -= n11 + i = encodeVarintControl(dAtA, i, uint64(n11)) i-- dAtA[i] = 0x32 if m.Total != 0 { @@ -2627,12 +2648,12 @@ func (m *VertexLog) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x18 } - n11, err11 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Timestamp, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Timestamp):]) - if err11 != nil { - return 0, err11 + n12, err12 := github_com_gogo_protobuf_types.StdTimeMarshalTo(m.Timestamp, dAtA[i-github_com_gogo_protobuf_types.SizeOfStdTime(m.Timestamp):]) + if err12 != nil { + return 0, err12 } - i -= n11 - i = encodeVarintControl(dAtA, i, uint64(n11)) + i -= n12 + i = encodeVarintControl(dAtA, i, uint64(n12)) i-- dAtA[i] = 0x12 if len(m.Vertex) > 0 { @@ -3215,6 +3236,10 @@ func (m *Vertex) Size() (n int) { if l > 0 { n += 1 + l + sovControl(uint64(l)) } + if m.ProgressGroup != nil { + l = m.ProgressGroup.Size() + n += 1 + l + sovControl(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -5954,6 +5979,42 @@ func (m *Vertex) Unmarshal(dAtA []byte) error { } m.Error = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex + case 8: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProgressGroup", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthControl + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ProgressGroup == nil { + m.ProgressGroup = &pb.ProgressGroup{} + } + if err := m.ProgressGroup.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipControl(dAtA[iNdEx:]) diff --git a/api/services/control/control.proto b/api/services/control/control.proto index 4f41352ec2ed..a468a293af88 100644 --- a/api/services/control/control.proto +++ b/api/services/control/control.proto @@ -115,6 +115,7 @@ message Vertex { google.protobuf.Timestamp started = 5 [(gogoproto.stdtime) = true ]; google.protobuf.Timestamp completed = 6 [(gogoproto.stdtime) = true ]; string error = 7; // typed errors? + pb.ProgressGroup progressGroup = 8; } message VertexStatus { diff --git a/cache/manager.go b/cache/manager.go index cadc32f10cf7..512981d36232 100644 --- a/cache/manager.go +++ b/cache/manager.go @@ -725,6 +725,7 @@ func (cm *cacheManager) createMergeRef(ctx context.Context, parents parentRefs, } if len(parents.mergeParents) == 1 { // merge of 1 thing is that thing + parents.mergeParents[0].progress = pg return parents.mergeParents[0], nil } diff --git a/client/graph.go b/client/graph.go index 6171e7db7021..aaa96f293f1a 100644 --- a/client/graph.go +++ b/client/graph.go @@ -8,13 +8,14 @@ import ( ) type Vertex struct { - Digest digest.Digest - Inputs []digest.Digest - Name string - Started *time.Time - Completed *time.Time - Cached bool - Error string + Digest digest.Digest + Inputs []digest.Digest + Name string + Started *time.Time + Completed *time.Time + Cached bool + Error string + ProgressGroup *pb.ProgressGroup } type VertexStatus struct { diff --git a/client/llb/state.go b/client/llb/state.go index 3ff9d5051332..722affb52343 100644 --- a/client/llb/state.go +++ b/client/llb/state.go @@ -505,6 +505,10 @@ func mergeMetadata(m1, m2 pb.OpMetadata) pb.OpMetadata { m1.Caps[k] = true } + if m2.ProgressGroup != nil { + m1.ProgressGroup = m2.ProgressGroup + } + return m1 } @@ -594,6 +598,12 @@ func LocalUniqueID(v string) ConstraintsOpt { }) } +func ProgressGroup(id, name string) ConstraintsOpt { + return constraintsOptFunc(func(c *Constraints) { + c.Metadata.ProgressGroup = &pb.ProgressGroup{Id: id, Name: name} + }) +} + var ( LinuxAmd64 = Platform(ocispecs.Platform{OS: "linux", Architecture: "amd64"}) LinuxArmhf = Platform(ocispecs.Platform{OS: "linux", Architecture: "arm", Variant: "v7"}) diff --git a/client/solve.go b/client/solve.go index f7b653d459fa..40a12c7a89de 100644 --- a/client/solve.go +++ b/client/solve.go @@ -263,13 +263,14 @@ func (c *Client) solve(ctx context.Context, def *llb.Definition, runGateway runG s := SolveStatus{} for _, v := range resp.Vertexes { s.Vertexes = append(s.Vertexes, &Vertex{ - Digest: v.Digest, - Inputs: v.Inputs, - Name: v.Name, - Started: v.Started, - Completed: v.Completed, - Error: v.Error, - Cached: v.Cached, + Digest: v.Digest, + Inputs: v.Inputs, + Name: v.Name, + Started: v.Started, + Completed: v.Completed, + Error: v.Error, + Cached: v.Cached, + ProgressGroup: v.ProgressGroup, }) } for _, v := range resp.Statuses { diff --git a/control/control.go b/control/control.go index c778e2997f40..0d3e7976e5b7 100644 --- a/control/control.go +++ b/control/control.go @@ -338,13 +338,14 @@ func (c *Controller) Status(req *controlapi.StatusRequest, stream controlapi.Con sr := controlapi.StatusResponse{} for _, v := range ss.Vertexes { sr.Vertexes = append(sr.Vertexes, &controlapi.Vertex{ - Digest: v.Digest, - Inputs: v.Inputs, - Name: v.Name, - Started: v.Started, - Completed: v.Completed, - Error: v.Error, - Cached: v.Cached, + Digest: v.Digest, + Inputs: v.Inputs, + Name: v.Name, + Started: v.Started, + Completed: v.Completed, + Error: v.Error, + Cached: v.Cached, + ProgressGroup: v.ProgressGroup, }) } for _, v := range ss.Statuses { diff --git a/solver/jobs.go b/solver/jobs.go index a67b2b272454..83f7e367e6bb 100644 --- a/solver/jobs.go +++ b/solver/jobs.go @@ -889,9 +889,10 @@ func initClientVertex(v Vertex) client.Vertex { inputDigests = append(inputDigests, inp.Vertex.Digest()) } return client.Vertex{ - Inputs: inputDigests, - Name: v.Name(), - Digest: v.Digest(), + Inputs: inputDigests, + Name: v.Name(), + Digest: v.Digest(), + ProgressGroup: v.Options().ProgressGroup, } } diff --git a/solver/llbsolver/ops/diff.go b/solver/llbsolver/ops/diff.go index a2f47b915967..1a05f7a6c7ca 100644 --- a/solver/llbsolver/ops/diff.go +++ b/solver/llbsolver/ops/diff.go @@ -71,6 +71,7 @@ func (d *diffOp) CacheMap(ctx context.Context, group session.Group, index int) ( WriterFactory: progress.FromContext(ctx), Digest: d.vtx.Digest(), Name: d.vtx.Name(), + ProgressGroup: d.vtx.Options().ProgressGroup, } cm.Opts[cache.ProgressKey{}] = d.pg diff --git a/solver/llbsolver/ops/merge.go b/solver/llbsolver/ops/merge.go index aee4bee47073..13bb60ba88a7 100644 --- a/solver/llbsolver/ops/merge.go +++ b/solver/llbsolver/ops/merge.go @@ -63,6 +63,7 @@ func (m *mergeOp) CacheMap(ctx context.Context, group session.Group, index int) WriterFactory: progress.FromContext(ctx), Digest: m.vtx.Digest(), Name: m.vtx.Name(), + ProgressGroup: m.vtx.Options().ProgressGroup, } cm.Opts[cache.ProgressKey{}] = m.pg diff --git a/solver/llbsolver/vertex.go b/solver/llbsolver/vertex.go index 0c9460f969bd..4f36c2eddbb3 100644 --- a/solver/llbsolver/vertex.go +++ b/solver/llbsolver/vertex.go @@ -162,6 +162,7 @@ func newVertex(dgst digest.Digest, op *pb.Op, opMeta *pb.OpMetadata, load func(d if opMeta.ExportCache != nil { opt.ExportCache = &opMeta.ExportCache.Value } + opt.ProgressGroup = opMeta.ProgressGroup } for _, fn := range opts { if err := fn(op, opMeta, &opt); err != nil { diff --git a/solver/pb/ops.pb.go b/solver/pb/ops.pb.go index c65e598a1a15..cf79d7ee125a 100644 --- a/solver/pb/ops.pb.go +++ b/solver/pb/ops.pb.go @@ -1270,8 +1270,9 @@ type OpMetadata struct { Description map[string]string `protobuf:"bytes,2,rep,name=description,proto3" json:"description,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` // index 3 reserved for WorkerConstraint in previous versions // WorkerConstraint worker_constraint = 3; - ExportCache *ExportCache `protobuf:"bytes,4,opt,name=export_cache,json=exportCache,proto3" json:"export_cache,omitempty"` - Caps map[github_com_moby_buildkit_util_apicaps.CapID]bool `protobuf:"bytes,5,rep,name=caps,proto3,castkey=github.com/moby/buildkit/util/apicaps.CapID" json:"caps" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` + ExportCache *ExportCache `protobuf:"bytes,4,opt,name=export_cache,json=exportCache,proto3" json:"export_cache,omitempty"` + Caps map[github_com_moby_buildkit_util_apicaps.CapID]bool `protobuf:"bytes,5,rep,name=caps,proto3,castkey=github.com/moby/buildkit/util/apicaps.CapID" json:"caps" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"varint,2,opt,name=value,proto3"` + ProgressGroup *ProgressGroup `protobuf:"bytes,6,opt,name=progress_group,json=progressGroup,proto3" json:"progress_group,omitempty"` } func (m *OpMetadata) Reset() { *m = OpMetadata{} } @@ -1331,6 +1332,13 @@ func (m *OpMetadata) GetCaps() map[github_com_moby_buildkit_util_apicaps.CapID]b return nil } +func (m *OpMetadata) GetProgressGroup() *ProgressGroup { + if m != nil { + return m.ProgressGroup + } + return nil +} + // Source is a source mapping description for a file type Source struct { Locations map[string]*Locations `protobuf:"bytes,1,rep,name=locations,proto3" json:"locations,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` @@ -1665,6 +1673,54 @@ func (m *ExportCache) GetValue() bool { return false } +type ProgressGroup struct { + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` + Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` +} + +func (m *ProgressGroup) Reset() { *m = ProgressGroup{} } +func (m *ProgressGroup) String() string { return proto.CompactTextString(m) } +func (*ProgressGroup) ProtoMessage() {} +func (*ProgressGroup) Descriptor() ([]byte, []int) { + return fileDescriptor_8de16154b2733812, []int{24} +} +func (m *ProgressGroup) XXX_Unmarshal(b []byte) error { + return m.Unmarshal(b) +} +func (m *ProgressGroup) XXX_Marshal(b []byte, deterministic bool) ([]byte, error) { + b = b[:cap(b)] + n, err := m.MarshalToSizedBuffer(b) + if err != nil { + return nil, err + } + return b[:n], nil +} +func (m *ProgressGroup) XXX_Merge(src proto.Message) { + xxx_messageInfo_ProgressGroup.Merge(m, src) +} +func (m *ProgressGroup) XXX_Size() int { + return m.Size() +} +func (m *ProgressGroup) XXX_DiscardUnknown() { + xxx_messageInfo_ProgressGroup.DiscardUnknown(m) +} + +var xxx_messageInfo_ProgressGroup proto.InternalMessageInfo + +func (m *ProgressGroup) GetId() string { + if m != nil { + return m.Id + } + return "" +} + +func (m *ProgressGroup) GetName() string { + if m != nil { + return m.Name + } + return "" +} + type ProxyEnv struct { HttpProxy string `protobuf:"bytes,1,opt,name=http_proxy,json=httpProxy,proto3" json:"http_proxy,omitempty"` HttpsProxy string `protobuf:"bytes,2,opt,name=https_proxy,json=httpsProxy,proto3" json:"https_proxy,omitempty"` @@ -1677,7 +1733,7 @@ func (m *ProxyEnv) Reset() { *m = ProxyEnv{} } func (m *ProxyEnv) String() string { return proto.CompactTextString(m) } func (*ProxyEnv) ProtoMessage() {} func (*ProxyEnv) Descriptor() ([]byte, []int) { - return fileDescriptor_8de16154b2733812, []int{24} + return fileDescriptor_8de16154b2733812, []int{25} } func (m *ProxyEnv) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1746,7 +1802,7 @@ func (m *WorkerConstraints) Reset() { *m = WorkerConstraints{} } func (m *WorkerConstraints) String() string { return proto.CompactTextString(m) } func (*WorkerConstraints) ProtoMessage() {} func (*WorkerConstraints) Descriptor() ([]byte, []int) { - return fileDescriptor_8de16154b2733812, []int{25} + return fileDescriptor_8de16154b2733812, []int{26} } func (m *WorkerConstraints) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1793,7 +1849,7 @@ func (m *Definition) Reset() { *m = Definition{} } func (m *Definition) String() string { return proto.CompactTextString(m) } func (*Definition) ProtoMessage() {} func (*Definition) Descriptor() ([]byte, []int) { - return fileDescriptor_8de16154b2733812, []int{26} + return fileDescriptor_8de16154b2733812, []int{27} } func (m *Definition) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1847,7 +1903,7 @@ func (m *FileOp) Reset() { *m = FileOp{} } func (m *FileOp) String() string { return proto.CompactTextString(m) } func (*FileOp) ProtoMessage() {} func (*FileOp) Descriptor() ([]byte, []int) { - return fileDescriptor_8de16154b2733812, []int{27} + return fileDescriptor_8de16154b2733812, []int{28} } func (m *FileOp) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -1895,7 +1951,7 @@ func (m *FileAction) Reset() { *m = FileAction{} } func (m *FileAction) String() string { return proto.CompactTextString(m) } func (*FileAction) ProtoMessage() {} func (*FileAction) Descriptor() ([]byte, []int) { - return fileDescriptor_8de16154b2733812, []int{28} + return fileDescriptor_8de16154b2733812, []int{29} } func (m *FileAction) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2022,7 +2078,7 @@ func (m *FileActionCopy) Reset() { *m = FileActionCopy{} } func (m *FileActionCopy) String() string { return proto.CompactTextString(m) } func (*FileActionCopy) ProtoMessage() {} func (*FileActionCopy) Descriptor() ([]byte, []int) { - return fileDescriptor_8de16154b2733812, []int{29} + return fileDescriptor_8de16154b2733812, []int{30} } func (m *FileActionCopy) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2155,7 +2211,7 @@ func (m *FileActionMkFile) Reset() { *m = FileActionMkFile{} } func (m *FileActionMkFile) String() string { return proto.CompactTextString(m) } func (*FileActionMkFile) ProtoMessage() {} func (*FileActionMkFile) Descriptor() ([]byte, []int) { - return fileDescriptor_8de16154b2733812, []int{30} + return fileDescriptor_8de16154b2733812, []int{31} } func (m *FileActionMkFile) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2232,7 +2288,7 @@ func (m *FileActionMkDir) Reset() { *m = FileActionMkDir{} } func (m *FileActionMkDir) String() string { return proto.CompactTextString(m) } func (*FileActionMkDir) ProtoMessage() {} func (*FileActionMkDir) Descriptor() ([]byte, []int) { - return fileDescriptor_8de16154b2733812, []int{31} + return fileDescriptor_8de16154b2733812, []int{32} } func (m *FileActionMkDir) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2305,7 +2361,7 @@ func (m *FileActionRm) Reset() { *m = FileActionRm{} } func (m *FileActionRm) String() string { return proto.CompactTextString(m) } func (*FileActionRm) ProtoMessage() {} func (*FileActionRm) Descriptor() ([]byte, []int) { - return fileDescriptor_8de16154b2733812, []int{32} + return fileDescriptor_8de16154b2733812, []int{33} } func (m *FileActionRm) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2360,7 +2416,7 @@ func (m *ChownOpt) Reset() { *m = ChownOpt{} } func (m *ChownOpt) String() string { return proto.CompactTextString(m) } func (*ChownOpt) ProtoMessage() {} func (*ChownOpt) Descriptor() ([]byte, []int) { - return fileDescriptor_8de16154b2733812, []int{33} + return fileDescriptor_8de16154b2733812, []int{34} } func (m *ChownOpt) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2410,7 +2466,7 @@ func (m *UserOpt) Reset() { *m = UserOpt{} } func (m *UserOpt) String() string { return proto.CompactTextString(m) } func (*UserOpt) ProtoMessage() {} func (*UserOpt) Descriptor() ([]byte, []int) { - return fileDescriptor_8de16154b2733812, []int{34} + return fileDescriptor_8de16154b2733812, []int{35} } func (m *UserOpt) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2489,7 +2545,7 @@ func (m *NamedUserOpt) Reset() { *m = NamedUserOpt{} } func (m *NamedUserOpt) String() string { return proto.CompactTextString(m) } func (*NamedUserOpt) ProtoMessage() {} func (*NamedUserOpt) Descriptor() ([]byte, []int) { - return fileDescriptor_8de16154b2733812, []int{35} + return fileDescriptor_8de16154b2733812, []int{36} } func (m *NamedUserOpt) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2529,7 +2585,7 @@ func (m *MergeInput) Reset() { *m = MergeInput{} } func (m *MergeInput) String() string { return proto.CompactTextString(m) } func (*MergeInput) ProtoMessage() {} func (*MergeInput) Descriptor() ([]byte, []int) { - return fileDescriptor_8de16154b2733812, []int{36} + return fileDescriptor_8de16154b2733812, []int{37} } func (m *MergeInput) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2562,7 +2618,7 @@ func (m *MergeOp) Reset() { *m = MergeOp{} } func (m *MergeOp) String() string { return proto.CompactTextString(m) } func (*MergeOp) ProtoMessage() {} func (*MergeOp) Descriptor() ([]byte, []int) { - return fileDescriptor_8de16154b2733812, []int{37} + return fileDescriptor_8de16154b2733812, []int{38} } func (m *MergeOp) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2602,7 +2658,7 @@ func (m *LowerDiffInput) Reset() { *m = LowerDiffInput{} } func (m *LowerDiffInput) String() string { return proto.CompactTextString(m) } func (*LowerDiffInput) ProtoMessage() {} func (*LowerDiffInput) Descriptor() ([]byte, []int) { - return fileDescriptor_8de16154b2733812, []int{38} + return fileDescriptor_8de16154b2733812, []int{39} } func (m *LowerDiffInput) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2635,7 +2691,7 @@ func (m *UpperDiffInput) Reset() { *m = UpperDiffInput{} } func (m *UpperDiffInput) String() string { return proto.CompactTextString(m) } func (*UpperDiffInput) ProtoMessage() {} func (*UpperDiffInput) Descriptor() ([]byte, []int) { - return fileDescriptor_8de16154b2733812, []int{39} + return fileDescriptor_8de16154b2733812, []int{40} } func (m *UpperDiffInput) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2669,7 +2725,7 @@ func (m *DiffOp) Reset() { *m = DiffOp{} } func (m *DiffOp) String() string { return proto.CompactTextString(m) } func (*DiffOp) ProtoMessage() {} func (*DiffOp) Descriptor() ([]byte, []int) { - return fileDescriptor_8de16154b2733812, []int{40} + return fileDescriptor_8de16154b2733812, []int{41} } func (m *DiffOp) XXX_Unmarshal(b []byte) error { return m.Unmarshal(b) @@ -2743,6 +2799,7 @@ func init() { proto.RegisterType((*Range)(nil), "pb.Range") proto.RegisterType((*Position)(nil), "pb.Position") proto.RegisterType((*ExportCache)(nil), "pb.ExportCache") + proto.RegisterType((*ProgressGroup)(nil), "pb.ProgressGroup") proto.RegisterType((*ProxyEnv)(nil), "pb.ProxyEnv") proto.RegisterType((*WorkerConstraints)(nil), "pb.WorkerConstraints") proto.RegisterType((*Definition)(nil), "pb.Definition") @@ -2766,163 +2823,166 @@ func init() { func init() { proto.RegisterFile("ops.proto", fileDescriptor_8de16154b2733812) } var fileDescriptor_8de16154b2733812 = []byte{ - // 2486 bytes of a gzipped FileDescriptorProto + // 2529 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xbc, 0x59, 0xcf, 0x6f, 0x1b, 0xc7, - 0xf5, 0x17, 0x7f, 0x93, 0x8f, 0x12, 0xcd, 0xef, 0xd8, 0x49, 0x18, 0x7d, 0x5d, 0x59, 0xd9, 0xa4, - 0x81, 0x2c, 0xdb, 0x12, 0xaa, 0x00, 0x71, 0x60, 0x14, 0x45, 0x25, 0x91, 0x86, 0x18, 0xdb, 0xa2, + 0xf5, 0x17, 0x7f, 0x93, 0x8f, 0x12, 0xcd, 0x8c, 0x9d, 0x84, 0xd1, 0xd7, 0x5f, 0x59, 0xd9, 0xa4, + 0x81, 0x2c, 0xdb, 0x12, 0xaa, 0x00, 0x71, 0x60, 0x14, 0x45, 0x25, 0x91, 0xae, 0x18, 0xdb, 0xa2, 0x30, 0xb4, 0x9d, 0x1e, 0x0a, 0x18, 0xab, 0xe5, 0x90, 0x5a, 0x68, 0xb9, 0xb3, 0x98, 0x1d, 0xc6, - 0x62, 0x0f, 0x3d, 0xe4, 0x5e, 0x20, 0x40, 0x81, 0xa2, 0x97, 0xa2, 0xff, 0x44, 0xaf, 0xbd, 0x07, - 0xe8, 0x25, 0x87, 0x1e, 0x82, 0x1e, 0xd2, 0xc2, 0xb9, 0xf4, 0x8f, 0x68, 0x81, 0xe2, 0xbd, 0x99, - 0xfd, 0x41, 0x5a, 0x81, 0xed, 0xb6, 0xe8, 0x89, 0xb3, 0x9f, 0xf7, 0x99, 0xf7, 0xde, 0xcc, 0xbc, - 0x37, 0xf3, 0x66, 0x08, 0x0d, 0x19, 0xc5, 0x3b, 0x91, 0x92, 0x5a, 0xb2, 0x62, 0x74, 0xba, 0x7e, - 0x67, 0xe2, 0xeb, 0xb3, 0xd9, 0xe9, 0x8e, 0x27, 0xa7, 0xbb, 0x13, 0x39, 0x91, 0xbb, 0x24, 0x3a, - 0x9d, 0x8d, 0xe9, 0x8b, 0x3e, 0xa8, 0x65, 0xba, 0x38, 0x7f, 0x2f, 0x42, 0x71, 0x10, 0xb1, 0xf7, - 0xa0, 0xea, 0x87, 0xd1, 0x4c, 0xc7, 0x9d, 0xc2, 0x66, 0x69, 0xab, 0xb9, 0xd7, 0xd8, 0x89, 0x4e, - 0x77, 0xfa, 0x88, 0x70, 0x2b, 0x60, 0x9b, 0x50, 0x16, 0x17, 0xc2, 0xeb, 0x14, 0x37, 0x0b, 0x5b, - 0xcd, 0x3d, 0x40, 0x42, 0xef, 0x42, 0x78, 0x83, 0xe8, 0x68, 0x85, 0x93, 0x84, 0x7d, 0x08, 0xd5, - 0x58, 0xce, 0x94, 0x27, 0x3a, 0x25, 0xe2, 0xac, 0x22, 0x67, 0x48, 0x08, 0xb1, 0xac, 0x14, 0x35, - 0x8d, 0xfd, 0x40, 0x74, 0xca, 0x99, 0xa6, 0xfb, 0x7e, 0x60, 0x38, 0x24, 0x61, 0xef, 0x43, 0xe5, - 0x74, 0xe6, 0x07, 0xa3, 0x4e, 0x85, 0x28, 0x4d, 0xa4, 0x1c, 0x20, 0x40, 0x1c, 0x23, 0x43, 0xd2, - 0x54, 0xa8, 0x89, 0xe8, 0x54, 0x33, 0xd2, 0x23, 0x04, 0x0c, 0x89, 0x64, 0x68, 0x6b, 0xe4, 0x8f, - 0xc7, 0x9d, 0x5a, 0x66, 0xab, 0xeb, 0x8f, 0xc7, 0xc6, 0x16, 0x4a, 0xd8, 0x16, 0xd4, 0xa3, 0xc0, - 0xd5, 0x63, 0xa9, 0xa6, 0x1d, 0xc8, 0xfc, 0x3e, 0xb1, 0x18, 0x4f, 0xa5, 0xec, 0x2e, 0x34, 0x3d, - 0x19, 0xc6, 0x5a, 0xb9, 0x7e, 0xa8, 0xe3, 0x4e, 0x93, 0xc8, 0x6f, 0x21, 0xf9, 0x33, 0xa9, 0xce, - 0x85, 0x3a, 0xcc, 0x84, 0x3c, 0xcf, 0x3c, 0x28, 0x43, 0x51, 0x46, 0xce, 0x6f, 0x0a, 0x50, 0x4f, - 0xb4, 0x32, 0x07, 0x56, 0xf7, 0x95, 0x77, 0xe6, 0x6b, 0xe1, 0xe9, 0x99, 0x12, 0x9d, 0xc2, 0x66, - 0x61, 0xab, 0xc1, 0x17, 0x30, 0xd6, 0x82, 0xe2, 0x60, 0x48, 0xf3, 0xdd, 0xe0, 0xc5, 0xc1, 0x90, - 0x75, 0xa0, 0xf6, 0xd4, 0x55, 0xbe, 0x1b, 0x6a, 0x9a, 0xe0, 0x06, 0x4f, 0x3e, 0xd9, 0x75, 0x68, - 0x0c, 0x86, 0x4f, 0x85, 0x8a, 0x7d, 0x19, 0xd2, 0xb4, 0x36, 0x78, 0x06, 0xb0, 0x0d, 0x80, 0xc1, - 0xf0, 0xbe, 0x70, 0x51, 0x69, 0xdc, 0xa9, 0x6c, 0x96, 0xb6, 0x1a, 0x3c, 0x87, 0x38, 0xbf, 0x84, - 0x0a, 0x2d, 0x35, 0xfb, 0x14, 0xaa, 0x23, 0x7f, 0x22, 0x62, 0x6d, 0xdc, 0x39, 0xd8, 0xfb, 0xea, - 0xdb, 0x1b, 0x2b, 0x7f, 0xf9, 0xf6, 0xc6, 0x76, 0x2e, 0xa6, 0x64, 0x24, 0x42, 0x4f, 0x86, 0xda, - 0xf5, 0x43, 0xa1, 0xe2, 0xdd, 0x89, 0xbc, 0x63, 0xba, 0xec, 0x74, 0xe9, 0x87, 0x5b, 0x0d, 0xec, - 0x26, 0x54, 0xfc, 0x70, 0x24, 0x2e, 0xc8, 0xff, 0xd2, 0xc1, 0x55, 0xab, 0xaa, 0x39, 0x98, 0xe9, - 0x68, 0xa6, 0xfb, 0x28, 0xe2, 0x86, 0xe1, 0xfc, 0xa9, 0x00, 0x55, 0x13, 0x4a, 0xec, 0x3a, 0x94, - 0xa7, 0x42, 0xbb, 0x64, 0xbf, 0xb9, 0x57, 0x37, 0x4b, 0xaa, 0x5d, 0x4e, 0x28, 0x46, 0xe9, 0x54, - 0xce, 0x70, 0xee, 0x8b, 0x59, 0x94, 0x3e, 0x42, 0x84, 0x5b, 0x01, 0xfb, 0x21, 0xd4, 0x42, 0xa1, - 0x9f, 0x4b, 0x75, 0x4e, 0x73, 0xd4, 0x32, 0x61, 0x71, 0x2c, 0xf4, 0x23, 0x39, 0x12, 0x3c, 0x91, - 0xb1, 0xdb, 0x50, 0x8f, 0x85, 0x37, 0x53, 0xbe, 0x9e, 0xd3, 0x7c, 0xb5, 0xf6, 0xda, 0x14, 0xac, - 0x16, 0x23, 0x72, 0xca, 0x60, 0xb7, 0xa0, 0x11, 0x0b, 0x4f, 0x09, 0x2d, 0xc2, 0xcf, 0x69, 0xfe, - 0x9a, 0x7b, 0x6b, 0x96, 0xae, 0x84, 0xee, 0x85, 0x9f, 0xf3, 0x4c, 0xee, 0xfc, 0xaa, 0x08, 0x65, - 0xf4, 0x99, 0x31, 0x28, 0xbb, 0x6a, 0x62, 0x32, 0xaa, 0xc1, 0xa9, 0xcd, 0xda, 0x50, 0x42, 0x1d, - 0x45, 0x82, 0xb0, 0x89, 0x88, 0xf7, 0x7c, 0x64, 0x17, 0x14, 0x9b, 0xd8, 0x6f, 0x16, 0x0b, 0x65, - 0xd7, 0x91, 0xda, 0xec, 0x26, 0x34, 0x22, 0x25, 0x2f, 0xe6, 0xcf, 0x8c, 0x07, 0x59, 0x94, 0x22, - 0x88, 0x0e, 0xd4, 0x23, 0xdb, 0x62, 0xdb, 0x00, 0xe2, 0x42, 0x2b, 0xf7, 0x48, 0xc6, 0x3a, 0xee, - 0x54, 0xc9, 0x5b, 0x8a, 0x7b, 0x04, 0xfa, 0x27, 0x3c, 0x27, 0x65, 0xeb, 0x50, 0x3f, 0x93, 0xb1, - 0x0e, 0xdd, 0xa9, 0xa0, 0x0c, 0x69, 0xf0, 0xf4, 0x9b, 0x39, 0x50, 0x9d, 0x05, 0xfe, 0xd4, 0xd7, - 0x9d, 0x46, 0xa6, 0xe3, 0x09, 0x21, 0xdc, 0x4a, 0x30, 0x8a, 0xbd, 0x89, 0x92, 0xb3, 0xe8, 0xc4, - 0x55, 0x22, 0xd4, 0x94, 0x3f, 0x0d, 0xbe, 0x80, 0x39, 0xb7, 0xa1, 0x6a, 0x2c, 0xe3, 0xc0, 0xb0, - 0x65, 0x63, 0x9d, 0xda, 0x18, 0xe3, 0xfd, 0x93, 0x24, 0xc6, 0xfb, 0x27, 0x4e, 0x17, 0xaa, 0xc6, - 0x06, 0xb2, 0x8f, 0xd1, 0x2f, 0xcb, 0xc6, 0x36, 0x62, 0x43, 0x39, 0xd6, 0x26, 0xa6, 0x38, 0xb5, - 0x49, 0xab, 0xab, 0xcc, 0x0c, 0x96, 0x38, 0xb5, 0x9d, 0x07, 0xd0, 0x48, 0xd7, 0x86, 0x4c, 0x74, - 0xad, 0x9a, 0x62, 0xbf, 0x8b, 0x1d, 0x68, 0xc0, 0xc6, 0x28, 0xb5, 0x71, 0x22, 0x64, 0xa4, 0x7d, - 0x19, 0xba, 0x01, 0x29, 0xaa, 0xf3, 0xf4, 0xdb, 0xf9, 0x6d, 0x09, 0x2a, 0x14, 0x64, 0x6c, 0x0b, - 0x63, 0x3a, 0x9a, 0x99, 0x11, 0x94, 0x0e, 0x98, 0x8d, 0x69, 0xa0, 0xec, 0x49, 0x43, 0x1a, 0x33, - 0x69, 0x1d, 0xe3, 0x2b, 0x10, 0x9e, 0x96, 0xca, 0xda, 0x49, 0xbf, 0xd1, 0xfe, 0x08, 0x73, 0xcc, - 0x2c, 0x39, 0xb5, 0xd9, 0x2d, 0xa8, 0x4a, 0x4a, 0x0c, 0x5a, 0xf5, 0xef, 0x49, 0x17, 0x4b, 0x41, - 0xe5, 0x4a, 0xb8, 0x23, 0x19, 0x06, 0x73, 0x8a, 0x85, 0x3a, 0x4f, 0xbf, 0x31, 0x54, 0x29, 0x13, - 0x1e, 0xcf, 0x23, 0xb3, 0x31, 0xb6, 0x4c, 0xa8, 0x3e, 0x4a, 0x40, 0x9e, 0xc9, 0x71, 0xeb, 0x7b, - 0x3c, 0x8d, 0xc6, 0xf1, 0x20, 0xd2, 0x9d, 0xab, 0x59, 0x50, 0x25, 0x18, 0x4f, 0xa5, 0xc8, 0xf4, - 0x5c, 0xef, 0x4c, 0x20, 0xf3, 0x5a, 0xc6, 0x3c, 0xb4, 0x18, 0x4f, 0xa5, 0x59, 0xae, 0x20, 0xf5, - 0x2d, 0xa2, 0xe6, 0x72, 0x05, 0xb9, 0x99, 0x1c, 0x63, 0x6c, 0x38, 0x3c, 0x42, 0xe6, 0xdb, 0xd9, - 0xfe, 0x6c, 0x10, 0x6e, 0x25, 0x66, 0xb4, 0xf1, 0x2c, 0xd0, 0xfd, 0x6e, 0xe7, 0x1d, 0x33, 0x95, - 0xc9, 0xb7, 0xb3, 0x91, 0x0d, 0x00, 0xa7, 0x35, 0xf6, 0x7f, 0x61, 0xe2, 0xa5, 0xc4, 0xa9, 0xed, - 0xf4, 0xa1, 0x9e, 0xb8, 0xf8, 0x52, 0x18, 0xdc, 0x81, 0x5a, 0x7c, 0xe6, 0x2a, 0x3f, 0x9c, 0xd0, - 0x0a, 0xb5, 0xf6, 0xae, 0xa6, 0x23, 0x1a, 0x1a, 0x1c, 0xbd, 0x48, 0x38, 0x8e, 0x4c, 0x42, 0xea, - 0x32, 0x5d, 0x6d, 0x28, 0xcd, 0xfc, 0x11, 0xe9, 0x59, 0xe3, 0xd8, 0x44, 0x64, 0xe2, 0x9b, 0xa0, - 0x5c, 0xe3, 0xd8, 0x44, 0xff, 0xa6, 0x72, 0x64, 0x4e, 0xbd, 0x35, 0x4e, 0xed, 0x85, 0xb0, 0xab, - 0x2c, 0x85, 0x5d, 0x90, 0xcc, 0xcd, 0xff, 0xc4, 0xda, 0xaf, 0x0b, 0x50, 0x4f, 0x8e, 0x6a, 0x3c, - 0x30, 0xfc, 0x91, 0x08, 0xb5, 0x3f, 0xf6, 0x85, 0xb2, 0x86, 0x73, 0x08, 0xbb, 0x03, 0x15, 0x57, - 0x6b, 0x95, 0x6c, 0xc3, 0xef, 0xe4, 0xcf, 0xf9, 0x9d, 0x7d, 0x94, 0xf4, 0x42, 0xad, 0xe6, 0xdc, - 0xb0, 0xd6, 0x3f, 0x01, 0xc8, 0x40, 0xf4, 0xf5, 0x5c, 0xcc, 0xad, 0x56, 0x6c, 0xb2, 0x6b, 0x50, - 0xf9, 0xdc, 0x0d, 0x66, 0x49, 0x46, 0x9a, 0x8f, 0x7b, 0xc5, 0x4f, 0x0a, 0xce, 0x1f, 0x8b, 0x50, - 0xb3, 0xe7, 0x3e, 0xbb, 0x0d, 0x35, 0x3a, 0xf7, 0xad, 0x47, 0x97, 0xa7, 0x5f, 0x42, 0x61, 0xbb, - 0x69, 0x41, 0x93, 0xf3, 0xd1, 0xaa, 0x32, 0x85, 0x8d, 0xf5, 0x31, 0x2b, 0x6f, 0x4a, 0x23, 0x31, - 0xb6, 0x95, 0x4b, 0x8b, 0xea, 0x04, 0x31, 0xf6, 0x43, 0x1f, 0xe7, 0x87, 0xa3, 0x88, 0xdd, 0x4e, - 0x46, 0x5d, 0x26, 0x8d, 0x6f, 0xe7, 0x35, 0xbe, 0x3c, 0xe8, 0x3e, 0x34, 0x73, 0x66, 0x2e, 0x19, - 0xf5, 0x07, 0xf9, 0x51, 0x5b, 0x93, 0xa4, 0xce, 0x94, 0x5d, 0xd9, 0x2c, 0xfc, 0x07, 0xf3, 0xf7, - 0x31, 0x40, 0xa6, 0xf2, 0xf5, 0xb7, 0x2f, 0xe7, 0x8b, 0x12, 0xc0, 0x20, 0xc2, 0x53, 0x6c, 0xe4, - 0xd2, 0xb9, 0xbb, 0xea, 0x4f, 0x42, 0xa9, 0xc4, 0x33, 0x4a, 0x73, 0xea, 0x5f, 0xe7, 0x4d, 0x83, - 0x51, 0xc6, 0xb0, 0x7d, 0x68, 0x8e, 0x44, 0xec, 0x29, 0x9f, 0x02, 0xca, 0x4e, 0xfa, 0x0d, 0x1c, - 0x53, 0xa6, 0x67, 0xa7, 0x9b, 0x31, 0xcc, 0x5c, 0xe5, 0xfb, 0xb0, 0x3d, 0x58, 0x15, 0x17, 0x91, - 0x54, 0xda, 0x5a, 0x31, 0xe5, 0xe1, 0x15, 0x53, 0x68, 0x22, 0x4e, 0x96, 0x78, 0x53, 0x64, 0x1f, - 0xcc, 0x85, 0xb2, 0xe7, 0x46, 0xb1, 0x3d, 0x94, 0x3b, 0x4b, 0xf6, 0x0e, 0xdd, 0xc8, 0x4c, 0xda, - 0xc1, 0x47, 0x38, 0xd6, 0x2f, 0xfe, 0x7a, 0xe3, 0x56, 0xae, 0x92, 0x99, 0xca, 0xd3, 0xf9, 0x2e, - 0xc5, 0xcb, 0xb9, 0xaf, 0x77, 0x67, 0xda, 0x0f, 0x76, 0xdd, 0xc8, 0x47, 0x75, 0xd8, 0xb1, 0xdf, - 0xe5, 0xa4, 0x7a, 0xfd, 0x27, 0xd0, 0x5e, 0xf6, 0xfb, 0x4d, 0xd6, 0x60, 0xfd, 0x2e, 0x34, 0x52, - 0x3f, 0x5e, 0xd5, 0xb1, 0x9e, 0x5f, 0xbc, 0x3f, 0x14, 0xa0, 0x6a, 0xb2, 0x8a, 0xdd, 0x85, 0x46, - 0x20, 0x3d, 0x17, 0x1d, 0x48, 0x2a, 0xf4, 0x77, 0xb3, 0xa4, 0xdb, 0x79, 0x98, 0xc8, 0xcc, 0xac, - 0x66, 0x5c, 0x0c, 0x32, 0x3f, 0x1c, 0xcb, 0x24, 0x0b, 0x5a, 0x59, 0xa7, 0x7e, 0x38, 0x96, 0xdc, - 0x08, 0xd7, 0x1f, 0x40, 0x6b, 0x51, 0xc5, 0x25, 0x7e, 0xbe, 0xbf, 0x18, 0xae, 0xb4, 0xa7, 0xa7, - 0x9d, 0xf2, 0x6e, 0xdf, 0x85, 0x46, 0x8a, 0xb3, 0xed, 0x97, 0x1d, 0x5f, 0xcd, 0xf7, 0xcc, 0xf9, - 0xea, 0x04, 0x00, 0x99, 0x6b, 0xb8, 0x59, 0xe1, 0x55, 0x20, 0xcc, 0x4a, 0x80, 0xf4, 0x9b, 0x4e, - 0x50, 0x57, 0xbb, 0xe4, 0xca, 0x2a, 0xa7, 0x36, 0xdb, 0x01, 0x18, 0xa5, 0x09, 0xfb, 0x3d, 0x69, - 0x9c, 0x63, 0x38, 0x03, 0xa8, 0x27, 0x4e, 0xb0, 0x4d, 0x68, 0xc6, 0xd6, 0x32, 0x56, 0xac, 0x68, - 0xae, 0xc2, 0xf3, 0x10, 0x56, 0x9e, 0xca, 0x0d, 0x27, 0x62, 0xa1, 0xf2, 0xe4, 0x88, 0x70, 0x2b, - 0x70, 0x3e, 0x83, 0x0a, 0x01, 0x98, 0x66, 0xb1, 0x76, 0x95, 0xb6, 0x45, 0xac, 0xa9, 0xd3, 0x64, - 0x4c, 0x66, 0x0f, 0xca, 0x18, 0x88, 0xdc, 0x10, 0xd8, 0x07, 0x58, 0x0d, 0x8e, 0xec, 0x8c, 0x5e, - 0xc6, 0x43, 0xb1, 0xf3, 0x63, 0xa8, 0x27, 0x30, 0x8e, 0xfc, 0xa1, 0x1f, 0x0a, 0xeb, 0x22, 0xb5, - 0xb1, 0xf8, 0x3f, 0x3c, 0x73, 0x95, 0xeb, 0x69, 0x61, 0x8a, 0x8d, 0x0a, 0xcf, 0x00, 0xe7, 0x7d, - 0x68, 0xe6, 0xb2, 0x07, 0xc3, 0xed, 0x29, 0x2d, 0xa3, 0xc9, 0x61, 0xf3, 0xe1, 0xfc, 0x1e, 0xaf, - 0x26, 0x49, 0x01, 0xf9, 0x03, 0x80, 0x33, 0xad, 0xa3, 0x67, 0x54, 0x51, 0xda, 0xb9, 0x6f, 0x20, - 0x42, 0x0c, 0x76, 0x03, 0x9a, 0xf8, 0x11, 0x5b, 0xb9, 0x89, 0x77, 0xea, 0x11, 0x1b, 0xc2, 0xff, - 0x43, 0x63, 0x9c, 0x76, 0x2f, 0xd9, 0xa5, 0x4b, 0x7a, 0xbf, 0x0b, 0xf5, 0x50, 0x5a, 0x99, 0x29, - 0x70, 0x6b, 0xa1, 0x4c, 0xfb, 0xb9, 0x41, 0x60, 0x65, 0x15, 0xd3, 0xcf, 0x0d, 0x02, 0x12, 0x3a, - 0xb7, 0xe0, 0xff, 0x5e, 0xba, 0x64, 0xb1, 0xb7, 0xa1, 0x3a, 0xf6, 0x03, 0x4d, 0x27, 0x02, 0x16, - 0xd4, 0xf6, 0xcb, 0xf9, 0x67, 0x01, 0x20, 0x5b, 0x76, 0x0c, 0x66, 0xdc, 0xda, 0x91, 0xb3, 0x6a, - 0xb6, 0xf2, 0x00, 0xea, 0x53, 0xbb, 0x49, 0xd8, 0x05, 0xbd, 0xbe, 0x18, 0x2a, 0x3b, 0xc9, 0x1e, - 0x62, 0xb6, 0x8f, 0x3d, 0xbb, 0x7d, 0xbc, 0xc9, 0x45, 0x28, 0xb5, 0x40, 0x55, 0x4e, 0xfe, 0x5e, - 0x0c, 0x59, 0x16, 0x72, 0x2b, 0x59, 0x7f, 0x00, 0x6b, 0x0b, 0x26, 0x5f, 0xf3, 0xc0, 0xc8, 0x36, - 0xbb, 0x7c, 0x0a, 0xee, 0x41, 0xd5, 0x5c, 0xa8, 0xd9, 0x16, 0xd4, 0x5c, 0xcf, 0x64, 0x5f, 0x6e, - 0x07, 0x40, 0xe1, 0x3e, 0xc1, 0x3c, 0x11, 0x3b, 0x7f, 0x2e, 0x02, 0x64, 0xf8, 0x1b, 0x94, 0xba, - 0xf7, 0xa0, 0x15, 0x0b, 0x4f, 0x86, 0x23, 0x57, 0xcd, 0x49, 0x6a, 0x6f, 0x7c, 0x97, 0x75, 0x59, - 0x62, 0xe6, 0xca, 0xde, 0xd2, 0xab, 0xcb, 0xde, 0x2d, 0x28, 0x7b, 0x32, 0x9a, 0xdb, 0x73, 0x81, - 0x2d, 0x0e, 0xe4, 0x50, 0x46, 0x73, 0xbc, 0xd2, 0x23, 0x83, 0xed, 0x40, 0x75, 0x7a, 0x4e, 0x4f, - 0x0c, 0xe6, 0xaa, 0x74, 0x6d, 0x91, 0xfb, 0xe8, 0x1c, 0xdb, 0x47, 0x2b, 0xdc, 0xb2, 0xd8, 0x2d, - 0xa8, 0x4c, 0xcf, 0x47, 0xbe, 0xb2, 0x2f, 0x09, 0x57, 0x97, 0xe9, 0x5d, 0x5f, 0xd1, 0x8b, 0x02, - 0x72, 0x98, 0x03, 0x45, 0x35, 0xb5, 0xef, 0x09, 0xed, 0xa5, 0xd9, 0x9c, 0x1e, 0xad, 0xf0, 0xa2, - 0x9a, 0x1e, 0xd4, 0xa1, 0x6a, 0xe6, 0xd5, 0xf9, 0x47, 0x09, 0x5a, 0x8b, 0x5e, 0xe2, 0xca, 0xc6, - 0xca, 0x4b, 0x56, 0x36, 0x56, 0x5e, 0x7a, 0x23, 0x28, 0xe6, 0x6e, 0x04, 0x0e, 0x54, 0xe4, 0xf3, - 0x50, 0xa8, 0xfc, 0x5b, 0xca, 0xe1, 0x99, 0x7c, 0x1e, 0x62, 0x55, 0x6a, 0x44, 0x0b, 0x45, 0x5e, - 0xc5, 0x16, 0x79, 0x1f, 0xc0, 0xda, 0x58, 0x06, 0x81, 0x7c, 0x3e, 0x9c, 0x4f, 0x03, 0x3f, 0x3c, - 0xb7, 0x95, 0xde, 0x22, 0xc8, 0xb6, 0xe0, 0xca, 0xc8, 0x57, 0xe8, 0xce, 0xa1, 0x0c, 0xb5, 0x08, - 0xe9, 0xa6, 0x88, 0xbc, 0x65, 0x98, 0x7d, 0x0a, 0x9b, 0xae, 0xd6, 0x62, 0x1a, 0xe9, 0x27, 0x61, - 0xe4, 0x7a, 0xe7, 0x5d, 0xe9, 0x51, 0x16, 0x4e, 0x23, 0x57, 0xfb, 0xa7, 0x7e, 0x80, 0x37, 0xe8, - 0x1a, 0x75, 0x7d, 0x25, 0x8f, 0x7d, 0x08, 0x2d, 0x4f, 0x09, 0x57, 0x8b, 0xae, 0x88, 0xf5, 0x89, - 0xab, 0xcf, 0x3a, 0x75, 0xea, 0xb9, 0x84, 0xe2, 0x18, 0x5c, 0xf4, 0xf6, 0x33, 0x3f, 0x18, 0x79, - 0x78, 0xb7, 0x6b, 0x98, 0x31, 0x2c, 0x80, 0x6c, 0x07, 0x18, 0x01, 0xbd, 0x69, 0xa4, 0xe7, 0x29, - 0x15, 0x88, 0x7a, 0x89, 0x04, 0xf7, 0x49, 0xed, 0x4f, 0x45, 0xac, 0xdd, 0x69, 0x44, 0x8f, 0x37, - 0x25, 0x9e, 0x01, 0xec, 0x26, 0xb4, 0xfd, 0xd0, 0x0b, 0x66, 0x23, 0xf1, 0x2c, 0xc2, 0x81, 0xa8, - 0x30, 0xee, 0xac, 0xd2, 0xae, 0x72, 0xc5, 0xe2, 0x27, 0x16, 0x46, 0xaa, 0xb8, 0x58, 0xa2, 0xae, - 0x19, 0xaa, 0xc5, 0x13, 0xaa, 0xf3, 0x65, 0x01, 0xda, 0xcb, 0x81, 0x87, 0xcb, 0x16, 0xe1, 0xe0, - 0xed, 0xcd, 0x16, 0xdb, 0xe9, 0x52, 0x16, 0x73, 0x4b, 0x99, 0x1c, 0x73, 0xa5, 0xdc, 0x31, 0x97, - 0x86, 0x45, 0xf9, 0xfb, 0xc3, 0x62, 0x61, 0xa0, 0x95, 0xa5, 0x81, 0x3a, 0xbf, 0x2b, 0xc0, 0x95, - 0xa5, 0xe0, 0x7e, 0x6d, 0x8f, 0x36, 0xa1, 0x39, 0x75, 0xcf, 0x85, 0xb9, 0xd9, 0xc7, 0xf6, 0xa6, - 0x9c, 0x87, 0xfe, 0x0b, 0xfe, 0x85, 0xb0, 0x9a, 0xcf, 0xa8, 0x4b, 0x7d, 0x4b, 0x02, 0xe4, 0x58, - 0xea, 0xfb, 0x72, 0x66, 0x8f, 0xd0, 0x24, 0x40, 0x12, 0xf0, 0xe5, 0x30, 0x2a, 0x5d, 0x12, 0x46, - 0xce, 0x31, 0xd4, 0x13, 0x07, 0xd9, 0x0d, 0xfb, 0xf4, 0x52, 0xc8, 0x5e, 0x14, 0x9f, 0xc4, 0x42, - 0xa1, 0xef, 0xe6, 0x1d, 0xe6, 0x3d, 0xa8, 0xd0, 0xdb, 0x86, 0xdd, 0x83, 0x17, 0x18, 0x46, 0xe2, - 0x0c, 0xa1, 0x66, 0x11, 0xb6, 0x0d, 0xd5, 0xd3, 0x79, 0xfa, 0x88, 0x61, 0xb7, 0x0b, 0xfc, 0x1e, - 0x59, 0x06, 0xee, 0x41, 0x86, 0xc1, 0xae, 0x41, 0xf9, 0x74, 0xde, 0xef, 0x9a, 0x5b, 0x1d, 0xee, - 0x64, 0xf8, 0x75, 0x50, 0x35, 0x0e, 0x39, 0x0f, 0x61, 0x35, 0xdf, 0x2f, 0x7d, 0xc3, 0x28, 0xe4, - 0xde, 0x30, 0xd2, 0x2d, 0xbb, 0xf8, 0xaa, 0xf2, 0xfe, 0x63, 0x00, 0x7a, 0x28, 0x7d, 0xd3, 0x6b, - 0xc1, 0x8f, 0xa0, 0x66, 0x1f, 0x58, 0xd9, 0x87, 0x4b, 0x0f, 0xc6, 0xad, 0xf4, 0xf5, 0x75, 0xe1, - 0xd5, 0xd8, 0xb9, 0x87, 0xa5, 0xe5, 0x73, 0xa1, 0xba, 0xfe, 0x78, 0xfc, 0xa6, 0xe6, 0xee, 0x41, - 0xeb, 0x49, 0x14, 0xfd, 0x7b, 0x7d, 0x7f, 0x0e, 0x55, 0xf3, 0xce, 0x8b, 0x7d, 0x02, 0xf4, 0xc0, - 0xae, 0x01, 0x33, 0xe5, 0x67, 0xde, 0x25, 0x6e, 0x08, 0xc8, 0x9c, 0xa1, 0x3d, 0xbb, 0xb8, 0xc4, - 0x5c, 0x74, 0x80, 0x1b, 0xc2, 0xf6, 0x16, 0xd4, 0xec, 0x93, 0x22, 0x6b, 0x40, 0xe5, 0xc9, 0xf1, - 0xb0, 0xf7, 0xb8, 0xbd, 0xc2, 0xea, 0x50, 0x3e, 0x1a, 0x0c, 0x1f, 0xb7, 0x0b, 0xd8, 0x3a, 0x1e, - 0x1c, 0xf7, 0xda, 0xc5, 0xed, 0x9b, 0xb0, 0x9a, 0x7f, 0x54, 0x64, 0x4d, 0xa8, 0x0d, 0xf7, 0x8f, - 0xbb, 0x07, 0x83, 0x9f, 0xb5, 0x57, 0xd8, 0x2a, 0xd4, 0xfb, 0xc7, 0xc3, 0xde, 0xe1, 0x13, 0xde, - 0x6b, 0x17, 0xb6, 0x7f, 0x0a, 0x8d, 0xf4, 0x95, 0x06, 0x35, 0x1c, 0xf4, 0x8f, 0xbb, 0xed, 0x15, - 0x06, 0x50, 0x1d, 0xf6, 0x0e, 0x79, 0x0f, 0xf5, 0xd6, 0xa0, 0x34, 0x1c, 0x1e, 0xb5, 0x8b, 0x68, - 0xf5, 0x70, 0xff, 0xf0, 0xa8, 0xd7, 0x2e, 0x61, 0xf3, 0xf1, 0xa3, 0x93, 0xfb, 0xc3, 0x76, 0x79, - 0xfb, 0x63, 0xb8, 0xb2, 0xf4, 0x7e, 0x41, 0xbd, 0x8f, 0xf6, 0x79, 0x0f, 0x35, 0x35, 0xa1, 0x76, - 0xc2, 0xfb, 0x4f, 0xf7, 0x1f, 0xf7, 0xda, 0x05, 0x14, 0x3c, 0x1c, 0x1c, 0x3e, 0xe8, 0x75, 0xdb, - 0xc5, 0x83, 0xeb, 0x5f, 0xbd, 0xd8, 0x28, 0x7c, 0xfd, 0x62, 0xa3, 0xf0, 0xcd, 0x8b, 0x8d, 0xc2, - 0xdf, 0x5e, 0x6c, 0x14, 0xbe, 0xfc, 0x6e, 0x63, 0xe5, 0xeb, 0xef, 0x36, 0x56, 0xbe, 0xf9, 0x6e, - 0x63, 0xe5, 0xb4, 0x4a, 0xff, 0x14, 0x7c, 0xf4, 0xaf, 0x00, 0x00, 0x00, 0xff, 0xff, 0xcd, 0x9c, - 0x8e, 0xbd, 0x69, 0x18, 0x00, 0x00, + 0x62, 0x0f, 0x3d, 0xf4, 0x5e, 0x20, 0x40, 0x81, 0xa2, 0x97, 0xa2, 0xff, 0x44, 0x8f, 0xed, 0x3d, + 0x40, 0x2f, 0x39, 0xf4, 0x10, 0xf4, 0x90, 0x16, 0xce, 0xa5, 0x7f, 0x44, 0x0b, 0x14, 0xef, 0xcd, + 0xec, 0x0f, 0xd2, 0x0a, 0x1c, 0xb7, 0x45, 0x4f, 0x9c, 0xfd, 0xbc, 0xcf, 0xbc, 0x79, 0x33, 0xf3, + 0xde, 0xbc, 0x37, 0x43, 0x68, 0xc8, 0x28, 0xde, 0x89, 0x94, 0xd4, 0x92, 0x15, 0xa3, 0xd3, 0xf5, + 0x3b, 0x13, 0x5f, 0x9f, 0xcd, 0x4e, 0x77, 0x3c, 0x39, 0xdd, 0x9d, 0xc8, 0x89, 0xdc, 0x25, 0xd1, + 0xe9, 0x6c, 0x4c, 0x5f, 0xf4, 0x41, 0x2d, 0xd3, 0xc5, 0xf9, 0x7b, 0x11, 0x8a, 0x83, 0x88, 0xbd, + 0x0b, 0x55, 0x3f, 0x8c, 0x66, 0x3a, 0xee, 0x14, 0x36, 0x4b, 0x5b, 0xcd, 0xbd, 0xc6, 0x4e, 0x74, + 0xba, 0xd3, 0x47, 0x84, 0x5b, 0x01, 0xdb, 0x84, 0xb2, 0xb8, 0x10, 0x5e, 0xa7, 0xb8, 0x59, 0xd8, + 0x6a, 0xee, 0x01, 0x12, 0x7a, 0x17, 0xc2, 0x1b, 0x44, 0x47, 0x2b, 0x9c, 0x24, 0xec, 0x03, 0xa8, + 0xc6, 0x72, 0xa6, 0x3c, 0xd1, 0x29, 0x11, 0x67, 0x15, 0x39, 0x43, 0x42, 0x88, 0x65, 0xa5, 0xa8, + 0x69, 0xec, 0x07, 0xa2, 0x53, 0xce, 0x34, 0xdd, 0xf7, 0x03, 0xc3, 0x21, 0x09, 0x7b, 0x0f, 0x2a, + 0xa7, 0x33, 0x3f, 0x18, 0x75, 0x2a, 0x44, 0x69, 0x22, 0xe5, 0x00, 0x01, 0xe2, 0x18, 0x19, 0x92, + 0xa6, 0x42, 0x4d, 0x44, 0xa7, 0x9a, 0x91, 0x1e, 0x21, 0x60, 0x48, 0x24, 0xc3, 0xb1, 0x46, 0xfe, + 0x78, 0xdc, 0xa9, 0x65, 0x63, 0x75, 0xfd, 0xf1, 0xd8, 0x8c, 0x85, 0x12, 0xb6, 0x05, 0xf5, 0x28, + 0x70, 0xf5, 0x58, 0xaa, 0x69, 0x07, 0x32, 0xbb, 0x4f, 0x2c, 0xc6, 0x53, 0x29, 0xbb, 0x0b, 0x4d, + 0x4f, 0x86, 0xb1, 0x56, 0xae, 0x1f, 0xea, 0xb8, 0xd3, 0x24, 0xf2, 0x9b, 0x48, 0xfe, 0x54, 0xaa, + 0x73, 0xa1, 0x0e, 0x33, 0x21, 0xcf, 0x33, 0x0f, 0xca, 0x50, 0x94, 0x91, 0xf3, 0xeb, 0x02, 0xd4, + 0x13, 0xad, 0xcc, 0x81, 0xd5, 0x7d, 0xe5, 0x9d, 0xf9, 0x5a, 0x78, 0x7a, 0xa6, 0x44, 0xa7, 0xb0, + 0x59, 0xd8, 0x6a, 0xf0, 0x05, 0x8c, 0xb5, 0xa0, 0x38, 0x18, 0xd2, 0x7a, 0x37, 0x78, 0x71, 0x30, + 0x64, 0x1d, 0xa8, 0x3d, 0x75, 0x95, 0xef, 0x86, 0x9a, 0x16, 0xb8, 0xc1, 0x93, 0x4f, 0x76, 0x1d, + 0x1a, 0x83, 0xe1, 0x53, 0xa1, 0x62, 0x5f, 0x86, 0xb4, 0xac, 0x0d, 0x9e, 0x01, 0x6c, 0x03, 0x60, + 0x30, 0xbc, 0x2f, 0x5c, 0x54, 0x1a, 0x77, 0x2a, 0x9b, 0xa5, 0xad, 0x06, 0xcf, 0x21, 0xce, 0xcf, + 0xa1, 0x42, 0x5b, 0xcd, 0x3e, 0x81, 0xea, 0xc8, 0x9f, 0x88, 0x58, 0x1b, 0x73, 0x0e, 0xf6, 0xbe, + 0xf8, 0xfa, 0xc6, 0xca, 0x5f, 0xbe, 0xbe, 0xb1, 0x9d, 0xf3, 0x29, 0x19, 0x89, 0xd0, 0x93, 0xa1, + 0x76, 0xfd, 0x50, 0xa8, 0x78, 0x77, 0x22, 0xef, 0x98, 0x2e, 0x3b, 0x5d, 0xfa, 0xe1, 0x56, 0x03, + 0xbb, 0x09, 0x15, 0x3f, 0x1c, 0x89, 0x0b, 0xb2, 0xbf, 0x74, 0x70, 0xd5, 0xaa, 0x6a, 0x0e, 0x66, + 0x3a, 0x9a, 0xe9, 0x3e, 0x8a, 0xb8, 0x61, 0x38, 0x7f, 0x2a, 0x40, 0xd5, 0xb8, 0x12, 0xbb, 0x0e, + 0xe5, 0xa9, 0xd0, 0x2e, 0x8d, 0xdf, 0xdc, 0xab, 0x9b, 0x2d, 0xd5, 0x2e, 0x27, 0x14, 0xbd, 0x74, + 0x2a, 0x67, 0xb8, 0xf6, 0xc5, 0xcc, 0x4b, 0x1f, 0x21, 0xc2, 0xad, 0x80, 0x7d, 0x0f, 0x6a, 0xa1, + 0xd0, 0xcf, 0xa5, 0x3a, 0xa7, 0x35, 0x6a, 0x19, 0xb7, 0x38, 0x16, 0xfa, 0x91, 0x1c, 0x09, 0x9e, + 0xc8, 0xd8, 0x6d, 0xa8, 0xc7, 0xc2, 0x9b, 0x29, 0x5f, 0xcf, 0x69, 0xbd, 0x5a, 0x7b, 0x6d, 0x72, + 0x56, 0x8b, 0x11, 0x39, 0x65, 0xb0, 0x5b, 0xd0, 0x88, 0x85, 0xa7, 0x84, 0x16, 0xe1, 0x67, 0xb4, + 0x7e, 0xcd, 0xbd, 0x35, 0x4b, 0x57, 0x42, 0xf7, 0xc2, 0xcf, 0x78, 0x26, 0x77, 0x7e, 0x59, 0x84, + 0x32, 0xda, 0xcc, 0x18, 0x94, 0x5d, 0x35, 0x31, 0x11, 0xd5, 0xe0, 0xd4, 0x66, 0x6d, 0x28, 0xa1, + 0x8e, 0x22, 0x41, 0xd8, 0x44, 0xc4, 0x7b, 0x3e, 0xb2, 0x1b, 0x8a, 0x4d, 0xec, 0x37, 0x8b, 0x85, + 0xb2, 0xfb, 0x48, 0x6d, 0x76, 0x13, 0x1a, 0x91, 0x92, 0x17, 0xf3, 0x67, 0xc6, 0x82, 0xcc, 0x4b, + 0x11, 0x44, 0x03, 0xea, 0x91, 0x6d, 0xb1, 0x6d, 0x00, 0x71, 0xa1, 0x95, 0x7b, 0x24, 0x63, 0x1d, + 0x77, 0xaa, 0x64, 0x2d, 0xf9, 0x3d, 0x02, 0xfd, 0x13, 0x9e, 0x93, 0xb2, 0x75, 0xa8, 0x9f, 0xc9, + 0x58, 0x87, 0xee, 0x54, 0x50, 0x84, 0x34, 0x78, 0xfa, 0xcd, 0x1c, 0xa8, 0xce, 0x02, 0x7f, 0xea, + 0xeb, 0x4e, 0x23, 0xd3, 0xf1, 0x84, 0x10, 0x6e, 0x25, 0xe8, 0xc5, 0xde, 0x44, 0xc9, 0x59, 0x74, + 0xe2, 0x2a, 0x11, 0x6a, 0x8a, 0x9f, 0x06, 0x5f, 0xc0, 0x9c, 0xdb, 0x50, 0x35, 0x23, 0xe3, 0xc4, + 0xb0, 0x65, 0x7d, 0x9d, 0xda, 0xe8, 0xe3, 0xfd, 0x93, 0xc4, 0xc7, 0xfb, 0x27, 0x4e, 0x17, 0xaa, + 0x66, 0x0c, 0x64, 0x1f, 0xa3, 0x5d, 0x96, 0x8d, 0x6d, 0xc4, 0x86, 0x72, 0xac, 0x8d, 0x4f, 0x71, + 0x6a, 0x93, 0x56, 0x57, 0x99, 0x15, 0x2c, 0x71, 0x6a, 0x3b, 0x0f, 0xa0, 0x91, 0xee, 0x0d, 0x0d, + 0xd1, 0xb5, 0x6a, 0x8a, 0xfd, 0x2e, 0x76, 0xa0, 0x09, 0x9b, 0x41, 0xa9, 0x8d, 0x0b, 0x21, 0x23, + 0xed, 0xcb, 0xd0, 0x0d, 0x48, 0x51, 0x9d, 0xa7, 0xdf, 0xce, 0x6f, 0x4a, 0x50, 0x21, 0x27, 0x63, + 0x5b, 0xe8, 0xd3, 0xd1, 0xcc, 0xcc, 0xa0, 0x74, 0xc0, 0xac, 0x4f, 0x03, 0x45, 0x4f, 0xea, 0xd2, + 0x18, 0x49, 0xeb, 0xe8, 0x5f, 0x81, 0xf0, 0xb4, 0x54, 0x76, 0x9c, 0xf4, 0x1b, 0xc7, 0x1f, 0x61, + 0x8c, 0x99, 0x2d, 0xa7, 0x36, 0xbb, 0x05, 0x55, 0x49, 0x81, 0x41, 0xbb, 0xfe, 0x2d, 0xe1, 0x62, + 0x29, 0xa8, 0x5c, 0x09, 0x77, 0x24, 0xc3, 0x60, 0x4e, 0xbe, 0x50, 0xe7, 0xe9, 0x37, 0xba, 0x2a, + 0x45, 0xc2, 0xe3, 0x79, 0x64, 0x0e, 0xc6, 0x96, 0x71, 0xd5, 0x47, 0x09, 0xc8, 0x33, 0x39, 0x1e, + 0x7d, 0x8f, 0xa7, 0xd1, 0x38, 0x1e, 0x44, 0xba, 0x73, 0x35, 0x73, 0xaa, 0x04, 0xe3, 0xa9, 0x14, + 0x99, 0x9e, 0xeb, 0x9d, 0x09, 0x64, 0x5e, 0xcb, 0x98, 0x87, 0x16, 0xe3, 0xa9, 0x34, 0x8b, 0x15, + 0xa4, 0xbe, 0x49, 0xd4, 0x5c, 0xac, 0x20, 0x37, 0x93, 0xa3, 0x8f, 0x0d, 0x87, 0x47, 0xc8, 0x7c, + 0x2b, 0x3b, 0x9f, 0x0d, 0xc2, 0xad, 0xc4, 0xcc, 0x36, 0x9e, 0x05, 0xba, 0xdf, 0xed, 0xbc, 0x6d, + 0x96, 0x32, 0xf9, 0x76, 0x36, 0xb2, 0x09, 0xe0, 0xb2, 0xc6, 0xfe, 0xcf, 0x8c, 0xbf, 0x94, 0x38, + 0xb5, 0x9d, 0x3e, 0xd4, 0x13, 0x13, 0x5f, 0x72, 0x83, 0x3b, 0x50, 0x8b, 0xcf, 0x5c, 0xe5, 0x87, + 0x13, 0xda, 0xa1, 0xd6, 0xde, 0xd5, 0x74, 0x46, 0x43, 0x83, 0xa3, 0x15, 0x09, 0xc7, 0x91, 0x89, + 0x4b, 0x5d, 0xa6, 0xab, 0x0d, 0xa5, 0x99, 0x3f, 0x22, 0x3d, 0x6b, 0x1c, 0x9b, 0x88, 0x4c, 0x7c, + 0xe3, 0x94, 0x6b, 0x1c, 0x9b, 0x68, 0xdf, 0x54, 0x8e, 0x4c, 0xd6, 0x5b, 0xe3, 0xd4, 0x5e, 0x70, + 0xbb, 0xca, 0x92, 0xdb, 0x05, 0xc9, 0xda, 0xfc, 0x4f, 0x46, 0xfb, 0x55, 0x01, 0xea, 0x49, 0xaa, + 0xc6, 0x84, 0xe1, 0x8f, 0x44, 0xa8, 0xfd, 0xb1, 0x2f, 0x94, 0x1d, 0x38, 0x87, 0xb0, 0x3b, 0x50, + 0x71, 0xb5, 0x56, 0xc9, 0x31, 0xfc, 0x76, 0x3e, 0xcf, 0xef, 0xec, 0xa3, 0xa4, 0x17, 0x6a, 0x35, + 0xe7, 0x86, 0xb5, 0xfe, 0x31, 0x40, 0x06, 0xa2, 0xad, 0xe7, 0x62, 0x6e, 0xb5, 0x62, 0x93, 0x5d, + 0x83, 0xca, 0x67, 0x6e, 0x30, 0x4b, 0x22, 0xd2, 0x7c, 0xdc, 0x2b, 0x7e, 0x5c, 0x70, 0xfe, 0x58, + 0x84, 0x9a, 0xcd, 0xfb, 0xec, 0x36, 0xd4, 0x28, 0xef, 0x5b, 0x8b, 0x2e, 0x0f, 0xbf, 0x84, 0xc2, + 0x76, 0xd3, 0x82, 0x26, 0x67, 0xa3, 0x55, 0x65, 0x0a, 0x1b, 0x6b, 0x63, 0x56, 0xde, 0x94, 0x46, + 0x62, 0x6c, 0x2b, 0x97, 0x16, 0xd5, 0x09, 0x62, 0xec, 0x87, 0x3e, 0xae, 0x0f, 0x47, 0x11, 0xbb, + 0x9d, 0xcc, 0xba, 0x4c, 0x1a, 0xdf, 0xca, 0x6b, 0x7c, 0x79, 0xd2, 0x7d, 0x68, 0xe6, 0x86, 0xb9, + 0x64, 0xd6, 0xef, 0xe7, 0x67, 0x6d, 0x87, 0x24, 0x75, 0xa6, 0xec, 0xca, 0x56, 0xe1, 0x3f, 0x58, + 0xbf, 0x8f, 0x00, 0x32, 0x95, 0xdf, 0xfd, 0xf8, 0x72, 0xfe, 0x50, 0x02, 0x18, 0x44, 0x98, 0xc5, + 0x46, 0x2e, 0xe5, 0xdd, 0x55, 0x7f, 0x12, 0x4a, 0x25, 0x9e, 0x51, 0x98, 0x53, 0xff, 0x3a, 0x6f, + 0x1a, 0x8c, 0x22, 0x86, 0xed, 0x43, 0x73, 0x24, 0x62, 0x4f, 0xf9, 0xe4, 0x50, 0x76, 0xd1, 0x6f, + 0xe0, 0x9c, 0x32, 0x3d, 0x3b, 0xdd, 0x8c, 0x61, 0xd6, 0x2a, 0xdf, 0x87, 0xed, 0xc1, 0xaa, 0xb8, + 0x88, 0xa4, 0xd2, 0x76, 0x14, 0x53, 0x1e, 0x5e, 0x31, 0x85, 0x26, 0xe2, 0x34, 0x12, 0x6f, 0x8a, + 0xec, 0x83, 0xb9, 0x50, 0xf6, 0xdc, 0x28, 0xb6, 0x49, 0xb9, 0xb3, 0x34, 0xde, 0xa1, 0x1b, 0x99, + 0x45, 0x3b, 0xf8, 0x10, 0xe7, 0xfa, 0x8b, 0xbf, 0xde, 0xb8, 0x95, 0xab, 0x64, 0xa6, 0xf2, 0x74, + 0xbe, 0x4b, 0xfe, 0x72, 0xee, 0xeb, 0xdd, 0x99, 0xf6, 0x83, 0x5d, 0x37, 0xf2, 0x51, 0x1d, 0x76, + 0xec, 0x77, 0x39, 0xa9, 0x66, 0x1f, 0x43, 0x2b, 0x52, 0x72, 0xa2, 0x44, 0x1c, 0x3f, 0xa3, 0xbc, + 0x66, 0xeb, 0xcd, 0x37, 0x6c, 0xfe, 0x25, 0xc9, 0x8f, 0x51, 0xc0, 0xd7, 0xa2, 0xfc, 0xe7, 0xfa, + 0x0f, 0xa1, 0xbd, 0x3c, 0xe3, 0xd7, 0xd9, 0xbd, 0xf5, 0xbb, 0xd0, 0x48, 0x67, 0xf0, 0xaa, 0x8e, + 0xf5, 0xfc, 0xb6, 0xff, 0xbe, 0x00, 0x55, 0x13, 0x8f, 0xec, 0x2e, 0x34, 0x02, 0xe9, 0xb9, 0x68, + 0x40, 0x52, 0xdb, 0xbf, 0x93, 0x85, 0xeb, 0xce, 0xc3, 0x44, 0x66, 0xf6, 0x23, 0xe3, 0xa2, 0x7b, + 0xfa, 0xe1, 0x58, 0x26, 0xf1, 0xd3, 0xca, 0x3a, 0xf5, 0xc3, 0xb1, 0xe4, 0x46, 0xb8, 0xfe, 0x00, + 0x5a, 0x8b, 0x2a, 0x2e, 0xb1, 0xf3, 0xbd, 0x45, 0x47, 0xa7, 0x6c, 0x90, 0x76, 0xca, 0x9b, 0x7d, + 0x17, 0x1a, 0x29, 0xce, 0xb6, 0x5f, 0x36, 0x7c, 0x35, 0xdf, 0x33, 0x67, 0xab, 0x13, 0x00, 0x64, + 0xa6, 0xe1, 0x31, 0x87, 0x97, 0x88, 0x30, 0x2b, 0x1e, 0xd2, 0x6f, 0xca, 0xbd, 0xae, 0x76, 0xc9, + 0x94, 0x55, 0x4e, 0x6d, 0xb6, 0x03, 0x30, 0x4a, 0x43, 0xfd, 0x5b, 0x0e, 0x80, 0x1c, 0xc3, 0x19, + 0x40, 0x3d, 0x31, 0x82, 0x6d, 0x42, 0x33, 0xb6, 0x23, 0x63, 0xad, 0x8b, 0xc3, 0x55, 0x78, 0x1e, + 0xc2, 0x9a, 0x55, 0xb9, 0xe1, 0x44, 0x2c, 0xd4, 0xac, 0x1c, 0x11, 0x6e, 0x05, 0xce, 0xa7, 0x50, + 0x21, 0x00, 0x03, 0x34, 0xd6, 0xae, 0xd2, 0xb6, 0xfc, 0x35, 0x15, 0x9e, 0x8c, 0x69, 0xd8, 0x83, + 0x32, 0xba, 0x30, 0x37, 0x04, 0xf6, 0x3e, 0xd6, 0x91, 0x23, 0xbb, 0xa2, 0x97, 0xf1, 0x50, 0xec, + 0xfc, 0x00, 0xea, 0x09, 0x8c, 0x33, 0x7f, 0xe8, 0x87, 0xc2, 0x9a, 0x48, 0x6d, 0xbc, 0x36, 0x1c, + 0x9e, 0xb9, 0xca, 0xf5, 0xb4, 0x30, 0x65, 0x4a, 0x85, 0x67, 0x80, 0xf3, 0x1e, 0x34, 0x73, 0x71, + 0x87, 0xee, 0xf6, 0x94, 0xb6, 0xd1, 0x44, 0xbf, 0xf9, 0x70, 0x3e, 0x84, 0xb5, 0x85, 0x18, 0xc0, + 0x64, 0xe5, 0x8f, 0x92, 0x64, 0x65, 0x12, 0xd1, 0x72, 0xb5, 0xe5, 0xfc, 0x0e, 0x6f, 0x42, 0x49, + 0xbd, 0xfa, 0xff, 0x00, 0x67, 0x5a, 0x47, 0xcf, 0xa8, 0x80, 0xb5, 0x1d, 0x1b, 0x88, 0x10, 0x83, + 0xdd, 0x80, 0x26, 0x7e, 0xc4, 0x56, 0x6e, 0xd4, 0x50, 0x8f, 0xd8, 0x10, 0xfe, 0x0f, 0x1a, 0xe3, + 0xb4, 0x7b, 0xc9, 0xee, 0x77, 0xd2, 0xfb, 0x1d, 0xa8, 0x87, 0xd2, 0xca, 0x4c, 0x3d, 0x5d, 0x0b, + 0x65, 0xda, 0xcf, 0x0d, 0x02, 0x2b, 0xab, 0x98, 0x7e, 0x6e, 0x10, 0x90, 0xd0, 0xb9, 0x05, 0x6f, + 0xbc, 0x74, 0xa7, 0x63, 0x6f, 0x41, 0x75, 0xec, 0x07, 0x9a, 0x12, 0x10, 0xd6, 0xef, 0xf6, 0xcb, + 0xf9, 0x67, 0x01, 0x20, 0xf3, 0x15, 0x8c, 0x00, 0xcc, 0x24, 0xc8, 0x59, 0x35, 0x99, 0x23, 0x80, + 0xfa, 0xd4, 0x9e, 0x49, 0xd6, 0x0b, 0xae, 0x2f, 0xfa, 0xd7, 0x4e, 0x72, 0x64, 0x99, 0xd3, 0x6a, + 0xcf, 0x9e, 0x56, 0xaf, 0x73, 0xef, 0x4a, 0x47, 0xa0, 0xa2, 0x2a, 0x7f, 0x0d, 0x87, 0x2c, 0x74, + 0xb9, 0x95, 0xac, 0x3f, 0x80, 0xb5, 0x85, 0x21, 0xbf, 0x63, 0x7e, 0xca, 0xce, 0xd6, 0x7c, 0xdc, + 0xee, 0x41, 0xd5, 0xdc, 0xdf, 0xd9, 0x16, 0xd4, 0x5c, 0xcf, 0x84, 0x6c, 0xee, 0xd8, 0x40, 0xe1, + 0x3e, 0xc1, 0x3c, 0x11, 0x3b, 0x7f, 0x2e, 0x02, 0x64, 0xf8, 0x6b, 0x54, 0xd6, 0xf7, 0xa0, 0x15, + 0x0b, 0x4f, 0x86, 0x23, 0x57, 0xcd, 0x49, 0x6a, 0x2f, 0x98, 0x97, 0x75, 0x59, 0x62, 0xe6, 0xaa, + 0xec, 0xd2, 0xab, 0xab, 0xec, 0x2d, 0x28, 0x7b, 0x32, 0x9a, 0xdb, 0x34, 0xc4, 0x16, 0x27, 0x72, + 0x28, 0xa3, 0xf9, 0xd1, 0x0a, 0x27, 0x06, 0xdb, 0x81, 0xea, 0xf4, 0x9c, 0x5e, 0x34, 0xcc, 0xcd, + 0xec, 0xda, 0x22, 0xf7, 0xd1, 0x39, 0xb6, 0x8f, 0x56, 0xb8, 0x65, 0xb1, 0x5b, 0x50, 0x99, 0x9e, + 0x8f, 0x7c, 0x65, 0x13, 0xc9, 0xd5, 0x65, 0x7a, 0xd7, 0x57, 0xf4, 0x80, 0x81, 0x1c, 0xe6, 0x40, + 0x51, 0x4d, 0xed, 0xf3, 0x45, 0x7b, 0x69, 0x35, 0xa7, 0x47, 0x2b, 0xbc, 0xa8, 0xa6, 0x07, 0x75, + 0xa8, 0x9a, 0x75, 0x75, 0xfe, 0x51, 0x82, 0xd6, 0xa2, 0x95, 0xb8, 0xb3, 0xb1, 0xf2, 0x92, 0x9d, + 0x8d, 0x95, 0x97, 0x5e, 0x40, 0x8a, 0xb9, 0x0b, 0x88, 0x03, 0x15, 0xf9, 0x3c, 0x14, 0x2a, 0xff, + 0x74, 0x73, 0x78, 0x26, 0x9f, 0x87, 0x58, 0x04, 0x1b, 0xd1, 0x42, 0x4d, 0x59, 0xb1, 0x35, 0xe5, + 0xfb, 0xb0, 0x36, 0x96, 0x41, 0x20, 0x9f, 0x0f, 0xe7, 0xd3, 0xc0, 0x0f, 0xcf, 0x6d, 0x61, 0xb9, + 0x08, 0xb2, 0x2d, 0xb8, 0x32, 0xf2, 0x15, 0x9a, 0x73, 0x28, 0x43, 0x2d, 0x42, 0xba, 0x98, 0x22, + 0x6f, 0x19, 0x66, 0x9f, 0xc0, 0xa6, 0xab, 0xb5, 0x98, 0x46, 0xfa, 0x49, 0x18, 0xb9, 0xde, 0x79, + 0x57, 0x7a, 0x14, 0x85, 0xd3, 0xc8, 0xd5, 0xfe, 0xa9, 0x1f, 0xe0, 0x85, 0xbd, 0x46, 0x5d, 0x5f, + 0xc9, 0x63, 0x1f, 0x40, 0xcb, 0x53, 0xc2, 0xd5, 0xa2, 0x2b, 0x62, 0x7d, 0xe2, 0xea, 0xb3, 0x4e, + 0x9d, 0x7a, 0x2e, 0xa1, 0x38, 0x07, 0x17, 0xad, 0xfd, 0xd4, 0x0f, 0x46, 0x1e, 0x5e, 0x25, 0x1b, + 0x66, 0x0e, 0x0b, 0x20, 0xdb, 0x01, 0x46, 0x40, 0x6f, 0x1a, 0xe9, 0x79, 0x4a, 0x05, 0xa2, 0x5e, + 0x22, 0xc1, 0xc3, 0x55, 0xfb, 0x53, 0x11, 0x6b, 0x77, 0x1a, 0xd1, 0x5b, 0x51, 0x89, 0x67, 0x00, + 0xbb, 0x09, 0x6d, 0x3f, 0xf4, 0x82, 0xd9, 0x48, 0x3c, 0x8b, 0x70, 0x22, 0x2a, 0x8c, 0x3b, 0xab, + 0x74, 0xaa, 0x5c, 0xb1, 0xf8, 0x89, 0x85, 0x91, 0x2a, 0x2e, 0x96, 0xa8, 0x6b, 0x86, 0x6a, 0xf1, + 0x84, 0xea, 0x7c, 0x5e, 0x80, 0xf6, 0xb2, 0xe3, 0xe1, 0xb6, 0x45, 0x38, 0x79, 0x7b, 0x91, 0xc6, + 0x76, 0xba, 0x95, 0xc5, 0xdc, 0x56, 0x26, 0xb9, 0xb1, 0x94, 0xcb, 0x8d, 0xa9, 0x5b, 0x94, 0xbf, + 0xdd, 0x2d, 0x16, 0x26, 0x5a, 0x59, 0x9a, 0xa8, 0xf3, 0xdb, 0x02, 0x5c, 0x59, 0x72, 0xee, 0xef, + 0x6c, 0xd1, 0x26, 0x34, 0xa7, 0xee, 0xb9, 0x30, 0x0f, 0x09, 0xb1, 0xbd, 0x98, 0xe7, 0xa1, 0xff, + 0x82, 0x7d, 0x21, 0xac, 0xe6, 0x23, 0xea, 0x52, 0xdb, 0x12, 0x07, 0x39, 0x96, 0xfa, 0xbe, 0x9c, + 0xd9, 0xbc, 0x9b, 0x38, 0x48, 0x02, 0xbe, 0xec, 0x46, 0xa5, 0x4b, 0xdc, 0xc8, 0x39, 0x86, 0x7a, + 0x62, 0x20, 0xbb, 0x61, 0x5f, 0x7a, 0x0a, 0xd9, 0x03, 0xe6, 0x93, 0x58, 0x28, 0xb4, 0xdd, 0x3c, + 0xfb, 0xbc, 0x0b, 0x15, 0x53, 0x72, 0x16, 0x5f, 0x66, 0x18, 0x89, 0x33, 0x84, 0x9a, 0x45, 0xd8, + 0x36, 0x54, 0x4f, 0xe7, 0xe9, 0x9b, 0x89, 0x3d, 0x2e, 0xf0, 0x7b, 0x64, 0x19, 0x78, 0x06, 0x19, + 0x06, 0xbb, 0x06, 0xe5, 0xd3, 0x79, 0xbf, 0x6b, 0x2e, 0x91, 0x78, 0x92, 0xe1, 0xd7, 0x41, 0xd5, + 0x18, 0xe4, 0x3c, 0x84, 0xd5, 0x7c, 0xbf, 0x34, 0x89, 0x17, 0x72, 0x4f, 0x26, 0xe9, 0x91, 0x5d, + 0x7c, 0xd5, 0x6d, 0xe2, 0x23, 0x00, 0x7a, 0x97, 0x7d, 0xdd, 0x5b, 0xc8, 0xf7, 0xa1, 0x66, 0xdf, + 0x73, 0xd9, 0x07, 0x4b, 0xef, 0xd3, 0xad, 0xf4, 0xb1, 0x77, 0xe1, 0x91, 0xda, 0xb9, 0x87, 0xf5, + 0xe8, 0x73, 0xa1, 0xba, 0xfe, 0x78, 0xfc, 0xba, 0xc3, 0xdd, 0x83, 0xd6, 0x93, 0x28, 0xfa, 0xf7, + 0xfa, 0xfe, 0x14, 0xaa, 0xe6, 0x59, 0x19, 0xfb, 0x04, 0x68, 0x81, 0xdd, 0x03, 0x66, 0x6a, 0xd6, + 0xbc, 0x49, 0xdc, 0x10, 0x90, 0x39, 0xc3, 0xf1, 0xec, 0xe6, 0x12, 0x73, 0xd1, 0x00, 0x6e, 0x08, + 0xdb, 0x5b, 0x50, 0xb3, 0x2f, 0x98, 0xac, 0x01, 0x95, 0x27, 0xc7, 0xc3, 0xde, 0xe3, 0xf6, 0x0a, + 0xab, 0x43, 0xf9, 0x68, 0x30, 0x7c, 0xdc, 0x2e, 0x60, 0xeb, 0x78, 0x70, 0xdc, 0x6b, 0x17, 0xb7, + 0x6f, 0xc2, 0x6a, 0xfe, 0x0d, 0x93, 0x35, 0xa1, 0x36, 0xdc, 0x3f, 0xee, 0x1e, 0x0c, 0x7e, 0xd2, + 0x5e, 0x61, 0xab, 0x50, 0xef, 0x1f, 0x0f, 0x7b, 0x87, 0x4f, 0x78, 0xaf, 0x5d, 0xd8, 0xfe, 0x11, + 0x34, 0xd2, 0x47, 0x21, 0xd4, 0x70, 0xd0, 0x3f, 0xee, 0xb6, 0x57, 0x18, 0x40, 0x75, 0xd8, 0x3b, + 0xe4, 0x3d, 0xd4, 0x5b, 0x83, 0xd2, 0x70, 0x78, 0xd4, 0x2e, 0xe2, 0xa8, 0x87, 0xfb, 0x87, 0x47, + 0xbd, 0x76, 0x09, 0x9b, 0x8f, 0x1f, 0x9d, 0xdc, 0x1f, 0xb6, 0xcb, 0xdb, 0x1f, 0xc1, 0x95, 0xa5, + 0xe7, 0x12, 0xea, 0x7d, 0xb4, 0xcf, 0x7b, 0xa8, 0xa9, 0x09, 0xb5, 0x13, 0xde, 0x7f, 0xba, 0xff, + 0xb8, 0xd7, 0x2e, 0xa0, 0xe0, 0xe1, 0xe0, 0xf0, 0x41, 0xaf, 0xdb, 0x2e, 0x1e, 0x5c, 0xff, 0xe2, + 0xc5, 0x46, 0xe1, 0xcb, 0x17, 0x1b, 0x85, 0xaf, 0x5e, 0x6c, 0x14, 0xfe, 0xf6, 0x62, 0xa3, 0xf0, + 0xf9, 0x37, 0x1b, 0x2b, 0x5f, 0x7e, 0xb3, 0xb1, 0xf2, 0xd5, 0x37, 0x1b, 0x2b, 0xa7, 0x55, 0xfa, + 0x63, 0xe2, 0xc3, 0x7f, 0x05, 0x00, 0x00, 0xff, 0xff, 0xda, 0xe3, 0x79, 0x1d, 0xd8, 0x18, 0x00, + 0x00, } func (m *Op) Marshal() (dAtA []byte, err error) { @@ -4019,6 +4079,18 @@ func (m *OpMetadata) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if m.ProgressGroup != nil { + { + size, err := m.ProgressGroup.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintOps(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x32 + } if len(m.Caps) > 0 { keysForCaps := make([]string, 0, len(m.Caps)) for k := range m.Caps { @@ -4400,6 +4472,43 @@ func (m *ExportCache) MarshalToSizedBuffer(dAtA []byte) (int, error) { return len(dAtA) - i, nil } +func (m *ProgressGroup) Marshal() (dAtA []byte, err error) { + size := m.Size() + dAtA = make([]byte, size) + n, err := m.MarshalToSizedBuffer(dAtA[:size]) + if err != nil { + return nil, err + } + return dAtA[:n], nil +} + +func (m *ProgressGroup) MarshalTo(dAtA []byte) (int, error) { + size := m.Size() + return m.MarshalToSizedBuffer(dAtA[:size]) +} + +func (m *ProgressGroup) MarshalToSizedBuffer(dAtA []byte) (int, error) { + i := len(dAtA) + _ = i + var l int + _ = l + if len(m.Name) > 0 { + i -= len(m.Name) + copy(dAtA[i:], m.Name) + i = encodeVarintOps(dAtA, i, uint64(len(m.Name))) + i-- + dAtA[i] = 0x12 + } + if len(m.Id) > 0 { + i -= len(m.Id) + copy(dAtA[i:], m.Id) + i = encodeVarintOps(dAtA, i, uint64(len(m.Id))) + i-- + dAtA[i] = 0xa + } + return len(dAtA) - i, nil +} + func (m *ProxyEnv) Marshal() (dAtA []byte, err error) { size := m.Size() dAtA = make([]byte, size) @@ -5876,6 +5985,10 @@ func (m *OpMetadata) Size() (n int) { n += mapEntrySize + 1 + sovOps(uint64(mapEntrySize)) } } + if m.ProgressGroup != nil { + l = m.ProgressGroup.Size() + n += 1 + l + sovOps(uint64(l)) + } return n } @@ -6001,6 +6114,23 @@ func (m *ExportCache) Size() (n int) { return n } +func (m *ProgressGroup) Size() (n int) { + if m == nil { + return 0 + } + var l int + _ = l + l = len(m.Id) + if l > 0 { + n += 1 + l + sovOps(uint64(l)) + } + l = len(m.Name) + if l > 0 { + n += 1 + l + sovOps(uint64(l)) + } + return n +} + func (m *ProxyEnv) Size() (n int) { if m == nil { return 0 @@ -9830,6 +9960,42 @@ func (m *OpMetadata) Unmarshal(dAtA []byte) error { } m.Caps[github_com_moby_buildkit_util_apicaps.CapID(mapkey)] = mapvalue iNdEx = postIndex + case 6: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field ProgressGroup", wireType) + } + var msglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + msglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if msglen < 0 { + return ErrInvalidLengthOps + } + postIndex := iNdEx + msglen + if postIndex < 0 { + return ErrInvalidLengthOps + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + if m.ProgressGroup == nil { + m.ProgressGroup = &ProgressGroup{} + } + if err := m.ProgressGroup.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipOps(dAtA[iNdEx:]) @@ -10677,6 +10843,120 @@ func (m *ExportCache) Unmarshal(dAtA []byte) error { } return nil } +func (m *ProgressGroup) Unmarshal(dAtA []byte) error { + l := len(dAtA) + iNdEx := 0 + for iNdEx < l { + preIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + wireType := int(wire & 0x7) + if wireType == 4 { + return fmt.Errorf("proto: ProgressGroup: wiretype end group for non-group") + } + if fieldNum <= 0 { + return fmt.Errorf("proto: ProgressGroup: illegal tag %d (wire type %d)", fieldNum, wire) + } + switch fieldNum { + case 1: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Id", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthOps + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthOps + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Id = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + case 2: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Name", wireType) + } + var stringLen uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowOps + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLen |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLen := int(stringLen) + if intStringLen < 0 { + return ErrInvalidLengthOps + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthOps + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.Name = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex + default: + iNdEx = preIndex + skippy, err := skipOps(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthOps + } + if (iNdEx + skippy) > l { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + + if iNdEx > l { + return io.ErrUnexpectedEOF + } + return nil +} func (m *ProxyEnv) Unmarshal(dAtA []byte) error { l := len(dAtA) iNdEx := 0 diff --git a/solver/pb/ops.proto b/solver/pb/ops.proto index c43b85a52fda..7d814619a2a8 100644 --- a/solver/pb/ops.proto +++ b/solver/pb/ops.proto @@ -207,6 +207,8 @@ message OpMetadata { ExportCache export_cache = 4; map caps = 5 [(gogoproto.castkey) = "github.com/moby/buildkit/util/apicaps.CapID", (gogoproto.nullable) = false]; + + ProgressGroup progress_group = 6; } // Source is a source mapping description for a file @@ -249,6 +251,11 @@ message ExportCache { bool Value = 1; } +message ProgressGroup { + string id = 1; + string name = 2; +} + message ProxyEnv { string http_proxy = 1; string https_proxy = 2; diff --git a/solver/types.go b/solver/types.go index 1aec5f4938f0..03a55f289ae5 100644 --- a/solver/types.go +++ b/solver/types.go @@ -55,6 +55,7 @@ type VertexOptions struct { Description map[string]string // text values with no special meaning for solver ExportCache *bool // WorkerConstraint + ProgressGroup *pb.ProgressGroup } // Result is an abstract return value for a solve diff --git a/source/containerimage/pull.go b/source/containerimage/pull.go index a04a1c5a2e5d..665954b98622 100644 --- a/source/containerimage/pull.go +++ b/source/containerimage/pull.go @@ -213,6 +213,7 @@ func (p *puller) CacheKey(ctx context.Context, g session.Group, index int) (cach if p.vtx != nil { progressController.Digest = p.vtx.Digest() progressController.Name = p.vtx.Name() + progressController.ProgressGroup = p.vtx.Options().ProgressGroup } p.descHandlers = cache.DescHandlers(make(map[digest.Digest]*cache.DescHandler)) diff --git a/util/progress/controller/controller.go b/util/progress/controller/controller.go index dfadb1cb6241..cda1bc03a96f 100644 --- a/util/progress/controller/controller.go +++ b/util/progress/controller/controller.go @@ -6,6 +6,7 @@ import ( "time" "github.com/moby/buildkit/client" + "github.com/moby/buildkit/solver/pb" "github.com/moby/buildkit/util/progress" digest "github.com/opencontainers/go-digest" ) @@ -18,6 +19,7 @@ type Controller struct { Digest digest.Digest Name string WriterFactory progress.WriterFactory + ProgressGroup *pb.ProgressGroup } var _ progress.Controller = &Controller{} @@ -32,9 +34,10 @@ func (c *Controller) Start(ctx context.Context) (context.Context, func(error)) { if c.Digest != "" { c.writer.Write(c.Digest.String(), client.Vertex{ - Digest: c.Digest, - Name: c.Name, - Started: c.started, + Digest: c.Digest, + Name: c.Name, + Started: c.started, + ProgressGroup: c.ProgressGroup, }) } } @@ -47,11 +50,12 @@ func (c *Controller) Start(ctx context.Context) (context.Context, func(error)) { } if c.Digest != "" { c.writer.Write(c.Digest.String(), client.Vertex{ - Digest: c.Digest, - Name: c.Name, - Started: c.started, - Completed: &now, - Error: errString, + Digest: c.Digest, + Name: c.Name, + Started: c.started, + Completed: &now, + Error: errString, + ProgressGroup: c.ProgressGroup, }) } c.writer.Close() diff --git a/util/progress/progressui/display.go b/util/progress/progressui/display.go index 8f9ddf858072..a7f177b4cfa3 100644 --- a/util/progress/progressui/display.go +++ b/util/progress/progressui/display.go @@ -121,10 +121,12 @@ type trace struct { nextIndex int updates map[digest.Digest]struct{} modeConsole bool + groups map[string]*vertexGroup // group id -> group } type vertex struct { *client.Vertex + statuses []*status byID map[string]*status indent string @@ -159,6 +161,42 @@ func (v *vertex) update(c int) { v.count += c } +type vertexGroup struct { + *vertex + subVtxs map[digest.Digest]client.Vertex +} + +func (vg *vertexGroup) mergeSubVtxs() *client.Vertex { + newVtx := *vg.Vertex + newVtx.Completed = &time.Time{} + newVtx.Cached = true + for _, subVtx := range vg.subVtxs { + // Group start time is the earliest start time of the vtxs (or nil if none have started) + if newVtx.Started == nil || (subVtx.Started != nil && subVtx.Started.Before(*newVtx.Started)) { + newVtx.Started = subVtx.Started + } + + // Group is considered not completed if any vtxs are not completed. If all vtxs + // are completed, then group completed time is the latest of those + if newVtx.Completed != nil { + if subVtx.Completed == nil { + newVtx.Completed = nil + } else if subVtx.Completed.After(*newVtx.Completed) { + newVtx.Completed = subVtx.Completed + } + } + + // Group is considered cached iff all subVtxs are cached + newVtx.Cached = newVtx.Cached && subVtx.Cached + + // Group error is set to the first error found in subvtxs, if any + if newVtx.Error == "" { + newVtx.Error = subVtx.Error + } + } + return &newVtx +} + type status struct { *client.VertexStatus } @@ -169,6 +207,7 @@ func newTrace(w io.Writer, modeConsole bool) *trace { updates: make(map[digest.Digest]struct{}), w: w, modeConsole: modeConsole, + groups: make(map[string]*vertexGroup), } } @@ -222,7 +261,36 @@ func (t *trace) triggerVertexEvent(v *client.Vertex) { } func (t *trace) update(s *client.SolveStatus, termWidth int) { + groups := make(map[string]struct{}) for _, v := range s.Vertexes { + if v.ProgressGroup != nil { + group, ok := t.groups[v.ProgressGroup.Id] + if !ok { + t.nextIndex++ + group = &vertexGroup{ + vertex: &vertex{ + Vertex: &client.Vertex{ + Digest: digest.Digest(v.ProgressGroup.Id), + Name: v.ProgressGroup.Name, + Completed: &time.Time{}, + }, + byID: make(map[string]*status), + statusUpdates: make(map[string]struct{}), + index: t.nextIndex, + }, + subVtxs: make(map[digest.Digest]client.Vertex), + } + if t.modeConsole { + group.term = vt100.NewVT100(termHeight, termWidth-termPad) + } + t.groups[v.ProgressGroup.Id] = group + t.byDigest[group.Digest] = group.vertex + } + groups[v.ProgressGroup.Id] = struct{}{} + group.subVtxs[v.Digest] = *v + t.byDigest[v.Digest] = group.vertex + continue + } prev, ok := t.byDigest[v.Digest] if !ok { t.nextIndex++ @@ -248,6 +316,19 @@ func (t *trace) update(s *client.SolveStatus, termWidth int) { } t.byDigest[v.Digest].jobCached = false } + for groupID := range groups { + group := t.groups[groupID] + newVtx := group.mergeSubVtxs() + t.triggerVertexEvent(newVtx) + if newVtx.Started != nil && group.Started == nil { + if t.localTimeDiff == 0 { + t.localTimeDiff = time.Since(*newVtx.Started) + } + t.vertexes = append(t.vertexes, group.vertex) + } + group.Vertex = newVtx + group.jobCached = false + } for _, s := range s.Statuses { v, ok := t.byDigest[s.Vertex] if !ok { @@ -344,6 +425,11 @@ func (t *trace) displayInfo() (d displayInfo) { } d.countTotal = len(t.byDigest) for _, v := range t.byDigest { + if v.ProgressGroup != nil { + // don't count vtxs in a group, they are merged into a single vtx + d.countTotal-- + continue + } if v.Completed != nil { d.countCompleted++ } From 2dfec01fed2edde59eab97bf6055cf2a75f79937 Mon Sep 17 00:00:00 2001 From: Erik Sipsma Date: Thu, 3 Feb 2022 18:35:41 -0800 Subject: [PATCH 4/5] progress: support vertex start/stop intervals. Previously, vertexes sent to progress might have multiple intervals in which they started+stopped, but the output only knew about the most recent interval. Now, all the intervals are tracked and the total time spent on each (after accounting for overlaps) is displayed. This is implemented both for normal vertexes and progress group vertexes. Signed-off-by: Erik Sipsma --- util/progress/progressui/display.go | 279 ++++++++++++++++++----- util/progress/progressui/display_test.go | 168 ++++++++++++++ 2 files changed, 386 insertions(+), 61 deletions(-) create mode 100644 util/progress/progressui/display_test.go diff --git a/util/progress/progressui/display.go b/util/progress/progressui/display.go index a7f177b4cfa3..8aa07283bcbd 100644 --- a/util/progress/progressui/display.go +++ b/util/progress/progressui/display.go @@ -103,18 +103,19 @@ type displayInfo struct { } type job struct { - startTime *time.Time - completedTime *time.Time - name string - status string - hasError bool - isCanceled bool - vertex *vertex - showTerm bool + intervals []interval + isCompleted bool + name string + status string + hasError bool + isCanceled bool + vertex *vertex + showTerm bool } type trace struct { w io.Writer + startTime *time.Time localTimeDiff time.Duration vertexes []*vertex byDigest map[digest.Digest]*vertex @@ -151,6 +152,11 @@ type vertex struct { term *vt100.VT100 termBytes int termCount int + + // Interval start time in unix nano -> interval. Using a map ensures + // that updates for the same interval overwrite their previous updates. + intervals map[int64]interval + mostRecentStart *time.Time } func (v *vertex) update(c int) { @@ -161,32 +167,48 @@ func (v *vertex) update(c int) { v.count += c } +func (v *vertex) isStarted() bool { + return len(v.intervals) > 0 +} + +func (v *vertex) isCompleted() bool { + for _, ival := range v.intervals { + if ival.stop == nil { + return false + } + } + return true +} + type vertexGroup struct { *vertex subVtxs map[digest.Digest]client.Vertex } -func (vg *vertexGroup) mergeSubVtxs() *client.Vertex { +func (vg *vertexGroup) refresh() (changed, newlyStarted bool) { newVtx := *vg.Vertex - newVtx.Completed = &time.Time{} newVtx.Cached = true + alreadyStarted := vg.isStarted() for _, subVtx := range vg.subVtxs { - // Group start time is the earliest start time of the vtxs (or nil if none have started) - if newVtx.Started == nil || (subVtx.Started != nil && subVtx.Started.Before(*newVtx.Started)) { - newVtx.Started = subVtx.Started - } - - // Group is considered not completed if any vtxs are not completed. If all vtxs - // are completed, then group completed time is the latest of those - if newVtx.Completed != nil { - if subVtx.Completed == nil { - newVtx.Completed = nil - } else if subVtx.Completed.After(*newVtx.Completed) { - newVtx.Completed = subVtx.Completed + if subVtx.Started != nil { + newInterval := interval{ + start: subVtx.Started, + stop: subVtx.Completed, + } + prevInterval := vg.intervals[subVtx.Started.UnixNano()] + if !newInterval.isEqual(prevInterval) { + changed = true + } + if !alreadyStarted { + newlyStarted = true + } + vg.intervals[subVtx.Started.UnixNano()] = newInterval + if vg.mostRecentStart == nil || subVtx.Started.After(*vg.mostRecentStart) { + vg.mostRecentStart = subVtx.Started } } - // Group is considered cached iff all subVtxs are cached + // Group is considered cached iff all subvtxs are cached newVtx.Cached = newVtx.Cached && subVtx.Cached // Group error is set to the first error found in subvtxs, if any @@ -194,7 +216,109 @@ func (vg *vertexGroup) mergeSubVtxs() *client.Vertex { newVtx.Error = subVtx.Error } } - return &newVtx + + if vg.Cached != newVtx.Cached { + changed = true + } + if vg.Error != newVtx.Error { + changed = true + } + vg.Vertex = &newVtx + + return changed, newlyStarted +} + +type interval struct { + start *time.Time + stop *time.Time +} + +func (ival interval) duration() time.Duration { + if ival.start == nil { + return 0 + } + if ival.stop == nil { + return time.Since(*ival.start) + } + return ival.stop.Sub(*ival.start) +} + +func (ival interval) isEqual(other interval) (isEqual bool) { + return equalTimes(ival.start, other.start) && equalTimes(ival.stop, other.stop) +} + +func equalTimes(t1, t2 *time.Time) bool { + if t2 == nil { + return t1 == nil + } + if t1 == nil { + return false + } + return t1.Equal(*t2) +} + +// mergeIntervals takes a slice of (start, stop) pairs and returns a slice where +// any intervals that overlap in time are combined into a single interval. If an +// interval's stop time is nil, it is treated as positive infinity and consumes +// any intervals after it. Intervals with nil start times are ignored and not +// returned. +func mergeIntervals(intervals []interval) []interval { + // remove any intervals that have not started + var filtered []interval + for _, interval := range intervals { + if interval.start != nil { + filtered = append(filtered, interval) + } + } + intervals = filtered + + if len(intervals) == 0 { + return nil + } + + // sort intervals by start time + sort.Slice(intervals, func(i, j int) bool { + return intervals[i].start.Before(*intervals[j].start) + }) + + var merged []interval + cur := intervals[0] + for i := 1; i < len(intervals); i++ { + next := intervals[i] + if cur.stop == nil { + // if cur doesn't stop, all intervals after it will be merged into it + merged = append(merged, cur) + return merged + } + if cur.stop.Before(*next.start) { + // if cur stops before next starts, no intervals after cur will be + // merged into it; cur stands on its own + merged = append(merged, cur) + cur = next + continue + } + if next.stop == nil { + // cur and next partially overlap, but next also never stops, so all + // subsequent intervals will be merged with both cur and next + merged = append(merged, interval{ + start: cur.start, + stop: nil, + }) + return merged + } + if cur.stop.After(*next.stop) || cur.stop.Equal(*next.stop) { + // cur fully subsumes next + continue + } + // cur partially overlaps with next, merge them together into cur + cur = interval{ + start: cur.start, + stop: next.stop, + } + } + // append anything we are left with + merged = append(merged, cur) + return merged } type status struct { @@ -263,6 +387,9 @@ func (t *trace) triggerVertexEvent(v *client.Vertex) { func (t *trace) update(s *client.SolveStatus, termWidth int) { groups := make(map[string]struct{}) for _, v := range s.Vertexes { + if t.startTime == nil { + t.startTime = v.Started + } if v.ProgressGroup != nil { group, ok := t.groups[v.ProgressGroup.Id] if !ok { @@ -270,13 +397,13 @@ func (t *trace) update(s *client.SolveStatus, termWidth int) { group = &vertexGroup{ vertex: &vertex{ Vertex: &client.Vertex{ - Digest: digest.Digest(v.ProgressGroup.Id), - Name: v.ProgressGroup.Name, - Completed: &time.Time{}, + Digest: digest.Digest(v.ProgressGroup.Id), + Name: v.ProgressGroup.Name, }, byID: make(map[string]*status), statusUpdates: make(map[string]struct{}), index: t.nextIndex, + intervals: make(map[int64]interval), }, subVtxs: make(map[digest.Digest]client.Vertex), } @@ -298,35 +425,47 @@ func (t *trace) update(s *client.SolveStatus, termWidth int) { byID: make(map[string]*status), statusUpdates: make(map[string]struct{}), index: t.nextIndex, + intervals: make(map[int64]interval), } if t.modeConsole { t.byDigest[v.Digest].term = vt100.NewVT100(termHeight, termWidth-termPad) } } t.triggerVertexEvent(v) - if v.Started != nil && (prev == nil || prev.Started == nil) { + if v.Started != nil && (prev == nil || !prev.isStarted()) { if t.localTimeDiff == 0 { t.localTimeDiff = time.Since(*v.Started) } t.vertexes = append(t.vertexes, t.byDigest[v.Digest]) } // allow a duplicate initial vertex that shouldn't reset state - if !(prev != nil && prev.Started != nil && v.Started == nil) { + if !(prev != nil && prev.isStarted() && v.Started == nil) { t.byDigest[v.Digest].Vertex = v } + if v.Started != nil { + t.byDigest[v.Digest].intervals[v.Started.UnixNano()] = interval{ + start: v.Started, + stop: v.Completed, + } + if t.byDigest[v.Digest].mostRecentStart == nil || v.Started.After(*t.byDigest[v.Digest].mostRecentStart) { + t.byDigest[v.Digest].mostRecentStart = v.Started + } + } t.byDigest[v.Digest].jobCached = false } for groupID := range groups { group := t.groups[groupID] - newVtx := group.mergeSubVtxs() - t.triggerVertexEvent(newVtx) - if newVtx.Started != nil && group.Started == nil { + changed, newlyStarted := group.refresh() + if changed { + group.update(1) + t.updates[group.Digest] = struct{}{} + } + if newlyStarted { if t.localTimeDiff == 0 { - t.localTimeDiff = time.Since(*newVtx.Started) + t.localTimeDiff = time.Since(*group.mostRecentStart) } t.vertexes = append(t.vertexes, group.vertex) } - group.Vertex = newVtx group.jobCached = false } for _, s := range s.Statuses { @@ -374,8 +513,8 @@ func (t *trace) update(s *client.SolveStatus, termWidth int) { v.logs[len(v.logs)-1] = append(v.logs[len(v.logs)-1], dt...) } else { ts := time.Duration(0) - if v.Started != nil { - ts = l.Timestamp.Sub(*v.Started) + if v.isStarted() { + ts = l.Timestamp.Sub(*v.mostRecentStart) } prec := 1 sec := ts.Seconds() @@ -420,8 +559,8 @@ func (t *trace) printErrorLogs(f io.Writer) { func (t *trace) displayInfo() (d displayInfo) { d.startTime = time.Now() - if t.localTimeDiff != 0 { - d.startTime = (*t.vertexes[0].Started).Add(t.localTimeDiff) + if t.startTime != nil { + d.startTime = t.startTime.Add(t.localTimeDiff) } d.countTotal = len(t.byDigest) for _, v := range t.byDigest { @@ -430,7 +569,7 @@ func (t *trace) displayInfo() (d displayInfo) { d.countTotal-- continue } - if v.Completed != nil { + if v.isCompleted() { d.countCompleted++ } } @@ -442,11 +581,20 @@ func (t *trace) displayInfo() (d displayInfo) { } var jobs []*job j := &job{ - startTime: addTime(v.Started, t.localTimeDiff), - completedTime: addTime(v.Completed, t.localTimeDiff), - name: strings.Replace(v.Name, "\t", " ", -1), - vertex: v, + name: strings.Replace(v.Name, "\t", " ", -1), + vertex: v, + isCompleted: true, + } + for _, ival := range v.intervals { + j.intervals = append(j.intervals, interval{ + start: addTime(ival.start, t.localTimeDiff), + stop: addTime(ival.stop, t.localTimeDiff), + }) + if ival.stop == nil { + j.isCompleted = false + } } + j.intervals = mergeIntervals(j.intervals) if v.Error != "" { if strings.HasSuffix(v.Error, context.Canceled.Error()) { j.isCanceled = true @@ -463,9 +611,12 @@ func (t *trace) displayInfo() (d displayInfo) { jobs = append(jobs, j) for _, s := range v.statuses { j := &job{ - startTime: addTime(s.Started, t.localTimeDiff), - completedTime: addTime(s.Completed, t.localTimeDiff), - name: v.indent + "=> " + s.ID, + intervals: []interval{{ + start: addTime(s.Started, t.localTimeDiff), + stop: addTime(s.Completed, t.localTimeDiff), + }}, + isCompleted: s.Completed != nil, + name: v.indent + "=> " + s.ID, } if s.Total != 0 { j.status = fmt.Sprintf("%.2f / %.2f", units.Bytes(s.Current), units.Bytes(s.Total)) @@ -476,11 +627,18 @@ func (t *trace) displayInfo() (d displayInfo) { } for _, w := range v.warnings { msg := "WARN: " + string(w.Short) + mostRecentStart := v.mostRecentStart + var mostRecentStop *time.Time + if mostRecentStart != nil { + mostRecentStop = v.intervals[mostRecentStart.UnixNano()].stop + } j := &job{ - startTime: addTime(v.Started, t.localTimeDiff), - completedTime: addTime(v.Completed, t.localTimeDiff), - name: msg, - isCanceled: true, + intervals: []interval{{ + start: addTime(mostRecentStart, t.localTimeDiff), + stop: addTime(mostRecentStop, t.localTimeDiff), + }}, + name: msg, + isCanceled: true, } jobs = append(jobs, j) } @@ -542,10 +700,10 @@ func setupTerminals(jobs []*job, height int, all bool) []*job { var candidates []*job numInUse := 0 for _, j := range jobs { - if j.vertex != nil && j.vertex.termBytes > 0 && j.completedTime == nil { + if j.vertex != nil && j.vertex.termBytes > 0 && !j.isCompleted { candidates = append(candidates, j) } - if j.completedTime == nil { + if j.isCompleted { numInUse++ } } @@ -598,14 +756,13 @@ func (disp *display) print(d displayInfo, width, height int, all bool) { fmt.Fprintln(disp.c, out) lineCount := 0 for _, j := range d.jobs { - endTime := time.Now() - if j.completedTime != nil { - endTime = *j.completedTime - } - if j.startTime == nil { + if len(j.intervals) == 0 { continue } - dt := endTime.Sub(*j.startTime).Seconds() + var dt float64 + for _, ival := range j.intervals { + dt += ival.duration().Seconds() + } if dt < 0.05 { dt = 0 } @@ -635,7 +792,7 @@ func (disp *display) print(d displayInfo, width, height int, all bool) { } out = align(out, timer, width) - if j.completedTime != nil { + if j.isCompleted { color := colorRun if j.isCanceled { color = colorCancel @@ -695,7 +852,7 @@ func wrapHeight(j []*job, limit int) []*job { // wrap things around if incomplete jobs were cut var invisible []*job for _, j := range j[:len(j)-limit] { - if j.completedTime == nil { + if !j.isCompleted { invisible = append(invisible, j) } } @@ -703,7 +860,7 @@ func wrapHeight(j []*job, limit int) []*job { if l := len(invisible); l > 0 { rewrapped := make([]*job, 0, len(wrapped)) for _, j := range wrapped { - if j.completedTime == nil || l <= 0 { + if !j.isCompleted || l <= 0 { rewrapped = append(rewrapped, j) } l-- diff --git a/util/progress/progressui/display_test.go b/util/progress/progressui/display_test.go new file mode 100644 index 000000000000..44c2a4ec87d0 --- /dev/null +++ b/util/progress/progressui/display_test.go @@ -0,0 +1,168 @@ +package progressui + +import ( + "testing" + "time" + + "github.com/stretchr/testify/require" +) + +func mkinterval(start, stop int64) interval { + unixStart := time.Unix(start, 0) + unixStop := time.Unix(stop, 0) + return interval{start: &unixStart, stop: &unixStop} +} + +func mkOpenInterval(start int64) interval { + unixStart := time.Unix(start, 0) + return interval{start: &unixStart, stop: nil} +} + +func TestMergeIntervals(t *testing.T) { + for _, tc := range []struct { + name string + input []interval + expected []interval + }{ + { + name: "none", + input: nil, + expected: nil, + }, + { + name: "one", + input: []interval{ + mkinterval(0, 1), + }, + expected: []interval{ + mkinterval(0, 1), + }, + }, + { + name: "unstarted", + input: []interval{ + mkinterval(0, 1), + {nil, nil}, + }, + expected: []interval{ + mkinterval(0, 1), + }, + }, + { + name: "equal", + input: []interval{ + mkinterval(2, 4), + mkinterval(2, 4), + }, + expected: []interval{ + mkinterval(2, 4), + }, + }, + { + name: "no overlap", + input: []interval{ + mkinterval(0, 1), + mkinterval(2, 3), + mkinterval(7, 8), + }, + expected: []interval{ + mkinterval(0, 1), + mkinterval(2, 3), + mkinterval(7, 8), + }, + }, + { + name: "subsumed", + input: []interval{ + mkinterval(0, 10), + mkinterval(1, 2), + mkinterval(4, 9), + mkinterval(9, 10), + }, + expected: []interval{ + mkinterval(0, 10), + }, + }, + { + name: "partial overlaps", + input: []interval{ + mkinterval(0, 3), + mkinterval(2, 5), + mkinterval(4, 8), + mkinterval(10, 12), + mkinterval(11, 14), + }, + expected: []interval{ + mkinterval(0, 8), + mkinterval(10, 14), + }, + }, + { + name: "joined", + input: []interval{ + mkinterval(0, 2), + mkinterval(2, 4), + mkinterval(4, 6), + mkinterval(8, 10), + mkinterval(10, 12), + mkinterval(11, 12), + mkinterval(11, 14), + }, + expected: []interval{ + mkinterval(0, 6), + mkinterval(8, 14), + }, + }, + { + name: "open interval", + input: []interval{ + mkinterval(0, 5), + mkOpenInterval(6), + }, + expected: []interval{ + mkinterval(0, 5), + mkOpenInterval(6), + }, + }, + { + name: "open interval with overlaps", + input: []interval{ + mkOpenInterval(1), + mkinterval(3, 5), + }, + expected: []interval{ + mkOpenInterval(1), + }, + }, + { + name: "complex", + input: []interval{ + mkinterval(0, 2), + mkinterval(1, 4), + mkinterval(1, 4), + mkinterval(1, 5), + {nil, nil}, + mkinterval(6, 20), + mkinterval(8, 10), + mkinterval(8, 10), + mkinterval(9, 10), + mkinterval(12, 14), + mkinterval(19, 21), + mkinterval(30, 31), + mkinterval(32, 35), + {nil, nil}, + mkOpenInterval(33), + }, + expected: []interval{ + mkinterval(0, 5), + mkinterval(6, 21), + mkinterval(30, 31), + mkOpenInterval(32), + }, + }, + } { + t.Run(tc.name, func(t *testing.T) { + require.Equal(t, tc.expected, mergeIntervals(tc.input)) + }) + } +} From ae245ff349569381c91f8920af55fc0fdf217894 Mon Sep 17 00:00:00 2001 From: Erik Sipsma Date: Mon, 7 Feb 2022 15:53:57 -0800 Subject: [PATCH 5/5] solver: use unique vertex progress IDs. This fixes an issue where if a vertex rapidly had progress updates (such as the case where CacheMap runs in succession for multiple indexes), then progress updates were all using the same ID and could incorrectly override one another, resulting in missing vertex updates. Signed-off-by: Erik Sipsma --- solver/jobs.go | 35 ++++++++++++++++++++--------------- 1 file changed, 20 insertions(+), 15 deletions(-) diff --git a/solver/jobs.go b/solver/jobs.go index 83f7e367e6bb..ae11e053d47d 100644 --- a/solver/jobs.go +++ b/solver/jobs.go @@ -647,10 +647,10 @@ func (s *sharedOp) LoadCache(ctx context.Context, rec *CacheRecord) (Result, err } // no cache hit. start evaluating the node span, ctx := tracing.StartSpan(ctx, "load cache: "+s.st.vtx.Name()) - notifyStarted(ctx, &s.st.clientVertex, true) + notifyStarted(ctx, &s.st.clientVertex, true, "load-cache") res, err := s.Cache().Load(withAncestorCacheOpts(ctx, s.st), rec) tracing.FinishWithError(span, err) - notifyCompleted(ctx, &s.st.clientVertex, err, true) + notifyCompleted(ctx, &s.st.clientVertex, err, true, "load-cache") return res, err } @@ -662,7 +662,8 @@ func (s *sharedOp) CalcSlowCache(ctx context.Context, index Index, p PreprocessF err = errdefs.WithOp(err, s.st.vtx.Sys()) err = errdefs.WrapVertex(err, s.st.origDigest) }() - key, err := s.g.Do(ctx, fmt.Sprintf("slow-compute-%d", index), func(ctx context.Context) (interface{}, error) { + flightControlKey := fmt.Sprintf("slow-compute-%d", index) + key, err := s.g.Do(ctx, flightControlKey, func(ctx context.Context) (interface{}, error) { s.slowMu.Lock() // TODO: add helpers for these stored values if res, ok := s.slowCacheRes[index]; ok { @@ -726,8 +727,8 @@ func (s *sharedOp) CalcSlowCache(ctx context.Context, index Index, p PreprocessF if s.st.mspan.Span != nil { ctx = trace.ContextWithSpan(ctx, s.st.mspan) } - notifyStarted(ctx, &s.st.clientVertex, false) - notifyCompleted(ctx, &s.st.clientVertex, err, false) + notifyStarted(ctx, &s.st.clientVertex, false, flightControlKey) + notifyCompleted(ctx, &s.st.clientVertex, err, false, flightControlKey) return "", err } return key.(digest.Digest), nil @@ -742,7 +743,8 @@ func (s *sharedOp) CacheMap(ctx context.Context, index int) (resp *cacheMapResp, if err != nil { return nil, err } - res, err := s.g.Do(ctx, fmt.Sprintf("cachemap-%d", index), func(ctx context.Context) (ret interface{}, retErr error) { + flightControlKey := fmt.Sprintf("cachemap-%d", index) + res, err := s.g.Do(ctx, flightControlKey, func(ctx context.Context) (ret interface{}, retErr error) { if s.cacheRes != nil && s.cacheDone || index < len(s.cacheRes) { return s.cacheRes, nil } @@ -757,10 +759,10 @@ func (s *sharedOp) CacheMap(ctx context.Context, index int) (resp *cacheMapResp, if len(s.st.vtx.Inputs()) == 0 { // no cache hit. start evaluating the node span, ctx := tracing.StartSpan(ctx, "cache request: "+s.st.vtx.Name()) - notifyStarted(ctx, &s.st.clientVertex, false) + notifyStarted(ctx, &s.st.clientVertex, false, flightControlKey) defer func() { tracing.FinishWithError(span, retErr) - notifyCompleted(ctx, &s.st.clientVertex, retErr, false) + notifyCompleted(ctx, &s.st.clientVertex, retErr, false, flightControlKey) }() } res, done, err := op.CacheMap(ctx, s.st, len(s.cacheRes)) @@ -805,7 +807,8 @@ func (s *sharedOp) Exec(ctx context.Context, inputs []Result) (outputs []Result, if err != nil { return nil, nil, err } - res, err := s.g.Do(ctx, "exec", func(ctx context.Context) (ret interface{}, retErr error) { + flightControlKey := "exec" + res, err := s.g.Do(ctx, flightControlKey, func(ctx context.Context) (ret interface{}, retErr error) { if s.execRes != nil || s.execErr != nil { return s.execRes, s.execErr } @@ -823,10 +826,10 @@ func (s *sharedOp) Exec(ctx context.Context, inputs []Result) (outputs []Result, // no cache hit. start evaluating the node span, ctx := tracing.StartSpan(ctx, s.st.vtx.Name()) - notifyStarted(ctx, &s.st.clientVertex, false) + notifyStarted(ctx, &s.st.clientVertex, false, flightControlKey) defer func() { tracing.FinishWithError(span, retErr) - notifyCompleted(ctx, &s.st.clientVertex, retErr, false) + notifyCompleted(ctx, &s.st.clientVertex, retErr, false, flightControlKey) }() res, err := op.Exec(ctx, s.st, inputs) @@ -926,17 +929,18 @@ func (v *vertexWithCacheOptions) Inputs() []Edge { return v.inputs } -func notifyStarted(ctx context.Context, v *client.Vertex, cached bool) { +func notifyStarted(ctx context.Context, v *client.Vertex, cached bool, idSuffixes ...string) { pw, _, _ := progress.NewFromContext(ctx) defer pw.Close() now := time.Now() v.Started = &now v.Completed = nil v.Cached = cached - pw.Write(v.Digest.String(), *v) + id := v.Digest.String() + strings.Join(idSuffixes, "-") + pw.Write(id, *v) } -func notifyCompleted(ctx context.Context, v *client.Vertex, err error, cached bool) { +func notifyCompleted(ctx context.Context, v *client.Vertex, err error, cached bool, idSuffixes ...string) { pw, _, _ := progress.NewFromContext(ctx) defer pw.Close() now := time.Now() @@ -950,7 +954,8 @@ func notifyCompleted(ctx context.Context, v *client.Vertex, err error, cached bo } else { v.Error = "" } - pw.Write(v.Digest.String(), *v) + id := v.Digest.String() + strings.Join(idSuffixes, "-") + pw.Write(id, *v) } type SlowCacheError struct {