From 9c5816a317607b9da89fbb9c7ff94e63bf6045b8 Mon Sep 17 00:00:00 2001 From: Justin Chadwell Date: Wed, 9 Aug 2023 12:09:41 +0100 Subject: [PATCH 01/12] chore: remove function indirection for cache exporters Signed-off-by: Justin Chadwell --- solver/llbsolver/solver.go | 55 +++++++++++++++++++------------------- 1 file changed, 27 insertions(+), 28 deletions(-) diff --git a/solver/llbsolver/solver.go b/solver/llbsolver/solver.go index 4f6ab93c981f..b3b367bb6f0f 100644 --- a/solver/llbsolver/solver.go +++ b/solver/llbsolver/solver.go @@ -602,37 +602,36 @@ func runCacheExporters(ctx context.Context, exporters []RemoteCacheExporter, j * var cacheExporterResponse map[string]string resps := make([]map[string]string, len(exporters)) for i, exp := range exporters { - func(exp RemoteCacheExporter, i int) { - eg.Go(func() (err error) { - id := fmt.Sprint(j.SessionID, "-cache-", i) - err = inBuilderContext(ctx, j, exp.Exporter.Name(), id, func(ctx context.Context, _ session.Group) error { - prepareDone := progress.OneOff(ctx, "preparing build cache for export") - if err := result.EachRef(cached, inp, func(res solver.CachedResult, ref cache.ImmutableRef) error { - ctx = withDescHandlerCacheOpts(ctx, ref) - - // Configure compression - compressionConfig := exp.Config().Compression - - // all keys have same export chain so exporting others is not needed - _, err = res.CacheKeys()[0].Exporter.ExportTo(ctx, exp, solver.CacheExportOpt{ - ResolveRemotes: workerRefResolver(cacheconfig.RefConfig{Compression: compressionConfig}, false, g), - Mode: exp.CacheExportMode, - Session: g, - CompressionOpt: &compressionConfig, - }) - return err - }); err != nil { - return prepareDone(err) - } - resps[i], err = exp.Finalize(ctx) + i, exp := i, exp + eg.Go(func() (err error) { + id := fmt.Sprint(j.SessionID, "-cache-", i) + err = inBuilderContext(ctx, j, exp.Exporter.Name(), id, func(ctx context.Context, _ session.Group) error { + prepareDone := progress.OneOff(ctx, "preparing build cache for export") + if err := result.EachRef(cached, inp, func(res solver.CachedResult, ref cache.ImmutableRef) error { + ctx = withDescHandlerCacheOpts(ctx, ref) + + // Configure compression + compressionConfig := exp.Config().Compression + + // all keys have same export chain so exporting others is not needed + _, err = res.CacheKeys()[0].Exporter.ExportTo(ctx, exp, solver.CacheExportOpt{ + ResolveRemotes: workerRefResolver(cacheconfig.RefConfig{Compression: compressionConfig}, false, g), + Mode: exp.CacheExportMode, + Session: g, + CompressionOpt: &compressionConfig, + }) + return err + }); err != nil { return prepareDone(err) - }) - if exp.IgnoreError { - err = nil } - return err + resps[i], err = exp.Finalize(ctx) + return prepareDone(err) }) - }(exp, i) + if exp.IgnoreError { + err = nil + } + return err + }) } if err := eg.Wait(); err != nil { return nil, err From 578e8105c344cc2b58a1f871c2f9a098a91d8d35 Mon Sep 17 00:00:00 2001 From: Justin Chadwell Date: Wed, 9 Aug 2023 12:11:56 +0100 Subject: [PATCH 02/12] filesync: add doc comments in proto Signed-off-by: Justin Chadwell --- session/filesync/filesync.proto | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/session/filesync/filesync.proto b/session/filesync/filesync.proto index e0724d6f0fab..68162d8a38f4 100644 --- a/session/filesync/filesync.proto +++ b/session/filesync/filesync.proto @@ -6,16 +6,18 @@ option go_package = "filesync"; import "github.com/tonistiigi/fsutil/types/wire.proto"; +// FileSync exposes local files from the client to the server. service FileSync{ rpc DiffCopy(stream fsutil.types.Packet) returns (stream fsutil.types.Packet); rpc TarStream(stream fsutil.types.Packet) returns (stream fsutil.types.Packet); } +// FileSend allows sending files from the server back to the client. service FileSend{ rpc DiffCopy(stream BytesMessage) returns (stream BytesMessage); } // BytesMessage contains a chunk of byte data -message BytesMessage{ +message BytesMessage { bytes data = 1; } From da231fb56ecaf8022d353b751aa6ae847f41fb19 Mon Sep 17 00:00:00 2001 From: Justin Chadwell Date: Wed, 9 Aug 2023 12:14:38 +0100 Subject: [PATCH 03/12] exporter: avoid modifying exporter inputs Co-authored-by: fahed dorgaa Co-authored-by: a-palchikov Signed-off-by: Justin Chadwell --- exporter/containerimage/export.go | 1 + exporter/oci/export.go | 1 + solver/result/result.go | 10 ++++++++++ 3 files changed, 12 insertions(+) diff --git a/exporter/containerimage/export.go b/exporter/containerimage/export.go index 3f8865f8e830..9a0b30ba409a 100644 --- a/exporter/containerimage/export.go +++ b/exporter/containerimage/export.go @@ -188,6 +188,7 @@ func (e *imageExporterInstance) Config() *exporter.Config { } func (e *imageExporterInstance) Export(ctx context.Context, src *exporter.Source, sessionID string) (_ map[string]string, descref exporter.DescriptorReference, err error) { + src = src.Clone() if src.Metadata == nil { src.Metadata = make(map[string][]byte) } diff --git a/exporter/oci/export.go b/exporter/oci/export.go index f1a1aa55c83b..b6124172a7bf 100644 --- a/exporter/oci/export.go +++ b/exporter/oci/export.go @@ -117,6 +117,7 @@ func (e *imageExporterInstance) Export(ctx context.Context, src *exporter.Source return nil, nil, errors.Errorf("docker exporter does not currently support exporting manifest lists") } + src = src.Clone() if src.Metadata == nil { src.Metadata = make(map[string][]byte) } diff --git a/solver/result/result.go b/solver/result/result.go index cfcfe9dcbd3e..644ec5d0fd53 100644 --- a/solver/result/result.go +++ b/solver/result/result.go @@ -1,6 +1,7 @@ package result import ( + "maps" "sync" "github.com/pkg/errors" @@ -14,6 +15,15 @@ type Result[T comparable] struct { Attestations map[string][]Attestation[T] } +func (r *Result[T]) Clone() *Result[T] { + return &Result[T]{ + Ref: r.Ref, + Refs: maps.Clone(r.Refs), + Metadata: maps.Clone(r.Metadata), + Attestations: maps.Clone(r.Attestations), + } +} + func (r *Result[T]) AddMeta(k string, v []byte) { r.mu.Lock() if r.Metadata == nil { From a80b48544c029f0e28e1243407b55c916c7757f2 Mon Sep 17 00:00:00 2001 From: Justin Chadwell Date: Wed, 9 Aug 2023 12:20:22 +0100 Subject: [PATCH 04/12] session: create helper type for exporter file output Co-authored-by: a-palchikov Signed-off-by: Justin Chadwell --- client/client_test.go | 3 ++- client/solve.go | 4 ++-- cmd/buildctl/build/output.go | 3 ++- frontend/dockerfile/dockerfile_test.go | 3 ++- session/filesync/filesync.go | 11 +++++++++-- 5 files changed, 17 insertions(+), 7 deletions(-) diff --git a/client/client_test.go b/client/client_test.go index 7951bd709765..5eab27b7ec81 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -46,6 +46,7 @@ import ( gatewaypb "github.com/moby/buildkit/frontend/gateway/pb" "github.com/moby/buildkit/identity" "github.com/moby/buildkit/session" + "github.com/moby/buildkit/session/filesync" "github.com/moby/buildkit/session/secrets/secretsprovider" "github.com/moby/buildkit/session/sshforward/sshprovider" "github.com/moby/buildkit/solver/errdefs" @@ -9659,7 +9660,7 @@ var hostNetwork integration.ConfigUpdater = &netModeHost{} var defaultNetwork integration.ConfigUpdater = &netModeDefault{} var bridgeDNSNetwork integration.ConfigUpdater = &netModeBridgeDNS{} -func fixedWriteCloser(wc io.WriteCloser) func(map[string]string) (io.WriteCloser, error) { +func fixedWriteCloser(wc io.WriteCloser) filesync.FileOutputFunc { return func(map[string]string) (io.WriteCloser, error) { return wc, nil } diff --git a/client/solve.go b/client/solve.go index 1c6f1489ed9b..41eb4af9a8b3 100644 --- a/client/solve.go +++ b/client/solve.go @@ -56,8 +56,8 @@ type SolveOpt struct { type ExportEntry struct { Type string Attrs map[string]string - Output func(map[string]string) (io.WriteCloser, error) // for ExporterOCI and ExporterDocker - OutputDir string // for ExporterLocal + Output filesync.FileOutputFunc // for ExporterOCI and ExporterDocker + OutputDir string // for ExporterLocal } type CacheOptionsEntry struct { diff --git a/cmd/buildctl/build/output.go b/cmd/buildctl/build/output.go index e22a6e034936..c5547583c849 100644 --- a/cmd/buildctl/build/output.go +++ b/cmd/buildctl/build/output.go @@ -9,6 +9,7 @@ import ( "github.com/containerd/console" "github.com/moby/buildkit/client" + "github.com/moby/buildkit/session/filesync" "github.com/pkg/errors" ) @@ -66,7 +67,7 @@ func ParseOutput(exports []string) ([]client.ExportEntry, error) { } // resolveExporterDest returns at most either one of io.WriteCloser (single file) or a string (directory path). -func resolveExporterDest(exporter, dest string, attrs map[string]string) (func(map[string]string) (io.WriteCloser, error), string, error) { +func resolveExporterDest(exporter, dest string, attrs map[string]string) (filesync.FileOutputFunc, string, error) { wrapWriter := func(wc io.WriteCloser) func(map[string]string) (io.WriteCloser, error) { return func(m map[string]string) (io.WriteCloser, error) { return wc, nil diff --git a/frontend/dockerfile/dockerfile_test.go b/frontend/dockerfile/dockerfile_test.go index 1ed794e10a8f..db73ad5741e1 100644 --- a/frontend/dockerfile/dockerfile_test.go +++ b/frontend/dockerfile/dockerfile_test.go @@ -42,6 +42,7 @@ import ( "github.com/moby/buildkit/frontend/subrequests" "github.com/moby/buildkit/identity" "github.com/moby/buildkit/session" + "github.com/moby/buildkit/session/filesync" "github.com/moby/buildkit/session/upload/uploadprovider" "github.com/moby/buildkit/solver/errdefs" "github.com/moby/buildkit/solver/pb" @@ -7197,7 +7198,7 @@ func (*networkModeSandbox) UpdateConfigFile(in string) string { var networkHostGranted integration.ConfigUpdater = &networkModeHost{} var networkHostDenied integration.ConfigUpdater = &networkModeSandbox{} -func fixedWriteCloser(wc io.WriteCloser) func(map[string]string) (io.WriteCloser, error) { +func fixedWriteCloser(wc io.WriteCloser) filesync.FileOutputFunc { return func(map[string]string) (io.WriteCloser, error) { return wc, nil } diff --git a/session/filesync/filesync.go b/session/filesync/filesync.go index f05c475f6dcf..e664251b1aee 100644 --- a/session/filesync/filesync.go +++ b/session/filesync/filesync.go @@ -35,6 +35,13 @@ type fsSyncProvider struct { doneCh chan error } +type FileOutputFunc func(map[string]string) (io.WriteCloser, error) + +type SyncedDir struct { + Dir string + Map func(string, *fstypes.Stat) fsutil.MapResult +} + type DirSource interface { LookupDir(string) (fsutil.FS, bool) } @@ -235,7 +242,7 @@ func NewFSSyncTargetDir(outdir string) session.Attachable { } // NewFSSyncTarget allows writing into an io.WriteCloser -func NewFSSyncTarget(f func(map[string]string) (io.WriteCloser, error)) session.Attachable { +func NewFSSyncTarget(f FileOutputFunc) session.Attachable { p := &fsSyncTarget{ f: f, } @@ -244,7 +251,7 @@ func NewFSSyncTarget(f func(map[string]string) (io.WriteCloser, error)) session. type fsSyncTarget struct { outdir string - f func(map[string]string) (io.WriteCloser, error) + f FileOutputFunc } func (sp *fsSyncTarget) Register(server *grpc.Server) { From d970f8638ccc894dd06e4b44c7f3ca0d803c12c0 Mon Sep 17 00:00:00 2001 From: Justin Chadwell Date: Wed, 9 Aug 2023 12:29:43 +0100 Subject: [PATCH 05/12] exporter: make ParseKey platform parameter optional Signed-off-by: Justin Chadwell --- exporter/containerimage/exptypes/parse.go | 11 +++++++---- exporter/containerimage/writer.go | 8 ++++---- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/exporter/containerimage/exptypes/parse.go b/exporter/containerimage/exptypes/parse.go index f77cd3f52565..293a24ed0772 100644 --- a/exporter/containerimage/exptypes/parse.go +++ b/exporter/containerimage/exptypes/parse.go @@ -46,10 +46,13 @@ func ParsePlatforms(meta map[string][]byte) (Platforms, error) { return ps, nil } -func ParseKey(meta map[string][]byte, key string, p Platform) []byte { - if v, ok := meta[fmt.Sprintf("%s/%s", key, p.ID)]; ok { - return v - } else if v, ok := meta[key]; ok { +func ParseKey(meta map[string][]byte, key string, p *Platform) []byte { + if p != nil { + if v, ok := meta[fmt.Sprintf("%s/%s", key, p.ID)]; ok { + return v + } + } + if v, ok := meta[key]; ok { return v } return nil diff --git a/exporter/containerimage/writer.go b/exporter/containerimage/writer.go index a215b1affe5f..149a27edc51c 100644 --- a/exporter/containerimage/writer.go +++ b/exporter/containerimage/writer.go @@ -114,9 +114,9 @@ func (ic *ImageWriter) Commit(ctx context.Context, inp *exporter.Source, session } var ref cache.ImmutableRef - var p exptypes.Platform + var p *exptypes.Platform if len(ps.Platforms) > 0 { - p = ps.Platforms[0] + p = &ps.Platforms[0] if r, ok := inp.FindRef(p.ID); ok { ref = r } @@ -199,8 +199,8 @@ func (ic *ImageWriter) Commit(ctx context.Context, inp *exporter.Source, session if !ok { return nil, errors.Errorf("failed to find ref for ID %s", p.ID) } - config := exptypes.ParseKey(inp.Metadata, exptypes.ExporterImageConfigKey, p) - inlineCache := exptypes.ParseKey(inp.Metadata, exptypes.ExporterInlineCache, p) + config := exptypes.ParseKey(inp.Metadata, exptypes.ExporterImageConfigKey, &p) + inlineCache := exptypes.ParseKey(inp.Metadata, exptypes.ExporterInlineCache, &p) remote := &remotes[remotesMap[p.ID]] if remote == nil { From 4c93f9d9bde9adbbb172d1ff98db54da5612206a Mon Sep 17 00:00:00 2001 From: Justin Chadwell Date: Wed, 9 Aug 2023 12:41:53 +0100 Subject: [PATCH 06/12] exporter: pass inline cache directly to exporter This preps for the case where a single exporter source does not map neatly onto a single inline cache - such as introducing multiple exporters. We also introduce laziness here - each exporter chooses to attempt extracting the inline cache on demand, which ensures that we avoid creating inline caches for exporters that do not support them. Co-authored-by: a-palchikov Co-authored-by: fahed dorgaa Signed-off-by: Justin Chadwell --- exporter/containerimage/export.go | 4 +- exporter/containerimage/exptypes/types.go | 10 ++- exporter/containerimage/writer.go | 51 +++++++++---- exporter/exporter.go | 3 +- exporter/local/export.go | 2 +- exporter/oci/export.go | 4 +- exporter/tar/export.go | 2 +- solver/llbsolver/solver.go | 88 ++++++++++++----------- 8 files changed, 99 insertions(+), 65 deletions(-) diff --git a/exporter/containerimage/export.go b/exporter/containerimage/export.go index 9a0b30ba409a..7625e1d7fb7d 100644 --- a/exporter/containerimage/export.go +++ b/exporter/containerimage/export.go @@ -187,7 +187,7 @@ func (e *imageExporterInstance) Config() *exporter.Config { return exporter.NewConfigWithCompression(e.opts.RefCfg.Compression) } -func (e *imageExporterInstance) Export(ctx context.Context, src *exporter.Source, sessionID string) (_ map[string]string, descref exporter.DescriptorReference, err error) { +func (e *imageExporterInstance) Export(ctx context.Context, src *exporter.Source, inlineCache exptypes.InlineCache, sessionID string) (_ map[string]string, descref exporter.DescriptorReference, err error) { src = src.Clone() if src.Metadata == nil { src.Metadata = make(map[string][]byte) @@ -213,7 +213,7 @@ func (e *imageExporterInstance) Export(ctx context.Context, src *exporter.Source } }() - desc, err := e.opt.ImageWriter.Commit(ctx, src, sessionID, &opts) + desc, err := e.opt.ImageWriter.Commit(ctx, src, sessionID, inlineCache, &opts) if err != nil { return nil, nil, err } diff --git a/exporter/containerimage/exptypes/types.go b/exporter/containerimage/exptypes/types.go index c4d5721ea65d..74465c858304 100644 --- a/exporter/containerimage/exptypes/types.go +++ b/exporter/containerimage/exptypes/types.go @@ -1,6 +1,9 @@ package exptypes import ( + "context" + + "github.com/moby/buildkit/solver/result" ocispecs "github.com/opencontainers/image-spec/specs-go/v1" ) @@ -10,7 +13,6 @@ const ( ExporterImageConfigKey = "containerimage.config" ExporterImageConfigDigestKey = "containerimage.config.digest" ExporterImageDescriptorKey = "containerimage.descriptor" - ExporterInlineCache = "containerimage.inlinecache" ExporterPlatformsKey = "refs.platforms" ) @@ -18,7 +20,6 @@ const ( // a platform to become platform specific var KnownRefMetadataKeys = []string{ ExporterImageConfigKey, - ExporterInlineCache, } type Platforms struct { @@ -29,3 +30,8 @@ type Platform struct { ID string Platform ocispecs.Platform } + +type InlineCacheEntry struct { + Data []byte +} +type InlineCache func(ctx context.Context) (*result.Result[*InlineCacheEntry], error) diff --git a/exporter/containerimage/writer.go b/exporter/containerimage/writer.go index 149a27edc51c..089c11798854 100644 --- a/exporter/containerimage/writer.go +++ b/exporter/containerimage/writer.go @@ -59,7 +59,7 @@ type ImageWriter struct { opt WriterOpt } -func (ic *ImageWriter) Commit(ctx context.Context, inp *exporter.Source, sessionID string, opts *ImageCommitOpts) (*ocispecs.Descriptor, error) { +func (ic *ImageWriter) Commit(ctx context.Context, inp *exporter.Source, sessionID string, inlineCache exptypes.InlineCache, opts *ImageCommitOpts) (*ocispecs.Descriptor, error) { if _, ok := inp.Metadata[exptypes.ExporterPlatformsKey]; len(inp.Refs) > 0 && !ok { return nil, errors.Errorf("unable to export multiple refs, missing platforms mapping") } @@ -123,27 +123,41 @@ func (ic *ImageWriter) Commit(ctx context.Context, inp *exporter.Source, session } else { ref = inp.Ref } + config := exptypes.ParseKey(inp.Metadata, exptypes.ExporterImageConfigKey, p) remotes, err := ic.exportLayers(ctx, opts.RefCfg, session.NewGroup(sessionID), ref) if err != nil { return nil, err } + remote := &remotes[0] + if opts.RewriteTimestamp { + remote, err = ic.rewriteRemoteWithEpoch(ctx, opts, remote) + if err != nil { + return nil, err + } + } annotations := opts.Annotations.Platform(nil) if len(annotations.Index) > 0 || len(annotations.IndexDescriptor) > 0 { return nil, errors.Errorf("index annotations not supported for single platform export") } - config := exptypes.ParseKey(inp.Metadata, exptypes.ExporterImageConfigKey, p) - inlineCache := exptypes.ParseKey(inp.Metadata, exptypes.ExporterInlineCache, p) - remote := &remotes[0] - if opts.RewriteTimestamp { - remote, err = ic.rewriteRemoteWithEpoch(ctx, opts, remote) + var inlineCacheEntry *exptypes.InlineCacheEntry + if inlineCache != nil { + inlineCacheResult, err := inlineCache(ctx) if err != nil { return nil, err } + if inlineCacheResult != nil { + if p != nil { + inlineCacheEntry, _ = inlineCacheResult.FindRef(p.ID) + } else { + inlineCacheEntry = inlineCacheResult.Ref + } + } } - mfstDesc, configDesc, err := ic.commitDistributionManifest(ctx, opts, ref, config, remote, annotations, inlineCache, opts.Epoch, session.NewGroup(sessionID)) + + mfstDesc, configDesc, err := ic.commitDistributionManifest(ctx, opts, ref, config, remote, annotations, inlineCacheEntry, opts.Epoch, session.NewGroup(sessionID)) if err != nil { return nil, err } @@ -178,6 +192,14 @@ func (ic *ImageWriter) Commit(ctx context.Context, inp *exporter.Source, session return nil, err } + var inlineCacheResult *result.Result[*exptypes.InlineCacheEntry] + if inlineCache != nil { + inlineCacheResult, err = inlineCache(ctx) + if err != nil { + return nil, err + } + } + idx := ocispecs.Index{ MediaType: ocispecs.MediaTypeImageIndex, Annotations: opts.Annotations.Platform(nil).Index, @@ -200,7 +222,6 @@ func (ic *ImageWriter) Commit(ctx context.Context, inp *exporter.Source, session return nil, errors.Errorf("failed to find ref for ID %s", p.ID) } config := exptypes.ParseKey(inp.Metadata, exptypes.ExporterImageConfigKey, &p) - inlineCache := exptypes.ParseKey(inp.Metadata, exptypes.ExporterInlineCache, &p) remote := &remotes[remotesMap[p.ID]] if remote == nil { @@ -208,7 +229,6 @@ func (ic *ImageWriter) Commit(ctx context.Context, inp *exporter.Source, session Provider: ic.opt.ContentStore, } } - if opts.RewriteTimestamp { remote, err = ic.rewriteRemoteWithEpoch(ctx, opts, remote) if err != nil { @@ -216,7 +236,12 @@ func (ic *ImageWriter) Commit(ctx context.Context, inp *exporter.Source, session } } - desc, _, err := ic.commitDistributionManifest(ctx, opts, r, config, remote, opts.Annotations.Platform(&p.Platform), inlineCache, opts.Epoch, session.NewGroup(sessionID)) + var inlineCacheEntry *exptypes.InlineCacheEntry + if inlineCacheResult != nil { + inlineCacheEntry, _ = inlineCacheResult.FindRef(p.ID) + } + + desc, _, err := ic.commitDistributionManifest(ctx, opts, r, config, remote, opts.Annotations.Platform(&p.Platform), inlineCacheEntry, opts.Epoch, session.NewGroup(sessionID)) if err != nil { return nil, err } @@ -386,7 +411,7 @@ func (ic *ImageWriter) rewriteRemoteWithEpoch(ctx context.Context, opts *ImageCo }, nil } -func (ic *ImageWriter) commitDistributionManifest(ctx context.Context, opts *ImageCommitOpts, ref cache.ImmutableRef, config []byte, remote *solver.Remote, annotations *Annotations, inlineCache []byte, epoch *time.Time, sg session.Group) (*ocispecs.Descriptor, *ocispecs.Descriptor, error) { +func (ic *ImageWriter) commitDistributionManifest(ctx context.Context, opts *ImageCommitOpts, ref cache.ImmutableRef, config []byte, remote *solver.Remote, annotations *Annotations, inlineCache *exptypes.InlineCacheEntry, epoch *time.Time, sg session.Group) (*ocispecs.Descriptor, *ocispecs.Descriptor, error) { if len(config) == 0 { var err error config, err = defaultImageConfig() @@ -633,7 +658,7 @@ func parseHistoryFromConfig(dt []byte) ([]ocispecs.History, error) { return config.History, nil } -func patchImageConfig(dt []byte, descs []ocispecs.Descriptor, history []ocispecs.History, cache []byte, epoch *time.Time) ([]byte, error) { +func patchImageConfig(dt []byte, descs []ocispecs.Descriptor, history []ocispecs.History, cache *exptypes.InlineCacheEntry, epoch *time.Time) ([]byte, error) { m := map[string]json.RawMessage{} if err := json.Unmarshal(dt, &m); err != nil { return nil, errors.Wrap(err, "failed to parse image config for patch") @@ -694,7 +719,7 @@ func patchImageConfig(dt []byte, descs []ocispecs.Descriptor, history []ocispecs } if cache != nil { - dt, err := json.Marshal(cache) + dt, err := json.Marshal(cache.Data) if err != nil { return nil, err } diff --git a/exporter/exporter.go b/exporter/exporter.go index 0e7d8d14f280..937c5d313221 100644 --- a/exporter/exporter.go +++ b/exporter/exporter.go @@ -4,6 +4,7 @@ import ( "context" "github.com/moby/buildkit/cache" + "github.com/moby/buildkit/exporter/containerimage/exptypes" "github.com/moby/buildkit/solver/result" "github.com/moby/buildkit/util/compression" ocispecs "github.com/opencontainers/image-spec/specs-go/v1" @@ -20,7 +21,7 @@ type Exporter interface { type ExporterInstance interface { Name() string Config() *Config - Export(ctx context.Context, src *Source, sessionID string) (map[string]string, DescriptorReference, error) + Export(ctx context.Context, src *Source, inlineCache exptypes.InlineCache, sessionID string) (map[string]string, DescriptorReference, error) } type DescriptorReference interface { diff --git a/exporter/local/export.go b/exporter/local/export.go index 96d8352bb04f..4c8a0c5b564c 100644 --- a/exporter/local/export.go +++ b/exporter/local/export.go @@ -60,7 +60,7 @@ func (e *localExporter) Config() *exporter.Config { return exporter.NewConfig() } -func (e *localExporterInstance) Export(ctx context.Context, inp *exporter.Source, sessionID string) (map[string]string, exporter.DescriptorReference, error) { +func (e *localExporterInstance) Export(ctx context.Context, inp *exporter.Source, _ exptypes.InlineCache, sessionID string) (map[string]string, exporter.DescriptorReference, error) { timeoutCtx, cancel := context.WithCancelCause(ctx) timeoutCtx, _ = context.WithTimeoutCause(timeoutCtx, 5*time.Second, errors.WithStack(context.DeadlineExceeded)) defer cancel(errors.WithStack(context.Canceled)) diff --git a/exporter/oci/export.go b/exporter/oci/export.go index b6124172a7bf..55e606c4d89c 100644 --- a/exporter/oci/export.go +++ b/exporter/oci/export.go @@ -112,7 +112,7 @@ func (e *imageExporterInstance) Config() *exporter.Config { return exporter.NewConfigWithCompression(e.opts.RefCfg.Compression) } -func (e *imageExporterInstance) Export(ctx context.Context, src *exporter.Source, sessionID string) (_ map[string]string, descref exporter.DescriptorReference, err error) { +func (e *imageExporterInstance) Export(ctx context.Context, src *exporter.Source, inlineCache exptypes.InlineCache, sessionID string) (_ map[string]string, descref exporter.DescriptorReference, err error) { if e.opt.Variant == VariantDocker && len(src.Refs) > 0 { return nil, nil, errors.Errorf("docker exporter does not currently support exporting manifest lists") } @@ -142,7 +142,7 @@ func (e *imageExporterInstance) Export(ctx context.Context, src *exporter.Source } }() - desc, err := e.opt.ImageWriter.Commit(ctx, src, sessionID, &opts) + desc, err := e.opt.ImageWriter.Commit(ctx, src, sessionID, inlineCache, &opts) if err != nil { return nil, nil, err } diff --git a/exporter/tar/export.go b/exporter/tar/export.go index c932000e594b..50703eb649bb 100644 --- a/exporter/tar/export.go +++ b/exporter/tar/export.go @@ -57,7 +57,7 @@ func (e *localExporterInstance) Config() *exporter.Config { return exporter.NewConfig() } -func (e *localExporterInstance) Export(ctx context.Context, inp *exporter.Source, sessionID string) (map[string]string, exporter.DescriptorReference, error) { +func (e *localExporterInstance) Export(ctx context.Context, inp *exporter.Source, _ exptypes.InlineCache, sessionID string) (map[string]string, exporter.DescriptorReference, error) { var defers []func() error defer func() { diff --git a/solver/llbsolver/solver.go b/solver/llbsolver/solver.go index b3b367bb6f0f..d4c93f1610df 100644 --- a/solver/llbsolver/solver.go +++ b/solver/llbsolver/solver.go @@ -554,21 +554,9 @@ func (s *Solver) Solve(ctx context.Context, id string, sessionID string, req fro cacheExporters, inlineCacheExporter := splitCacheExporters(exp.CacheExporters) var exporterResponse map[string]string - if e := exp.Exporter; e != nil { - meta, err := runInlineCacheExporter(ctx, e, inlineCacheExporter, j, cached) - if err != nil { - return nil, err - } - for k, v := range meta { - inp.AddMeta(k, v) - } - - if err := inBuilderContext(ctx, j, e.Name(), j.SessionID+"-export", func(ctx context.Context, _ session.Group) error { - exporterResponse, descref, err = e.Export(ctx, inp, j.SessionID) - return err - }); err != nil { - return nil, err - } + exporterResponse, descref, err = s.runExporter(ctx, exp.Exporter, inlineCacheExporter, j, cached, inp) + if err != nil { + return nil, err } cacheExporterResponse, err := runCacheExporters(ctx, cacheExporters, j, cached, inp) @@ -647,42 +635,59 @@ func runCacheExporters(ctx context.Context, exporters []RemoteCacheExporter, j * return cacheExporterResponse, nil } -func runInlineCacheExporter(ctx context.Context, e exporter.ExporterInstance, inlineExporter *RemoteCacheExporter, j *solver.Job, cached *result.Result[solver.CachedResult]) (map[string][]byte, error) { - meta := map[string][]byte{} +func runInlineCacheExporter(ctx context.Context, e exporter.ExporterInstance, inlineExporter inlineCacheExporter, j *solver.Job, cached *result.Result[solver.CachedResult]) (*result.Result[*exptypes.InlineCacheEntry], error) { if inlineExporter == nil { return nil, nil } + + var res *result.Result[*exptypes.InlineCacheEntry] if err := inBuilderContext(ctx, j, "preparing layers for inline cache", j.SessionID+"-cache-inline", func(ctx context.Context, _ session.Group) error { - if res := cached.Ref; res != nil { - dtic, err := inlineCache(ctx, inlineExporter.Exporter, res, e.Config().Compression(), session.NewGroup(j.SessionID)) + var err error + res, err = result.ConvertResult(cached, func(res solver.CachedResult) (*exptypes.InlineCacheEntry, error) { + dtic, err := inlineCache(ctx, inlineExporter, res, e.Config().Compression(), session.NewGroup(j.SessionID)) if err != nil { - return err + return nil, err } - if dtic != nil { - meta[exptypes.ExporterInlineCache] = dtic + if dtic == nil { + return nil, nil } + return &exptypes.InlineCacheEntry{Data: dtic}, nil + }) + return err + }); err != nil { + return nil, err + } + return res, nil +} + +func (s *Solver) runExporter(ctx context.Context, exp exporter.ExporterInstance, inlineCacheExporter inlineCacheExporter, job *solver.Job, cached *result.Result[solver.CachedResult], inp *result.Result[cache.ImmutableRef]) (exporterResponse map[string]string, descref exporter.DescriptorReference, err error) { + sessionID := job.SessionID + inlineCache := exptypes.InlineCache(func(ctx context.Context) (*result.Result[*exptypes.InlineCacheEntry], error) { + return runInlineCacheExporter(ctx, exp, inlineCacheExporter, job, cached) + }) + + err = inBuilderContext(ctx, job, exp.Name(), job.SessionID+"-export", func(ctx context.Context, _ session.Group) error { + var dref exporter.DescriptorReference + exporterResponse, dref, err = exp.Export(ctx, inp, inlineCache, sessionID) + if err != nil { + return err } - for k, res := range cached.Refs { - dtic, err := inlineCache(ctx, inlineExporter.Exporter, res, e.Config().Compression(), session.NewGroup(j.SessionID)) - if err != nil { - return err - } - if dtic != nil { - meta[fmt.Sprintf("%s/%s", exptypes.ExporterInlineCache, k)] = dtic - } + if dref != nil { + descref = dref } return nil - }); err != nil { - return nil, err + }) + if err != nil { + return nil, nil, err } - return meta, nil + return exporterResponse, descref, nil } -func splitCacheExporters(exporters []RemoteCacheExporter) (rest []RemoteCacheExporter, inline *RemoteCacheExporter) { +func splitCacheExporters(exporters []RemoteCacheExporter) (rest []RemoteCacheExporter, inline inlineCacheExporter) { rest = make([]RemoteCacheExporter, 0, len(exporters)) - for i, exp := range exporters { - if _, ok := asInlineCache(exp.Exporter); ok { - inline = &exporters[i] + for _, exp := range exporters { + if ic, ok := asInlineCache(exp.Exporter); ok { + inline = ic continue } rest = append(rest, exp) @@ -820,6 +825,7 @@ func getProvenance(ref solver.ResultProxy, br *provenanceBridge, id string, reqs } type inlineCacheExporter interface { + solver.CacheExporterTarget ExportForLayers(context.Context, []digest.Digest) ([]byte, error) } @@ -828,11 +834,7 @@ func asInlineCache(e remotecache.Exporter) (inlineCacheExporter, bool) { return ie, ok } -func inlineCache(ctx context.Context, e remotecache.Exporter, res solver.CachedResult, compressionopt compression.Config, g session.Group) ([]byte, error) { - ie, ok := asInlineCache(e) - if !ok { - return nil, nil - } +func inlineCache(ctx context.Context, ie inlineCacheExporter, res solver.CachedResult, compressionopt compression.Config, g session.Group) ([]byte, error) { workerRef, ok := res.Sys().(*worker.WorkerRef) if !ok { return nil, errors.Errorf("invalid reference: %T", res.Sys()) @@ -851,7 +853,7 @@ func inlineCache(ctx context.Context, e remotecache.Exporter, res solver.CachedR ctx = withDescHandlerCacheOpts(ctx, workerRef.ImmutableRef) refCfg := cacheconfig.RefConfig{Compression: compressionopt} - if _, err := res.CacheKeys()[0].Exporter.ExportTo(ctx, e, solver.CacheExportOpt{ + if _, err := res.CacheKeys()[0].Exporter.ExportTo(ctx, ie, solver.CacheExportOpt{ ResolveRemotes: workerRefResolver(refCfg, true, g), // load as many compression blobs as possible Mode: solver.CacheExportModeMin, Session: g, From 81b4762291a6acccca5b99cd517999bac5606cc2 Mon Sep 17 00:00:00 2001 From: Justin Chadwell Date: Wed, 9 Aug 2023 16:33:37 +0100 Subject: [PATCH 07/12] session: add file send multiplexing This patch adds multi-plexing to the local file transfer protocol (from server to client). This is implementation-wise similar to the multiplexing from the containerd content store transfer protocol, using a GRPC header to select the appropriate target. Signed-off-by: Justin Chadwell --- client/solve.go | 4 +- exporter/local/export.go | 2 +- exporter/oci/export.go | 2 +- exporter/tar/export.go | 2 +- session/filesync/filesync.go | 106 +++++++++++++++++++++++++++-------- 5 files changed, 89 insertions(+), 27 deletions(-) diff --git a/client/solve.go b/client/solve.go index 41eb4af9a8b3..be214e55fdd6 100644 --- a/client/solve.go +++ b/client/solve.go @@ -187,7 +187,7 @@ func (c *Client) solve(ctx context.Context, def *llb.Definition, runGateway runG if ex.Output == nil { return nil, errors.Errorf("output file writer is required for %s exporter", ex.Type) } - s.Allow(filesync.NewFSSyncTarget(ex.Output)) + s.Allow(filesync.NewFSSyncTarget(filesync.WithFSSync("", ex.Output))) } if supportDir { if ex.OutputDir == "" { @@ -205,7 +205,7 @@ func (c *Client) solve(ctx context.Context, def *llb.Definition, runGateway runG contentStores["export"] = cs storesToUpdate = append(storesToUpdate, ex.OutputDir) default: - s.Allow(filesync.NewFSSyncTargetDir(ex.OutputDir)) + s.Allow(filesync.NewFSSyncTarget(filesync.WithFSSyncDir("", ex.OutputDir))) } } diff --git a/exporter/local/export.go b/exporter/local/export.go index 4c8a0c5b564c..cf458420564c 100644 --- a/exporter/local/export.go +++ b/exporter/local/export.go @@ -148,7 +148,7 @@ func (e *localExporterInstance) Export(ctx context.Context, inp *exporter.Source } progress := NewProgressHandler(ctx, lbl) - if err := filesync.CopyToCaller(ctx, outputFS, caller, progress); err != nil { + if err := filesync.CopyToCaller(ctx, outputFS, "", caller, progress); err != nil { return err } return nil diff --git a/exporter/oci/export.go b/exporter/oci/export.go index 55e606c4d89c..abaac424cf33 100644 --- a/exporter/oci/export.go +++ b/exporter/oci/export.go @@ -241,7 +241,7 @@ func (e *imageExporterInstance) Export(ctx context.Context, src *exporter.Source } if e.tar { - w, err := filesync.CopyFileWriter(ctx, resp, caller) + w, err := filesync.CopyFileWriter(ctx, resp, "", caller) if err != nil { return nil, nil, err } diff --git a/exporter/tar/export.go b/exporter/tar/export.go index 50703eb649bb..e6aaff294eb1 100644 --- a/exporter/tar/export.go +++ b/exporter/tar/export.go @@ -152,7 +152,7 @@ func (e *localExporterInstance) Export(ctx context.Context, inp *exporter.Source return nil, nil, err } - w, err := filesync.CopyFileWriter(ctx, nil, caller) + w, err := filesync.CopyFileWriter(ctx, nil, "", caller) if err != nil { return nil, nil, err } diff --git a/session/filesync/filesync.go b/session/filesync/filesync.go index e664251b1aee..d781e02aee99 100644 --- a/session/filesync/filesync.go +++ b/session/filesync/filesync.go @@ -27,6 +27,8 @@ const ( keyFollowPaths = "followpaths" keyDirName = "dir-name" keyExporterMetaPrefix = "exporter-md-" + + keyExporterID = "buildkit-attachable-exporter-id" ) type fsSyncProvider struct { @@ -233,39 +235,91 @@ func FSSync(ctx context.Context, c session.Caller, opt FSSendRequestOpt) error { return pr.recvFn(stream, opt.DestDir, opt.CacheUpdater, opt.ProgressCb, opt.Differ, opt.Filter) } -// NewFSSyncTargetDir allows writing into a directory -func NewFSSyncTargetDir(outdir string) session.Attachable { - p := &fsSyncTarget{ +type FSSyncTarget interface { + target() *fsSyncTarget +} + +type fsSyncTarget struct { + id string + outdir string + f FileOutputFunc +} + +func (target *fsSyncTarget) target() *fsSyncTarget { + return target +} + +func WithFSSync(id string, f FileOutputFunc) FSSyncTarget { + return &fsSyncTarget{ + id: id, + f: f, + } +} + +func WithFSSyncDir(id string, outdir string) FSSyncTarget { + return &fsSyncTarget{ + id: id, outdir: outdir, } - return p } -// NewFSSyncTarget allows writing into an io.WriteCloser -func NewFSSyncTarget(f FileOutputFunc) session.Attachable { - p := &fsSyncTarget{ - f: f, +func NewFSSyncTarget(targets ...FSSyncTarget) session.Attachable { + fs := make(map[string]FileOutputFunc) + outdirs := make(map[string]string) + defaultID := "" + for _, t := range targets { + t := t.target() + if t.f != nil { + fs[t.id] = t.f + } + if t.outdir != "" { + outdirs[t.id] = t.outdir + } + if defaultID == "" { + defaultID = t.id + } + } + return &fsSyncAttachable{ + fs: fs, + outdirs: outdirs, + defaultID: defaultID, } - return p } -type fsSyncTarget struct { - outdir string - f FileOutputFunc +type fsSyncAttachable struct { + fs map[string]FileOutputFunc + outdirs map[string]string + + defaultID string } -func (sp *fsSyncTarget) Register(server *grpc.Server) { +func (sp *fsSyncAttachable) Register(server *grpc.Server) { RegisterFileSendServer(server, sp) } -func (sp *fsSyncTarget) DiffCopy(stream FileSend_DiffCopyServer) (err error) { - if sp.outdir != "" { - return syncTargetDiffCopy(stream, sp.outdir) +func (sp *fsSyncAttachable) chooser(ctx context.Context) string { + md, ok := metadata.FromIncomingContext(ctx) + if !ok { + return sp.defaultID } + values := md[keyExporterID] + if len(values) == 0 { + return sp.defaultID + } + id := values[0] + return id +} - if sp.f == nil { - return errors.New("empty outfile and outdir") +func (sp *fsSyncAttachable) DiffCopy(stream FileSend_DiffCopyServer) (err error) { + id := sp.chooser(stream.Context()) + if outdir, ok := sp.outdirs[id]; ok { + return syncTargetDiffCopy(stream, outdir) } + f, ok := sp.fs[id] + if !ok { + return errors.Errorf("exporter %q not found", id) + } + opts, _ := metadata.FromIncomingContext(stream.Context()) // if no metadata continue with empty object md := map[string]string{} for k, v := range opts { @@ -273,7 +327,7 @@ func (sp *fsSyncTarget) DiffCopy(stream FileSend_DiffCopyServer) (err error) { md[strings.TrimPrefix(k, keyExporterMetaPrefix)] = strings.Join(v, ",") } } - wc, err := sp.f(md) + wc, err := f(md) if err != nil { return err } @@ -289,7 +343,7 @@ func (sp *fsSyncTarget) DiffCopy(stream FileSend_DiffCopyServer) (err error) { return writeTargetFile(stream, wc) } -func CopyToCaller(ctx context.Context, fs fsutil.FS, c session.Caller, progress func(int, bool)) error { +func CopyToCaller(ctx context.Context, fs fsutil.FS, id string, c session.Caller, progress func(int, bool)) error { method := session.MethodURL(_FileSend_serviceDesc.ServiceName, "diffcopy") if !c.Supports(method) { return errors.Errorf("method %s not supported by the client", method) @@ -297,6 +351,11 @@ func CopyToCaller(ctx context.Context, fs fsutil.FS, c session.Caller, progress client := NewFileSendClient(c.Conn()) + opts := map[string][]string{ + keyExporterID: {id}, + } + ctx = metadata.NewOutgoingContext(ctx, opts) + cc, err := client.DiffCopy(ctx) if err != nil { return errors.WithStack(err) @@ -305,7 +364,7 @@ func CopyToCaller(ctx context.Context, fs fsutil.FS, c session.Caller, progress return sendDiffCopy(cc, fs, progress) } -func CopyFileWriter(ctx context.Context, md map[string]string, c session.Caller) (io.WriteCloser, error) { +func CopyFileWriter(ctx context.Context, md map[string]string, id string, c session.Caller) (io.WriteCloser, error) { method := session.MethodURL(_FileSend_serviceDesc.ServiceName, "diffcopy") if !c.Supports(method) { return nil, errors.Errorf("method %s not supported by the client", method) @@ -324,7 +383,10 @@ func CopyFileWriter(ctx context.Context, md map[string]string, c session.Caller) } opts[k] = []string{v} } - + if existingVal, ok := opts[keyExporterID]; ok { + bklog.G(ctx).Warnf("overwriting grpc metadata key %q from value %+v to %+v", keyExporterID, existingVal, id) + } + opts[keyExporterID] = []string{id} ctx = metadata.NewOutgoingContext(ctx, opts) cc, err := client.DiffCopy(ctx) From 6cd8c2a427ba64df0c93692960f28a15a5ed8442 Mon Sep 17 00:00:00 2001 From: Justin Chadwell Date: Wed, 9 Aug 2023 16:51:16 +0100 Subject: [PATCH 08/12] exporter: add new id parameter to exporter instances Co-authored-by: a-palchikov Co-authored-by: fahed dorgaa Signed-off-by: Justin Chadwell --- control/control.go | 2 +- exporter/containerimage/export.go | 9 ++++++++- exporter/exporter.go | 3 ++- exporter/local/export.go | 11 +++++++++-- exporter/oci/export.go | 11 +++++++++-- exporter/tar/export.go | 15 ++++++++++++--- 6 files changed, 41 insertions(+), 10 deletions(-) diff --git a/control/control.go b/control/control.go index 3d6409c529b8..ac09ec5fb525 100644 --- a/control/control.go +++ b/control/control.go @@ -358,7 +358,7 @@ func (c *Controller) Solve(ctx context.Context, req *controlapi.SolveRequest) (* if err != nil { return nil, err } - expi, err = exp.Resolve(ctx, req.ExporterAttrs) + expi, err = exp.Resolve(ctx, "", req.ExporterAttrs) if err != nil { return nil, err } diff --git a/exporter/containerimage/export.go b/exporter/containerimage/export.go index 7625e1d7fb7d..c8550b8f7eac 100644 --- a/exporter/containerimage/export.go +++ b/exporter/containerimage/export.go @@ -64,9 +64,10 @@ func New(opt Opt) (exporter.Exporter, error) { return im, nil } -func (e *imageExporter) Resolve(ctx context.Context, opt map[string]string) (exporter.ExporterInstance, error) { +func (e *imageExporter) Resolve(ctx context.Context, id string, opt map[string]string) (exporter.ExporterInstance, error) { i := &imageExporterInstance{ imageExporter: e, + id: id, opts: ImageCommitOpts{ RefCfg: cacheconfig.RefConfig{ Compression: compression.New(compression.Default), @@ -167,6 +168,8 @@ func (e *imageExporter) Resolve(ctx context.Context, opt map[string]string) (exp type imageExporterInstance struct { *imageExporter + id string + opts ImageCommitOpts push bool pushByDigest bool @@ -179,6 +182,10 @@ type imageExporterInstance struct { meta map[string][]byte } +func (e *imageExporterInstance) ID() string { + return e.id +} + func (e *imageExporterInstance) Name() string { return "exporting to image" } diff --git a/exporter/exporter.go b/exporter/exporter.go index 937c5d313221..eb19ddc6bfd0 100644 --- a/exporter/exporter.go +++ b/exporter/exporter.go @@ -15,10 +15,11 @@ type Source = result.Result[cache.ImmutableRef] type Attestation = result.Attestation[cache.ImmutableRef] type Exporter interface { - Resolve(context.Context, map[string]string) (ExporterInstance, error) + Resolve(context.Context, string, map[string]string) (ExporterInstance, error) } type ExporterInstance interface { + ID() string Name() string Config() *Config Export(ctx context.Context, src *Source, inlineCache exptypes.InlineCache, sessionID string) (map[string]string, DescriptorReference, error) diff --git a/exporter/local/export.go b/exporter/local/export.go index cf458420564c..a45ba920617a 100644 --- a/exporter/local/export.go +++ b/exporter/local/export.go @@ -35,8 +35,9 @@ func New(opt Opt) (exporter.Exporter, error) { return le, nil } -func (e *localExporter) Resolve(ctx context.Context, opt map[string]string) (exporter.ExporterInstance, error) { +func (e *localExporter) Resolve(ctx context.Context, id string, opt map[string]string) (exporter.ExporterInstance, error) { i := &localExporterInstance{ + id: id, localExporter: e, } _, err := i.opts.Load(opt) @@ -49,9 +50,15 @@ func (e *localExporter) Resolve(ctx context.Context, opt map[string]string) (exp type localExporterInstance struct { *localExporter + id string + opts CreateFSOpts } +func (e *localExporterInstance) ID() string { + return e.id +} + func (e *localExporterInstance) Name() string { return "exporting to client directory" } @@ -148,7 +155,7 @@ func (e *localExporterInstance) Export(ctx context.Context, inp *exporter.Source } progress := NewProgressHandler(ctx, lbl) - if err := filesync.CopyToCaller(ctx, outputFS, "", caller, progress); err != nil { + if err := filesync.CopyToCaller(ctx, outputFS, e.id, caller, progress); err != nil { return err } return nil diff --git a/exporter/oci/export.go b/exporter/oci/export.go index abaac424cf33..a788706b9e5e 100644 --- a/exporter/oci/export.go +++ b/exporter/oci/export.go @@ -58,9 +58,10 @@ func New(opt Opt) (exporter.Exporter, error) { return im, nil } -func (e *imageExporter) Resolve(ctx context.Context, opt map[string]string) (exporter.ExporterInstance, error) { +func (e *imageExporter) Resolve(ctx context.Context, id string, opt map[string]string) (exporter.ExporterInstance, error) { i := &imageExporterInstance{ imageExporter: e, + id: id, tar: true, opts: containerimage.ImageCommitOpts{ RefCfg: cacheconfig.RefConfig{ @@ -99,11 +100,17 @@ func (e *imageExporter) Resolve(ctx context.Context, opt map[string]string) (exp type imageExporterInstance struct { *imageExporter + id string + opts containerimage.ImageCommitOpts tar bool meta map[string][]byte } +func (e *imageExporterInstance) ID() string { + return e.id +} + func (e *imageExporterInstance) Name() string { return fmt.Sprintf("exporting to %s image format", e.opt.Variant) } @@ -241,7 +248,7 @@ func (e *imageExporterInstance) Export(ctx context.Context, src *exporter.Source } if e.tar { - w, err := filesync.CopyFileWriter(ctx, resp, "", caller) + w, err := filesync.CopyFileWriter(ctx, resp, e.id, caller) if err != nil { return nil, nil, err } diff --git a/exporter/tar/export.go b/exporter/tar/export.go index e6aaff294eb1..1d19f4b2e826 100644 --- a/exporter/tar/export.go +++ b/exporter/tar/export.go @@ -33,8 +33,11 @@ func New(opt Opt) (exporter.Exporter, error) { return le, nil } -func (e *localExporter) Resolve(ctx context.Context, opt map[string]string) (exporter.ExporterInstance, error) { - li := &localExporterInstance{localExporter: e} +func (e *localExporter) Resolve(ctx context.Context, id string, opt map[string]string) (exporter.ExporterInstance, error) { + li := &localExporterInstance{ + localExporter: e, + id: id, + } _, err := li.opts.Load(opt) if err != nil { return nil, err @@ -46,9 +49,15 @@ func (e *localExporter) Resolve(ctx context.Context, opt map[string]string) (exp type localExporterInstance struct { *localExporter + id string + opts local.CreateFSOpts } +func (e *localExporterInstance) ID() string { + return e.id +} + func (e *localExporterInstance) Name() string { return "exporting to client tarball" } @@ -152,7 +161,7 @@ func (e *localExporterInstance) Export(ctx context.Context, inp *exporter.Source return nil, nil, err } - w, err := filesync.CopyFileWriter(ctx, nil, "", caller) + w, err := filesync.CopyFileWriter(ctx, nil, e.id, caller) if err != nil { return nil, nil, err } From 7fb54984ea86857ae0ed01d7eed3827aed025dd8 Mon Sep 17 00:00:00 2001 From: Justin Chadwell Date: Wed, 9 Aug 2023 16:52:33 +0100 Subject: [PATCH 09/12] control: add multiple exporters This patch adds support for multiple exporters at the control API, and propogates the resulting required changes through the client and the solver. A few notable changes: - Each exporter instance now has an associated identifier - Build records in the build history now have multiple possible descriptors to built content - Exporter responses are all merged together (like we currently do with multiple cache exporters). We likely will need to revisit this design later, since now cache exporters do not line up one-to-one with exporters. For backwards compatability, new clients will continue to produce requests that contain the now deprecated exporter fields, as well as the new ones. New servers will attempt to use deprecated fields if they are present. Co-authored-by: a-palchikov Co-authored-by: fahed dorgaa Signed-off-by: Justin Chadwell --- api/services/control/control.pb.go | 699 +++++++++++++++++++++-------- api/services/control/control.proto | 16 +- client/client_test.go | 75 +++- client/solve.go | 149 +++--- cmd/buildctl/debug/monitor.go | 16 +- control/control.go | 36 +- solver/llbsolver/history.go | 14 +- solver/llbsolver/solver.go | 117 +++-- 8 files changed, 800 insertions(+), 322 deletions(-) diff --git a/api/services/control/control.pb.go b/api/services/control/control.pb.go index 9a04c36721bb..cdfcb25c0501 100644 --- a/api/services/control/control.pb.go +++ b/api/services/control/control.pb.go @@ -367,21 +367,25 @@ func (m *UsageRecord) GetParents() []string { } type SolveRequest struct { - Ref string `protobuf:"bytes,1,opt,name=Ref,proto3" json:"Ref,omitempty"` - Definition *pb.Definition `protobuf:"bytes,2,opt,name=Definition,proto3" json:"Definition,omitempty"` - Exporter string `protobuf:"bytes,3,opt,name=Exporter,proto3" json:"Exporter,omitempty"` - ExporterAttrs map[string]string `protobuf:"bytes,4,rep,name=ExporterAttrs,proto3" json:"ExporterAttrs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - Session string `protobuf:"bytes,5,opt,name=Session,proto3" json:"Session,omitempty"` - Frontend string `protobuf:"bytes,6,opt,name=Frontend,proto3" json:"Frontend,omitempty"` - FrontendAttrs map[string]string `protobuf:"bytes,7,rep,name=FrontendAttrs,proto3" json:"FrontendAttrs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - Cache CacheOptions `protobuf:"bytes,8,opt,name=Cache,proto3" json:"Cache"` - Entitlements []github_com_moby_buildkit_util_entitlements.Entitlement `protobuf:"bytes,9,rep,name=Entitlements,proto3,customtype=github.com/moby/buildkit/util/entitlements.Entitlement" json:"Entitlements,omitempty"` - FrontendInputs map[string]*pb.Definition `protobuf:"bytes,10,rep,name=FrontendInputs,proto3" json:"FrontendInputs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - Internal bool `protobuf:"varint,11,opt,name=Internal,proto3" json:"Internal,omitempty"` - SourcePolicy *pb1.Policy `protobuf:"bytes,12,opt,name=SourcePolicy,proto3" json:"SourcePolicy,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Ref string `protobuf:"bytes,1,opt,name=Ref,proto3" json:"Ref,omitempty"` + Definition *pb.Definition `protobuf:"bytes,2,opt,name=Definition,proto3" json:"Definition,omitempty"` + // ExporterDeprecated and ExporterAttrsDeprecated are deprecated in favor + // of the new Exporters. If these fields are set, then they will be + // appended to the Exporters field if Exporters was not explicitly set. + ExporterDeprecated string `protobuf:"bytes,3,opt,name=ExporterDeprecated,proto3" json:"ExporterDeprecated,omitempty"` + ExporterAttrsDeprecated map[string]string `protobuf:"bytes,4,rep,name=ExporterAttrsDeprecated,proto3" json:"ExporterAttrsDeprecated,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Session string `protobuf:"bytes,5,opt,name=Session,proto3" json:"Session,omitempty"` + Frontend string `protobuf:"bytes,6,opt,name=Frontend,proto3" json:"Frontend,omitempty"` + FrontendAttrs map[string]string `protobuf:"bytes,7,rep,name=FrontendAttrs,proto3" json:"FrontendAttrs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Cache CacheOptions `protobuf:"bytes,8,opt,name=Cache,proto3" json:"Cache"` + Entitlements []github_com_moby_buildkit_util_entitlements.Entitlement `protobuf:"bytes,9,rep,name=Entitlements,proto3,customtype=github.com/moby/buildkit/util/entitlements.Entitlement" json:"Entitlements,omitempty"` + FrontendInputs map[string]*pb.Definition `protobuf:"bytes,10,rep,name=FrontendInputs,proto3" json:"FrontendInputs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + Internal bool `protobuf:"varint,11,opt,name=Internal,proto3" json:"Internal,omitempty"` + SourcePolicy *pb1.Policy `protobuf:"bytes,12,opt,name=SourcePolicy,proto3" json:"SourcePolicy,omitempty"` + Exporters []*Exporter `protobuf:"bytes,13,rep,name=Exporters,proto3" json:"Exporters,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *SolveRequest) Reset() { *m = SolveRequest{} } @@ -431,16 +435,16 @@ func (m *SolveRequest) GetDefinition() *pb.Definition { return nil } -func (m *SolveRequest) GetExporter() string { +func (m *SolveRequest) GetExporterDeprecated() string { if m != nil { - return m.Exporter + return m.ExporterDeprecated } return "" } -func (m *SolveRequest) GetExporterAttrs() map[string]string { +func (m *SolveRequest) GetExporterAttrsDeprecated() map[string]string { if m != nil { - return m.ExporterAttrs + return m.ExporterAttrsDeprecated } return nil } @@ -494,6 +498,13 @@ func (m *SolveRequest) GetSourcePolicy() *pb1.Policy { return nil } +func (m *SolveRequest) GetExporters() []*Exporter { + if m != nil { + return m.Exporters + } + return nil +} + type CacheOptions struct { // ExportRefDeprecated is deprecated in favor or the new Exports since BuildKit v0.4.0. // When ExportRefDeprecated is set, the solver appends @@ -1832,11 +1843,12 @@ func (m *Descriptor) GetAnnotations() map[string]string { } type BuildResultInfo struct { - Result *Descriptor `protobuf:"bytes,1,opt,name=Result,proto3" json:"Result,omitempty"` - Attestations []*Descriptor `protobuf:"bytes,2,rep,name=Attestations,proto3" json:"Attestations,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + ResultDeprecated *Descriptor `protobuf:"bytes,1,opt,name=ResultDeprecated,proto3" json:"ResultDeprecated,omitempty"` + Attestations []*Descriptor `protobuf:"bytes,2,rep,name=Attestations,proto3" json:"Attestations,omitempty"` + Results map[string]*Descriptor `protobuf:"bytes,3,rep,name=Results,proto3" json:"Results,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *BuildResultInfo) Reset() { *m = BuildResultInfo{} } @@ -1872,9 +1884,9 @@ func (m *BuildResultInfo) XXX_DiscardUnknown() { var xxx_messageInfo_BuildResultInfo proto.InternalMessageInfo -func (m *BuildResultInfo) GetResult() *Descriptor { +func (m *BuildResultInfo) GetResultDeprecated() *Descriptor { if m != nil { - return m.Result + return m.ResultDeprecated } return nil } @@ -1886,12 +1898,24 @@ func (m *BuildResultInfo) GetAttestations() []*Descriptor { return nil } +func (m *BuildResultInfo) GetResults() map[string]*Descriptor { + if m != nil { + return m.Results + } + return nil +} + +// Exporter describes the output exporter type Exporter struct { - Type string `protobuf:"bytes,1,opt,name=Type,proto3" json:"Type,omitempty"` - Attrs map[string]string `protobuf:"bytes,2,rep,name=Attrs,proto3" json:"Attrs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + // Type identifies the exporter + Type string `protobuf:"bytes,1,opt,name=Type,proto3" json:"Type,omitempty"` + // Attrs specifies exporter configuration + Attrs map[string]string `protobuf:"bytes,2,rep,name=Attrs,proto3" json:"Attrs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + // ID identifies the exporter in the wire protocol + ID string `protobuf:"bytes,3,opt,name=id,proto3" json:"id,omitempty"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *Exporter) Reset() { *m = Exporter{} } @@ -1941,6 +1965,13 @@ func (m *Exporter) GetAttrs() map[string]string { return nil } +func (m *Exporter) GetID() string { + if m != nil { + return m.ID + } + return "" +} + func init() { proto.RegisterEnum("moby.buildkit.v1.BuildHistoryEventType", BuildHistoryEventType_name, BuildHistoryEventType_value) proto.RegisterType((*PruneRequest)(nil), "moby.buildkit.v1.PruneRequest") @@ -1948,7 +1979,7 @@ func init() { proto.RegisterType((*DiskUsageResponse)(nil), "moby.buildkit.v1.DiskUsageResponse") proto.RegisterType((*UsageRecord)(nil), "moby.buildkit.v1.UsageRecord") proto.RegisterType((*SolveRequest)(nil), "moby.buildkit.v1.SolveRequest") - proto.RegisterMapType((map[string]string)(nil), "moby.buildkit.v1.SolveRequest.ExporterAttrsEntry") + proto.RegisterMapType((map[string]string)(nil), "moby.buildkit.v1.SolveRequest.ExporterAttrsDeprecatedEntry") proto.RegisterMapType((map[string]string)(nil), "moby.buildkit.v1.SolveRequest.FrontendAttrsEntry") proto.RegisterMapType((map[string]*pb.Definition)(nil), "moby.buildkit.v1.SolveRequest.FrontendInputsEntry") proto.RegisterType((*CacheOptions)(nil), "moby.buildkit.v1.CacheOptions") @@ -1979,6 +2010,7 @@ func init() { proto.RegisterType((*Descriptor)(nil), "moby.buildkit.v1.Descriptor") proto.RegisterMapType((map[string]string)(nil), "moby.buildkit.v1.Descriptor.AnnotationsEntry") proto.RegisterType((*BuildResultInfo)(nil), "moby.buildkit.v1.BuildResultInfo") + proto.RegisterMapType((map[string]*Descriptor)(nil), "moby.buildkit.v1.BuildResultInfo.ResultsEntry") proto.RegisterType((*Exporter)(nil), "moby.buildkit.v1.Exporter") proto.RegisterMapType((map[string]string)(nil), "moby.buildkit.v1.Exporter.AttrsEntry") } @@ -1986,149 +2018,153 @@ func init() { func init() { proto.RegisterFile("control.proto", fileDescriptor_0c5120591600887d) } var fileDescriptor_0c5120591600887d = []byte{ - // 2260 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x59, 0xcd, 0x6e, 0x1b, 0xc9, - 0x11, 0xde, 0x21, 0x25, 0xfe, 0x14, 0x29, 0x59, 0x6a, 0x7b, 0x8d, 0xc9, 0xc4, 0x2b, 0xc9, 0xb3, - 0x76, 0x22, 0x38, 0xf6, 0x50, 0xcb, 0xac, 0x63, 0xaf, 0x9c, 0x38, 0x16, 0x45, 0x66, 0x2d, 0xc7, - 0x82, 0xb5, 0x2d, 0x79, 0x0d, 0x2c, 0xe0, 0x04, 0x23, 0xb2, 0x45, 0x0f, 0x34, 0x9c, 0x99, 0x74, - 0x37, 0xb5, 0xe6, 0x3e, 0x40, 0x80, 0xcd, 0x21, 0xc8, 0x25, 0xc8, 0x25, 0xf7, 0x9c, 0x72, 0xce, - 0x13, 0x04, 0xf0, 0x31, 0xe7, 0x3d, 0x38, 0x81, 0x1f, 0x20, 0xc8, 0x31, 0xb9, 0x05, 0xfd, 0x33, - 0xe4, 0x90, 0x33, 0x94, 0x28, 0xdb, 0x27, 0x76, 0x75, 0xd7, 0x57, 0x53, 0x55, 0x5d, 0x5d, 0x5d, - 0xd5, 0x84, 0x85, 0x76, 0x18, 0x70, 0x1a, 0xfa, 0x4e, 0x44, 0x43, 0x1e, 0xa2, 0xa5, 0x5e, 0x78, - 0x38, 0x70, 0x0e, 0xfb, 0x9e, 0xdf, 0x39, 0xf6, 0xb8, 0x73, 0xf2, 0x89, 0x55, 0xef, 0x7a, 0xfc, - 0x45, 0xff, 0xd0, 0x69, 0x87, 0xbd, 0x5a, 0x37, 0xec, 0x86, 0xb5, 0x6e, 0x18, 0x76, 0x7d, 0xe2, - 0x46, 0x1e, 0xd3, 0xc3, 0x1a, 0x8d, 0xda, 0x35, 0xc6, 0x5d, 0xde, 0x67, 0x4a, 0x8a, 0x75, 0x6b, - 0x12, 0x23, 0xa7, 0x0f, 0xfb, 0x47, 0x92, 0x92, 0x84, 0x1c, 0x69, 0xf6, 0x5a, 0x82, 0x5d, 0x7c, - 0xbf, 0x16, 0x7f, 0xbf, 0xe6, 0x46, 0x5e, 0x8d, 0x0f, 0x22, 0xc2, 0x6a, 0x5f, 0x87, 0xf4, 0x98, - 0x50, 0x0d, 0xb8, 0x39, 0x15, 0xc0, 0x42, 0xff, 0x84, 0xd0, 0x5a, 0x74, 0x58, 0x0b, 0xa3, 0x58, - 0x9b, 0xdb, 0xa7, 0x70, 0xf7, 0x69, 0x9b, 0x44, 0xa1, 0xef, 0xb5, 0x07, 0x02, 0xa3, 0x46, 0x1a, - 0xb6, 0xaa, 0xad, 0x1b, 0xea, 0xce, 0xbd, 0x1e, 0x61, 0xdc, 0xed, 0x45, 0x8a, 0xc1, 0xfe, 0xad, - 0x01, 0xd5, 0x3d, 0xda, 0x0f, 0x08, 0x26, 0xbf, 0xe9, 0x13, 0xc6, 0xd1, 0x65, 0x28, 0x1c, 0x79, - 0x3e, 0x27, 0xd4, 0x34, 0xd6, 0xf2, 0xeb, 0x65, 0xac, 0x29, 0xb4, 0x04, 0x79, 0xd7, 0xf7, 0xcd, - 0xdc, 0x9a, 0xb1, 0x5e, 0xc2, 0x62, 0x88, 0xd6, 0xa1, 0x7a, 0x4c, 0x48, 0xd4, 0xec, 0x53, 0x97, - 0x7b, 0x61, 0x60, 0xe6, 0xd7, 0x8c, 0xf5, 0x7c, 0x63, 0xee, 0xd5, 0xeb, 0x55, 0x03, 0x8f, 0xad, - 0x20, 0x1b, 0xca, 0x82, 0x6e, 0x0c, 0x38, 0x61, 0xe6, 0x5c, 0x82, 0x6d, 0x34, 0x6d, 0xdf, 0x80, - 0xa5, 0xa6, 0xc7, 0x8e, 0x9f, 0x32, 0xb7, 0x7b, 0x96, 0x2e, 0xf6, 0x23, 0x58, 0x4e, 0xf0, 0xb2, - 0x28, 0x0c, 0x18, 0x41, 0xb7, 0xa1, 0x40, 0x49, 0x3b, 0xa4, 0x1d, 0xc9, 0x5c, 0xa9, 0x7f, 0xe4, - 0x4c, 0x86, 0x81, 0xa3, 0x01, 0x82, 0x09, 0x6b, 0x66, 0xfb, 0x4f, 0x79, 0xa8, 0x24, 0xe6, 0xd1, - 0x22, 0xe4, 0x76, 0x9a, 0xa6, 0xb1, 0x66, 0xac, 0x97, 0x71, 0x6e, 0xa7, 0x89, 0x4c, 0x28, 0xee, - 0xf6, 0xb9, 0x7b, 0xe8, 0x13, 0x6d, 0x7b, 0x4c, 0xa2, 0x4b, 0x30, 0xbf, 0x13, 0x3c, 0x65, 0x44, - 0x1a, 0x5e, 0xc2, 0x8a, 0x40, 0x08, 0xe6, 0xf6, 0xbd, 0x6f, 0x88, 0x32, 0x13, 0xcb, 0x31, 0xb2, - 0xa0, 0xb0, 0xe7, 0x52, 0x12, 0x70, 0x73, 0x5e, 0xc8, 0x6d, 0xe4, 0x4c, 0x03, 0xeb, 0x19, 0xd4, - 0x80, 0xf2, 0x36, 0x25, 0x2e, 0x27, 0x9d, 0x2d, 0x6e, 0x16, 0xd6, 0x8c, 0xf5, 0x4a, 0xdd, 0x72, - 0xd4, 0xae, 0x39, 0xf1, 0xae, 0x39, 0x07, 0xf1, 0xae, 0x35, 0x4a, 0xaf, 0x5e, 0xaf, 0x7e, 0xf0, - 0x87, 0x7f, 0x0a, 0xdf, 0x0d, 0x61, 0xe8, 0x01, 0xc0, 0x63, 0x97, 0xf1, 0xa7, 0x4c, 0x0a, 0x29, - 0x9e, 0x29, 0x64, 0x4e, 0x0a, 0x48, 0x60, 0xd0, 0x0a, 0x80, 0x74, 0xc2, 0x76, 0xd8, 0x0f, 0xb8, - 0x59, 0x92, 0xba, 0x27, 0x66, 0xd0, 0x1a, 0x54, 0x9a, 0x84, 0xb5, 0xa9, 0x17, 0xc9, 0xad, 0x2e, - 0x4b, 0xf7, 0x24, 0xa7, 0x84, 0x04, 0xe5, 0xc1, 0x83, 0x41, 0x44, 0x4c, 0x90, 0x0c, 0x89, 0x19, - 0xb1, 0x97, 0xfb, 0x2f, 0x5c, 0x4a, 0x3a, 0x66, 0x45, 0xba, 0x4b, 0x53, 0xc2, 0xbf, 0xca, 0x13, - 0xcc, 0xac, 0xca, 0x4d, 0x8e, 0x49, 0xfb, 0x77, 0x45, 0xa8, 0xee, 0x8b, 0xa3, 0x10, 0x87, 0xc3, - 0x12, 0xe4, 0x31, 0x39, 0xd2, 0x7b, 0x23, 0x86, 0xc8, 0x01, 0x68, 0x92, 0x23, 0x2f, 0xf0, 0xa4, - 0x56, 0x39, 0x69, 0xf8, 0xa2, 0x13, 0x1d, 0x3a, 0xa3, 0x59, 0x9c, 0xe0, 0x40, 0x16, 0x94, 0x5a, - 0x2f, 0xa3, 0x90, 0x8a, 0x90, 0xca, 0x4b, 0x31, 0x43, 0x1a, 0x3d, 0x83, 0x85, 0x78, 0xbc, 0xc5, - 0x39, 0x15, 0x81, 0x2a, 0xc2, 0xe8, 0x93, 0x74, 0x18, 0x25, 0x95, 0x72, 0xc6, 0x30, 0xad, 0x80, - 0xd3, 0x01, 0x1e, 0x97, 0x23, 0x2c, 0xdc, 0x27, 0x8c, 0x09, 0x0d, 0xe5, 0xf6, 0xe3, 0x98, 0x14, - 0xea, 0xfc, 0x82, 0x86, 0x01, 0x27, 0x41, 0x47, 0x6e, 0x7d, 0x19, 0x0f, 0x69, 0xa1, 0x4e, 0x3c, - 0x56, 0xea, 0x14, 0x67, 0x52, 0x67, 0x0c, 0xa3, 0xd5, 0x19, 0x9b, 0x43, 0x9b, 0x30, 0xbf, 0xed, - 0xb6, 0x5f, 0x10, 0xb9, 0xcb, 0x95, 0xfa, 0x4a, 0x5a, 0xa0, 0x5c, 0x7e, 0x22, 0xb7, 0x95, 0xc9, - 0x83, 0xfa, 0x01, 0x56, 0x10, 0xf4, 0x2b, 0xa8, 0xb6, 0x02, 0xee, 0x71, 0x9f, 0xf4, 0xe4, 0x8e, - 0x95, 0xc5, 0x8e, 0x35, 0x36, 0xbf, 0x7b, 0xbd, 0xfa, 0x93, 0xa9, 0xf9, 0xa9, 0xcf, 0x3d, 0xbf, - 0x46, 0x12, 0x28, 0x27, 0x21, 0x02, 0x8f, 0xc9, 0x43, 0x5f, 0xc1, 0x62, 0xac, 0xec, 0x4e, 0x10, - 0xf5, 0x39, 0x33, 0x41, 0x5a, 0x5d, 0x9f, 0xd1, 0x6a, 0x05, 0x52, 0x66, 0x4f, 0x48, 0x12, 0xce, - 0xde, 0x09, 0x38, 0xa1, 0x81, 0xeb, 0xeb, 0x10, 0x1c, 0xd2, 0x68, 0x47, 0x44, 0x9a, 0x48, 0xa3, - 0x7b, 0x32, 0x79, 0x9a, 0x55, 0xe9, 0x9a, 0xeb, 0xe9, 0xaf, 0x26, 0x93, 0xad, 0xa3, 0x98, 0xf1, - 0x18, 0xd4, 0x7a, 0x00, 0x28, 0x1d, 0x12, 0x22, 0x74, 0x8f, 0xc9, 0x20, 0x0e, 0xdd, 0x63, 0x32, - 0x10, 0xd9, 0xe3, 0xc4, 0xf5, 0xfb, 0x2a, 0xab, 0x94, 0xb1, 0x22, 0x36, 0x73, 0x77, 0x0d, 0x21, - 0x21, 0xbd, 0x8b, 0xe7, 0x92, 0xf0, 0x05, 0x5c, 0xcc, 0xf0, 0x48, 0x86, 0x88, 0x6b, 0x49, 0x11, - 0xe9, 0xa3, 0x33, 0x12, 0x69, 0xff, 0x35, 0x0f, 0xd5, 0x64, 0x5c, 0xa0, 0x0d, 0xb8, 0xa8, 0xec, - 0xc4, 0xe4, 0xa8, 0x49, 0x22, 0x4a, 0xda, 0x22, 0x19, 0x69, 0xe1, 0x59, 0x4b, 0xa8, 0x0e, 0x97, - 0x76, 0x7a, 0x7a, 0x9a, 0x25, 0x20, 0x39, 0x79, 0xec, 0x33, 0xd7, 0x50, 0x08, 0x1f, 0x2a, 0x51, - 0xd2, 0x13, 0x09, 0x50, 0x5e, 0xc6, 0xc5, 0x67, 0xa7, 0x07, 0xaf, 0x93, 0x89, 0x55, 0xe1, 0x91, - 0x2d, 0x17, 0xfd, 0x0c, 0x8a, 0x6a, 0x21, 0x3e, 0xff, 0x1f, 0x9f, 0xfe, 0x09, 0x25, 0x2c, 0xc6, - 0x08, 0xb8, 0xb2, 0x83, 0x99, 0xf3, 0xe7, 0x80, 0x6b, 0x8c, 0xf5, 0x10, 0xac, 0xe9, 0x2a, 0x9f, - 0x27, 0x04, 0xec, 0xbf, 0x18, 0xb0, 0x9c, 0xfa, 0x90, 0xb8, 0x9c, 0x64, 0x7a, 0x56, 0x22, 0xe4, - 0x18, 0x35, 0x61, 0x5e, 0x25, 0x98, 0x9c, 0x54, 0xd8, 0x99, 0x41, 0x61, 0x27, 0x91, 0x5d, 0x14, - 0xd8, 0xba, 0x0b, 0xf0, 0x76, 0xc1, 0x6a, 0xff, 0xcd, 0x80, 0x05, 0x7d, 0x98, 0xf5, 0x4d, 0xee, - 0xc2, 0x52, 0x7c, 0x84, 0xe2, 0x39, 0x7d, 0xa7, 0xdf, 0x9e, 0x9a, 0x07, 0x14, 0x9b, 0x33, 0x89, - 0x53, 0x3a, 0xa6, 0xc4, 0x59, 0xdb, 0x71, 0x5c, 0x4d, 0xb0, 0x9e, 0x4b, 0xf3, 0xab, 0xb0, 0xb0, - 0x2f, 0x2b, 0xc6, 0xa9, 0x17, 0x94, 0xfd, 0x1f, 0x03, 0x16, 0x63, 0x1e, 0x6d, 0xdd, 0xa7, 0x50, - 0x3a, 0x21, 0x94, 0x93, 0x97, 0x84, 0x69, 0xab, 0xcc, 0xb4, 0x55, 0x5f, 0x4a, 0x0e, 0x3c, 0xe4, - 0x44, 0x9b, 0x50, 0x52, 0xd5, 0x29, 0x89, 0x37, 0x6a, 0x65, 0x1a, 0x4a, 0x7f, 0x6f, 0xc8, 0x8f, - 0x6a, 0x30, 0xe7, 0x87, 0x5d, 0xa6, 0xcf, 0xcc, 0xf7, 0xa7, 0xe1, 0x1e, 0x87, 0x5d, 0x2c, 0x19, - 0xd1, 0x3d, 0x28, 0x7d, 0xed, 0xd2, 0xc0, 0x0b, 0xba, 0xf1, 0x29, 0x58, 0x9d, 0x06, 0x7a, 0xa6, - 0xf8, 0xf0, 0x10, 0x20, 0x0a, 0xaa, 0x82, 0x5a, 0x43, 0x8f, 0xa0, 0xd0, 0xf1, 0xba, 0x84, 0x71, - 0xe5, 0x92, 0x46, 0x5d, 0xdc, 0x25, 0xdf, 0xbd, 0x5e, 0xbd, 0x91, 0xb8, 0x2c, 0xc2, 0x88, 0x04, - 0xa2, 0x7c, 0x77, 0xbd, 0x80, 0x50, 0x51, 0x8d, 0xdf, 0x52, 0x10, 0xa7, 0x29, 0x7f, 0xb0, 0x96, - 0x20, 0x64, 0x79, 0xea, 0x4a, 0x90, 0xf9, 0xe2, 0xed, 0x64, 0x29, 0x09, 0xe2, 0x18, 0x04, 0x6e, - 0x8f, 0xe8, 0x12, 0x40, 0x8e, 0x45, 0x7d, 0xd2, 0x16, 0x71, 0xde, 0x91, 0x95, 0x5b, 0x09, 0x6b, - 0x0a, 0x6d, 0x42, 0x91, 0x71, 0x97, 0x8a, 0x9c, 0x33, 0x3f, 0x63, 0x61, 0x15, 0x03, 0xd0, 0x7d, - 0x28, 0xb7, 0xc3, 0x5e, 0xe4, 0x13, 0x81, 0x2e, 0xcc, 0x88, 0x1e, 0x41, 0x44, 0xe8, 0x11, 0x4a, - 0x43, 0x2a, 0x4b, 0xba, 0x32, 0x56, 0x04, 0xba, 0x03, 0x0b, 0x11, 0x0d, 0xbb, 0x94, 0x30, 0xf6, - 0x39, 0x0d, 0xfb, 0x91, 0xbe, 0xc8, 0x97, 0x45, 0xf2, 0xde, 0x4b, 0x2e, 0xe0, 0x71, 0x3e, 0xfb, - 0xdf, 0x39, 0xa8, 0x26, 0x43, 0x24, 0x55, 0xeb, 0x3e, 0x82, 0x82, 0x0a, 0x38, 0x15, 0xeb, 0x6f, - 0xe7, 0x63, 0x25, 0x21, 0xd3, 0xc7, 0x26, 0x14, 0xdb, 0x7d, 0x2a, 0x0b, 0x61, 0x55, 0x1e, 0xc7, - 0xa4, 0xb0, 0x94, 0x87, 0xdc, 0xf5, 0xa5, 0x8f, 0xf3, 0x58, 0x11, 0xa2, 0x36, 0x1e, 0xf6, 0x2b, - 0xe7, 0xab, 0x8d, 0x87, 0xb0, 0xe4, 0xfe, 0x15, 0xdf, 0x69, 0xff, 0x4a, 0xe7, 0xde, 0x3f, 0xfb, - 0xef, 0x06, 0x94, 0x87, 0x67, 0x2b, 0xe1, 0x5d, 0xe3, 0x9d, 0xbd, 0x3b, 0xe6, 0x99, 0xdc, 0xdb, - 0x79, 0xe6, 0x32, 0x14, 0x18, 0xa7, 0xc4, 0xed, 0xa9, 0xce, 0x0d, 0x6b, 0x4a, 0x64, 0xb1, 0x1e, - 0xeb, 0xca, 0x1d, 0xaa, 0x62, 0x31, 0xb4, 0xff, 0x6b, 0xc0, 0xc2, 0xd8, 0x71, 0x7f, 0xaf, 0xb6, - 0x5c, 0x82, 0x79, 0x9f, 0x9c, 0x10, 0xd5, 0x5b, 0xe6, 0xb1, 0x22, 0xc4, 0x2c, 0x7b, 0x11, 0x52, - 0x2e, 0x95, 0xab, 0x62, 0x45, 0x08, 0x9d, 0x3b, 0x84, 0xbb, 0x9e, 0x2f, 0xf3, 0x52, 0x15, 0x6b, - 0x4a, 0xe8, 0xdc, 0xa7, 0xbe, 0xae, 0xaf, 0xc5, 0x10, 0xd9, 0x30, 0xe7, 0x05, 0x47, 0xa1, 0x0e, - 0x1b, 0x59, 0xd9, 0xa8, 0x3a, 0x6d, 0x27, 0x38, 0x0a, 0xb1, 0x5c, 0x43, 0x57, 0xa1, 0x40, 0xdd, - 0xa0, 0x4b, 0xe2, 0xe2, 0xba, 0x2c, 0xb8, 0xb0, 0x98, 0xc1, 0x7a, 0xc1, 0xb6, 0xa1, 0x2a, 0xfb, - 0xd3, 0x5d, 0xc2, 0x44, 0x37, 0x24, 0xc2, 0xba, 0xe3, 0x72, 0x57, 0x9a, 0x5d, 0xc5, 0x72, 0x6c, - 0xdf, 0x04, 0xf4, 0xd8, 0x63, 0xfc, 0x99, 0xec, 0xee, 0xd9, 0x59, 0xcd, 0xeb, 0x3e, 0x5c, 0x1c, - 0xe3, 0xd6, 0xd7, 0xc2, 0x4f, 0x27, 0xda, 0xd7, 0x6b, 0xe9, 0x8c, 0x2b, 0x1f, 0x11, 0x1c, 0x05, - 0x9c, 0xe8, 0x62, 0x17, 0xa0, 0x22, 0xed, 0x52, 0xdf, 0xb6, 0x5d, 0xa8, 0x2a, 0x52, 0x0b, 0xff, - 0x02, 0x2e, 0xc4, 0x82, 0xbe, 0x24, 0x54, 0xb6, 0x22, 0x86, 0xf4, 0xcb, 0x0f, 0xa7, 0x7d, 0xa5, - 0x31, 0xce, 0x8e, 0x27, 0xf1, 0x36, 0x81, 0x8b, 0x92, 0xe7, 0xa1, 0xc7, 0x78, 0x48, 0x07, 0xb1, - 0xd5, 0x2b, 0x00, 0x5b, 0x6d, 0xee, 0x9d, 0x90, 0x27, 0x81, 0xaf, 0xae, 0xd1, 0x12, 0x4e, 0xcc, - 0xc4, 0x57, 0x64, 0x6e, 0xd4, 0xc3, 0x5d, 0x81, 0x72, 0xcb, 0xa5, 0xfe, 0xa0, 0xf5, 0xd2, 0xe3, - 0xba, 0x95, 0x1e, 0x4d, 0xd8, 0xbf, 0x37, 0x60, 0x39, 0xf9, 0x9d, 0xd6, 0x89, 0x48, 0x17, 0xf7, - 0x60, 0x8e, 0xc7, 0x75, 0xcc, 0x62, 0x96, 0x11, 0x29, 0x88, 0x28, 0x75, 0xb0, 0x04, 0x25, 0x3c, - 0xad, 0x0e, 0xce, 0xb5, 0xd3, 0xe1, 0x13, 0x9e, 0xfe, 0x5f, 0x09, 0x50, 0x7a, 0x39, 0xa3, 0x37, - 0x4d, 0x36, 0x77, 0xb9, 0x89, 0xe6, 0xee, 0xf9, 0x64, 0x73, 0xa7, 0xae, 0xe6, 0x3b, 0xb3, 0x68, - 0x32, 0x43, 0x8b, 0x77, 0x17, 0xca, 0x71, 0x75, 0x13, 0x5f, 0xe0, 0x56, 0x5a, 0xf4, 0xb0, 0x00, - 0x1a, 0x31, 0xa3, 0xf5, 0xf8, 0xc6, 0x51, 0x77, 0x1d, 0x8a, 0x73, 0x0a, 0x8d, 0xda, 0x8e, 0xae, - 0x2b, 0xf4, 0x2d, 0x74, 0xff, 0x7c, 0xef, 0x16, 0x73, 0x93, 0x6f, 0x16, 0x0d, 0xa8, 0x6c, 0xc7, - 0x89, 0xf2, 0x1c, 0x8f, 0x16, 0x49, 0x10, 0xda, 0xd0, 0x85, 0x8d, 0x4a, 0xcd, 0x57, 0xd2, 0x26, - 0xc6, 0x0f, 0x14, 0x21, 0xd5, 0x95, 0xcd, 0x51, 0x46, 0x69, 0x59, 0x96, 0x0e, 0xda, 0x9c, 0xc9, - 0xf7, 0x33, 0xd6, 0x97, 0xe8, 0x33, 0x28, 0x60, 0xc2, 0xfa, 0x3e, 0x97, 0x2f, 0x21, 0x95, 0xfa, - 0xd5, 0x29, 0xd2, 0x15, 0x93, 0x3c, 0xab, 0x1a, 0x80, 0x7e, 0x09, 0x45, 0x35, 0x62, 0x66, 0x65, - 0x5a, 0xcb, 0x9f, 0xa1, 0x99, 0xc6, 0xe8, 0x86, 0x42, 0x53, 0xe2, 0x38, 0x7e, 0x4e, 0x02, 0xa2, - 0x5f, 0xe8, 0x44, 0x5b, 0x3b, 0x8f, 0x13, 0x33, 0xa8, 0x0e, 0xf3, 0x9c, 0xba, 0x6d, 0x62, 0x2e, - 0xcc, 0xe0, 0x42, 0xc5, 0x2a, 0x12, 0x5b, 0xe4, 0x05, 0x01, 0xe9, 0x98, 0x8b, 0xaa, 0x52, 0x52, - 0x14, 0xfa, 0x01, 0x2c, 0x06, 0xfd, 0x9e, 0x6c, 0x16, 0x3a, 0xfb, 0x9c, 0x44, 0xcc, 0xbc, 0x20, - 0xbf, 0x37, 0x31, 0x8b, 0xae, 0xc1, 0x42, 0xd0, 0xef, 0x1d, 0x88, 0x1b, 0x5e, 0xb1, 0x2d, 0x49, - 0xb6, 0xf1, 0x49, 0x74, 0x13, 0x96, 0x05, 0x2e, 0xde, 0x6d, 0xc5, 0xb9, 0x2c, 0x39, 0xd3, 0x0b, - 0xef, 0xa1, 0x67, 0x7e, 0x1f, 0x1d, 0x81, 0xf5, 0x1c, 0xaa, 0xc9, 0x7d, 0xc8, 0xc0, 0xde, 0x19, - 0xef, 0xb8, 0x67, 0x88, 0x8b, 0x44, 0xc3, 0xf1, 0x1c, 0xbe, 0xf7, 0x34, 0xea, 0xb8, 0x9c, 0x64, - 0x65, 0xde, 0x74, 0x06, 0xba, 0x0c, 0x85, 0x3d, 0xb5, 0x51, 0xea, 0xe5, 0x52, 0x53, 0x62, 0xbe, - 0x49, 0x84, 0xf3, 0x74, 0xba, 0xd5, 0x94, 0x7d, 0x05, 0xac, 0x2c, 0xf1, 0xca, 0x19, 0xf6, 0x9f, - 0x73, 0x00, 0xa3, 0x60, 0x40, 0x1f, 0x01, 0xf4, 0x48, 0xc7, 0x73, 0x7f, 0xcd, 0x47, 0x0d, 0x65, - 0x59, 0xce, 0xc8, 0xae, 0x72, 0x54, 0xfa, 0xe7, 0xde, 0xb9, 0xf4, 0x47, 0x30, 0xc7, 0xbc, 0x6f, - 0x88, 0x2e, 0x53, 0xe4, 0x18, 0x3d, 0x81, 0x8a, 0x1b, 0x04, 0x21, 0x97, 0x61, 0x1c, 0x37, 0xdb, - 0xb7, 0x4e, 0x0b, 0x5f, 0x67, 0x6b, 0xc4, 0xaf, 0x4e, 0x49, 0x52, 0x82, 0x75, 0x1f, 0x96, 0x26, - 0x19, 0xce, 0xd5, 0x0c, 0x7e, 0x6b, 0xc0, 0x85, 0x89, 0xad, 0x43, 0x9f, 0x0e, 0xb3, 0x80, 0x31, - 0xc3, 0xf1, 0x8a, 0x13, 0xc0, 0x03, 0xa8, 0x6e, 0x71, 0x2e, 0xb2, 0x9e, 0xb2, 0x4d, 0xb5, 0x7b, - 0xa7, 0x63, 0xc7, 0x10, 0xf6, 0x1f, 0x8d, 0xd1, 0x3b, 0x67, 0x66, 0xcf, 0x7f, 0x6f, 0xbc, 0xe7, - 0xbf, 0x3e, 0xfd, 0x72, 0x78, 0x9f, 0xad, 0xfe, 0x8d, 0x9f, 0xc3, 0x87, 0x99, 0x17, 0x33, 0xaa, - 0x40, 0x71, 0xff, 0x60, 0x0b, 0x1f, 0xb4, 0x9a, 0x4b, 0x1f, 0xa0, 0x2a, 0x94, 0xb6, 0x9f, 0xec, - 0xee, 0x3d, 0x6e, 0x1d, 0xb4, 0x96, 0x0c, 0xb1, 0xd4, 0x6c, 0x89, 0x71, 0x73, 0x29, 0x57, 0xff, - 0xb6, 0x00, 0xc5, 0x6d, 0xf5, 0x5f, 0x0f, 0x3a, 0x80, 0xf2, 0xf0, 0x4f, 0x00, 0x64, 0x67, 0x78, - 0x67, 0xe2, 0xdf, 0x04, 0xeb, 0xe3, 0x53, 0x79, 0x74, 0xe2, 0x7e, 0x08, 0xf3, 0xf2, 0xef, 0x10, - 0x94, 0xd1, 0x5e, 0x27, 0xff, 0x27, 0xb1, 0x4e, 0xff, 0x7b, 0x61, 0xc3, 0x10, 0x92, 0xe4, 0xdb, - 0x44, 0x96, 0xa4, 0xe4, 0xe3, 0xa5, 0xb5, 0x7a, 0xc6, 0xa3, 0x06, 0xda, 0x85, 0x82, 0x6e, 0xd8, - 0xb2, 0x58, 0x93, 0x2f, 0x10, 0xd6, 0xda, 0x74, 0x06, 0x25, 0x6c, 0xc3, 0x40, 0xbb, 0xc3, 0xf7, - 0xe8, 0x2c, 0xd5, 0x92, 0xd5, 0xae, 0x75, 0xc6, 0xfa, 0xba, 0xb1, 0x61, 0xa0, 0xaf, 0xa0, 0x92, - 0xa8, 0x67, 0x51, 0x46, 0x35, 0x95, 0x2e, 0x8e, 0xad, 0xeb, 0x67, 0x70, 0x69, 0xcb, 0x5b, 0x30, - 0x27, 0x0f, 0x52, 0x86, 0xb3, 0x13, 0xe5, 0x6e, 0x96, 0x9a, 0x63, 0xe5, 0xef, 0xa1, 0x2a, 0xd0, - 0x49, 0x90, 0x8c, 0x3e, 0x74, 0xfd, 0xac, 0x7b, 0x75, 0x6a, 0xd8, 0xa4, 0x82, 0x78, 0xc3, 0x40, - 0x21, 0xa0, 0x74, 0xf2, 0x44, 0x3f, 0xca, 0x88, 0x92, 0x69, 0x19, 0xdc, 0xba, 0x39, 0x1b, 0xb3, - 0x32, 0xaa, 0x51, 0x7d, 0xf5, 0x66, 0xc5, 0xf8, 0xc7, 0x9b, 0x15, 0xe3, 0x5f, 0x6f, 0x56, 0x8c, - 0xc3, 0x82, 0xac, 0x98, 0x7e, 0xfc, 0xff, 0x00, 0x00, 0x00, 0xff, 0xff, 0x00, 0xf6, 0x99, 0xcf, - 0x0b, 0x1d, 0x00, 0x00, + // 2331 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x59, 0x4f, 0x73, 0x1b, 0x49, + 0x15, 0xdf, 0x91, 0x64, 0x59, 0x7a, 0x92, 0xbd, 0x72, 0x67, 0x37, 0x0c, 0x43, 0xd6, 0x76, 0x66, + 0x13, 0x70, 0x85, 0x64, 0xe4, 0x15, 0x84, 0x64, 0x1d, 0x08, 0xb1, 0x2c, 0xb1, 0x71, 0x48, 0x2a, + 0xde, 0xb6, 0xb3, 0xa1, 0xb6, 0x2a, 0x50, 0x63, 0xa9, 0xad, 0x4c, 0x79, 0x34, 0x33, 0x74, 0xb7, + 0xbc, 0xf1, 0x9e, 0x38, 0x51, 0xc5, 0x85, 0xe2, 0xc6, 0x85, 0x3b, 0x27, 0xce, 0x9c, 0x39, 0x50, + 0x95, 0x23, 0xe7, 0x3d, 0x04, 0x2a, 0x1f, 0x80, 0xe2, 0x08, 0x37, 0xaa, 0xff, 0x8c, 0x34, 0xd2, + 0x8c, 0x2c, 0x39, 0xc9, 0x49, 0xfd, 0xba, 0xdf, 0xef, 0xcd, 0x7b, 0xaf, 0x5f, 0xbf, 0x7e, 0xaf, + 0x05, 0x4b, 0x9d, 0x30, 0xe0, 0x34, 0xf4, 0x9d, 0x88, 0x86, 0x3c, 0x44, 0xb5, 0x7e, 0x78, 0x78, + 0xea, 0x1c, 0x0e, 0x3c, 0xbf, 0x7b, 0xec, 0x71, 0xe7, 0xe4, 0x13, 0xab, 0xd1, 0xf3, 0xf8, 0xf3, + 0xc1, 0xa1, 0xd3, 0x09, 0xfb, 0xf5, 0x5e, 0xd8, 0x0b, 0xeb, 0xbd, 0x30, 0xec, 0xf9, 0xc4, 0x8d, + 0x3c, 0xa6, 0x87, 0x75, 0x1a, 0x75, 0xea, 0x8c, 0xbb, 0x7c, 0xc0, 0x94, 0x14, 0xeb, 0xc6, 0x24, + 0x46, 0x4e, 0x1f, 0x0e, 0x8e, 0x24, 0x25, 0x09, 0x39, 0xd2, 0xec, 0xf5, 0x04, 0xbb, 0xf8, 0x7e, + 0x3d, 0xfe, 0x7e, 0xdd, 0x8d, 0xbc, 0x3a, 0x3f, 0x8d, 0x08, 0xab, 0x7f, 0x15, 0xd2, 0x63, 0x42, + 0x35, 0xe0, 0xfa, 0x54, 0x00, 0x0b, 0xfd, 0x13, 0x42, 0xeb, 0xd1, 0x61, 0x3d, 0x8c, 0x62, 0x6d, + 0x6e, 0x9e, 0xc1, 0x3d, 0xa0, 0x1d, 0x12, 0x85, 0xbe, 0xd7, 0x39, 0x15, 0x18, 0x35, 0xd2, 0xb0, + 0x35, 0x6d, 0xdd, 0x50, 0x77, 0xee, 0xf5, 0x09, 0xe3, 0x6e, 0x3f, 0x52, 0x0c, 0xf6, 0x6f, 0x0d, + 0xa8, 0xee, 0xd1, 0x41, 0x40, 0x30, 0xf9, 0xf5, 0x80, 0x30, 0x8e, 0x2e, 0x42, 0xf1, 0xc8, 0xf3, + 0x39, 0xa1, 0xa6, 0xb1, 0x9e, 0xdf, 0x28, 0x63, 0x4d, 0xa1, 0x1a, 0xe4, 0x5d, 0xdf, 0x37, 0x73, + 0xeb, 0xc6, 0x46, 0x09, 0x8b, 0x21, 0xda, 0x80, 0xea, 0x31, 0x21, 0x51, 0x6b, 0x40, 0x5d, 0xee, + 0x85, 0x81, 0x99, 0x5f, 0x37, 0x36, 0xf2, 0xcd, 0xc2, 0xcb, 0x57, 0x6b, 0x06, 0x1e, 0x5b, 0x41, + 0x36, 0x94, 0x05, 0xdd, 0x3c, 0xe5, 0x84, 0x99, 0x85, 0x04, 0xdb, 0x68, 0xda, 0xbe, 0x06, 0xb5, + 0x96, 0xc7, 0x8e, 0x9f, 0x30, 0xb7, 0x37, 0x4b, 0x17, 0xfb, 0x01, 0xac, 0x24, 0x78, 0x59, 0x14, + 0x06, 0x8c, 0xa0, 0x9b, 0x50, 0xa4, 0xa4, 0x13, 0xd2, 0xae, 0x64, 0xae, 0x34, 0x3e, 0x72, 0x26, + 0xc3, 0xc0, 0xd1, 0x00, 0xc1, 0x84, 0x35, 0xb3, 0xfd, 0xc7, 0x3c, 0x54, 0x12, 0xf3, 0x68, 0x19, + 0x72, 0xbb, 0x2d, 0xd3, 0x58, 0x37, 0x36, 0xca, 0x38, 0xb7, 0xdb, 0x42, 0x26, 0x2c, 0x3e, 0x1a, + 0x70, 0xf7, 0xd0, 0x27, 0xda, 0xf6, 0x98, 0x44, 0x1f, 0xc0, 0xc2, 0x6e, 0xf0, 0x84, 0x11, 0x69, + 0x78, 0x09, 0x2b, 0x02, 0x21, 0x28, 0xec, 0x7b, 0x5f, 0x13, 0x65, 0x26, 0x96, 0x63, 0x64, 0x41, + 0x71, 0xcf, 0xa5, 0x24, 0xe0, 0xe6, 0x82, 0x90, 0xdb, 0xcc, 0x99, 0x06, 0xd6, 0x33, 0xa8, 0x09, + 0xe5, 0x1d, 0x4a, 0x5c, 0x4e, 0xba, 0xdb, 0xdc, 0x2c, 0xae, 0x1b, 0x1b, 0x95, 0x86, 0xe5, 0xa8, + 0x5d, 0x73, 0xe2, 0x5d, 0x73, 0x0e, 0xe2, 0x5d, 0x6b, 0x96, 0x5e, 0xbe, 0x5a, 0x7b, 0xef, 0x0f, + 0xff, 0x14, 0xbe, 0x1b, 0xc2, 0xd0, 0x3d, 0x80, 0x87, 0x2e, 0xe3, 0x4f, 0x98, 0x14, 0xb2, 0x38, + 0x53, 0x48, 0x41, 0x0a, 0x48, 0x60, 0xd0, 0x2a, 0x80, 0x74, 0xc2, 0x4e, 0x38, 0x08, 0xb8, 0x59, + 0x92, 0xba, 0x27, 0x66, 0xd0, 0x3a, 0x54, 0x5a, 0x84, 0x75, 0xa8, 0x17, 0xc9, 0xad, 0x2e, 0x4b, + 0xf7, 0x24, 0xa7, 0x84, 0x04, 0xe5, 0xc1, 0x83, 0xd3, 0x88, 0x98, 0x20, 0x19, 0x12, 0x33, 0x62, + 0x2f, 0xf7, 0x9f, 0xbb, 0x94, 0x74, 0xcd, 0x8a, 0x74, 0x97, 0xa6, 0x84, 0x7f, 0x95, 0x27, 0x98, + 0x59, 0x95, 0x9b, 0x1c, 0x93, 0xf6, 0x6f, 0x4a, 0x50, 0xdd, 0x17, 0x47, 0x21, 0x0e, 0x87, 0x1a, + 0xe4, 0x31, 0x39, 0xd2, 0x7b, 0x23, 0x86, 0xc8, 0x01, 0x68, 0x91, 0x23, 0x2f, 0xf0, 0xa4, 0x56, + 0x39, 0x69, 0xf8, 0xb2, 0x13, 0x1d, 0x3a, 0xa3, 0x59, 0x9c, 0xe0, 0x40, 0x0e, 0xa0, 0xf6, 0x8b, + 0x28, 0xa4, 0x9c, 0xd0, 0x16, 0x89, 0x28, 0xe9, 0x08, 0x07, 0xca, 0xfd, 0x2b, 0xe3, 0x8c, 0x15, + 0x34, 0x80, 0x6f, 0xc5, 0xb3, 0xdb, 0x9c, 0x53, 0x96, 0x00, 0x15, 0x64, 0x90, 0xdd, 0x49, 0x07, + 0x59, 0x52, 0x65, 0x67, 0x0a, 0xba, 0x1d, 0x70, 0x7a, 0x8a, 0xa7, 0xc9, 0x16, 0x3e, 0xd9, 0x27, + 0x8c, 0x09, 0x9b, 0x64, 0xc0, 0xe0, 0x98, 0x44, 0x16, 0x94, 0x7e, 0x46, 0xc3, 0x80, 0x93, 0xa0, + 0x2b, 0x83, 0xa5, 0x8c, 0x87, 0x34, 0x7a, 0x0a, 0x4b, 0xf1, 0x58, 0x0a, 0x34, 0x17, 0xa5, 0x8a, + 0x9f, 0xcc, 0x50, 0x71, 0x0c, 0xa3, 0x14, 0x1b, 0x97, 0x83, 0xb6, 0x60, 0x61, 0xc7, 0xed, 0x3c, + 0x27, 0x32, 0x2e, 0x2a, 0x8d, 0xd5, 0xb4, 0x40, 0xb9, 0xfc, 0x58, 0x06, 0x02, 0x93, 0x47, 0xfb, + 0x3d, 0xac, 0x20, 0xe8, 0x97, 0x50, 0x6d, 0x07, 0xdc, 0xe3, 0x3e, 0xe9, 0xcb, 0x3d, 0x2e, 0x8b, + 0x3d, 0x6e, 0x6e, 0x7d, 0xf3, 0x6a, 0xed, 0x47, 0x53, 0x33, 0xda, 0x80, 0x7b, 0x7e, 0x9d, 0x24, + 0x50, 0x4e, 0x42, 0x04, 0x1e, 0x93, 0x87, 0xbe, 0x84, 0xe5, 0x58, 0xd9, 0xdd, 0x20, 0x1a, 0x70, + 0x66, 0x82, 0xb4, 0xba, 0x31, 0xa7, 0xd5, 0x0a, 0xa4, 0xcc, 0x9e, 0x90, 0x24, 0x9c, 0xbd, 0x1b, + 0x70, 0x42, 0x03, 0xd7, 0xd7, 0x41, 0x3b, 0xa4, 0xd1, 0xae, 0x88, 0x4d, 0x91, 0x78, 0xf7, 0x64, + 0xba, 0x35, 0xab, 0xd2, 0x35, 0x57, 0xd3, 0x5f, 0x4d, 0xa6, 0x67, 0x47, 0x31, 0xe3, 0x31, 0x28, + 0xba, 0x0d, 0xe5, 0x38, 0x10, 0x98, 0xb9, 0x24, 0xb5, 0xb7, 0xd2, 0x72, 0x62, 0x16, 0x3c, 0x62, + 0xb6, 0x1e, 0xc0, 0xa5, 0xb3, 0x02, 0x4c, 0x1c, 0x98, 0x63, 0x72, 0x1a, 0x1f, 0x98, 0x63, 0x72, + 0x2a, 0x72, 0xd6, 0x89, 0xeb, 0x0f, 0x54, 0x2e, 0x2b, 0x63, 0x45, 0x6c, 0xe5, 0x6e, 0x1b, 0xd6, + 0x3d, 0x40, 0xe9, 0x48, 0x38, 0x97, 0x84, 0xcf, 0xe1, 0x42, 0x86, 0x57, 0x33, 0x44, 0x5c, 0x49, + 0x8a, 0x48, 0x1f, 0xd8, 0x91, 0x48, 0xfb, 0x2f, 0x79, 0xa8, 0x26, 0x63, 0x0b, 0x6d, 0xc2, 0x05, + 0x65, 0x31, 0x26, 0x47, 0x89, 0xc3, 0xa8, 0x84, 0x67, 0x2d, 0xa1, 0x06, 0x7c, 0xb0, 0xdb, 0xd7, + 0xd3, 0xc9, 0xf3, 0x9b, 0x93, 0xc9, 0x26, 0x73, 0x0d, 0x85, 0xf0, 0xa1, 0x12, 0x35, 0x79, 0xe8, + 0xf3, 0x72, 0x77, 0x3e, 0x3d, 0xfb, 0x00, 0x38, 0x99, 0x58, 0x15, 0x62, 0xd9, 0x72, 0xd1, 0x4f, + 0x60, 0x51, 0x2d, 0x30, 0x9d, 0x57, 0x3e, 0x3e, 0xfb, 0x13, 0x4a, 0x58, 0x8c, 0x11, 0x70, 0x65, + 0x07, 0x33, 0x17, 0xce, 0x01, 0xd7, 0x18, 0xeb, 0x3e, 0x58, 0xd3, 0x55, 0x3e, 0x4f, 0x08, 0xd8, + 0x7f, 0x36, 0x60, 0x25, 0xf5, 0x21, 0x71, 0x25, 0xca, 0x4b, 0x41, 0x89, 0x90, 0x63, 0xd4, 0x82, + 0x05, 0x95, 0xa4, 0x72, 0x52, 0x61, 0x67, 0x0e, 0x85, 0x9d, 0x44, 0x86, 0x52, 0x60, 0xeb, 0x36, + 0xc0, 0x9b, 0x05, 0xab, 0xfd, 0x57, 0x03, 0x96, 0x74, 0x42, 0xd0, 0xf5, 0x83, 0x0b, 0xb5, 0xe1, + 0x19, 0xd3, 0x73, 0xba, 0x92, 0xb8, 0x39, 0x35, 0x97, 0x28, 0x36, 0x67, 0x12, 0xa7, 0x74, 0x4c, + 0x89, 0xb3, 0x76, 0xe2, 0xb8, 0x9a, 0x60, 0x3d, 0x97, 0xe6, 0x97, 0x61, 0x69, 0x5f, 0xd6, 0xa9, + 0x53, 0xaf, 0x45, 0xfb, 0x3f, 0x06, 0x2c, 0xc7, 0x3c, 0xda, 0xba, 0x1f, 0x42, 0xe9, 0x84, 0x50, + 0x4e, 0x5e, 0x10, 0xa6, 0xad, 0x32, 0xd3, 0x56, 0x7d, 0x21, 0x39, 0xf0, 0x90, 0x13, 0x6d, 0x41, + 0x49, 0xd5, 0xc4, 0x24, 0xde, 0xa8, 0xd5, 0x69, 0x28, 0xfd, 0xbd, 0x21, 0x3f, 0xaa, 0x43, 0xc1, + 0x0f, 0x7b, 0x4c, 0x9f, 0x99, 0xef, 0x4c, 0xc3, 0x3d, 0x0c, 0x7b, 0x58, 0x32, 0xa2, 0x3b, 0x50, + 0xfa, 0xca, 0xa5, 0x81, 0x17, 0xf4, 0xe2, 0x53, 0xb0, 0x36, 0x0d, 0xf4, 0x54, 0xf1, 0xe1, 0x21, + 0x40, 0x94, 0x71, 0x45, 0xb5, 0x86, 0x1e, 0x40, 0xb1, 0xeb, 0xf5, 0x08, 0xe3, 0xca, 0x25, 0xcd, + 0x86, 0xb8, 0x8f, 0xbe, 0x79, 0xb5, 0x76, 0x2d, 0x71, 0xe1, 0x84, 0x11, 0x09, 0x44, 0xd3, 0xe0, + 0x7a, 0x01, 0xa1, 0xa2, 0x07, 0xb8, 0xa1, 0x20, 0x4e, 0x4b, 0xfe, 0x60, 0x2d, 0x41, 0xc8, 0xf2, + 0xd4, 0xb5, 0x22, 0xf3, 0xc5, 0x9b, 0xc9, 0x52, 0x12, 0xc4, 0x31, 0x08, 0xdc, 0x3e, 0xd1, 0xe5, + 0x86, 0x1c, 0x8b, 0xaa, 0xa8, 0x23, 0xe2, 0xbc, 0x2b, 0xeb, 0xc5, 0x12, 0xd6, 0x14, 0xda, 0x82, + 0x45, 0xc6, 0x5d, 0x2a, 0x72, 0xce, 0xc2, 0x9c, 0xe5, 0x5c, 0x0c, 0x40, 0x77, 0xa1, 0xdc, 0x09, + 0xfb, 0x91, 0x4f, 0x04, 0xba, 0x38, 0x27, 0x7a, 0x04, 0x11, 0xa1, 0x47, 0x28, 0x0d, 0xa9, 0x2c, + 0x24, 0xcb, 0x58, 0x11, 0xe8, 0x16, 0x2c, 0x45, 0x34, 0xec, 0x51, 0xc2, 0xd8, 0x67, 0x34, 0x1c, + 0x44, 0xba, 0x18, 0x58, 0x11, 0xc9, 0x7b, 0x2f, 0xb9, 0x80, 0xc7, 0xf9, 0xec, 0x7f, 0xe7, 0xa0, + 0x9a, 0x0c, 0x91, 0x54, 0x85, 0xfd, 0x00, 0x8a, 0x2a, 0xe0, 0x54, 0xac, 0xbf, 0x99, 0x8f, 0x95, + 0x84, 0x4c, 0x1f, 0x9b, 0xb0, 0xd8, 0x19, 0x50, 0x59, 0x7e, 0xab, 0xa2, 0x3c, 0x26, 0x85, 0xa5, + 0x3c, 0xe4, 0xae, 0x2f, 0x7d, 0x9c, 0xc7, 0x8a, 0x10, 0x15, 0xf9, 0xb0, 0x4b, 0x3a, 0x5f, 0x45, + 0x3e, 0x84, 0x25, 0xf7, 0x6f, 0xf1, 0xad, 0xf6, 0xaf, 0x74, 0xee, 0xfd, 0xb3, 0xff, 0x6e, 0x40, + 0x79, 0x78, 0xb6, 0x12, 0xde, 0x35, 0xde, 0xda, 0xbb, 0x63, 0x9e, 0xc9, 0xbd, 0x99, 0x67, 0x2e, + 0x42, 0x91, 0x71, 0x4a, 0xdc, 0xbe, 0xea, 0x17, 0xb1, 0xa6, 0x44, 0x16, 0xeb, 0xb3, 0x9e, 0xdc, + 0xa1, 0x2a, 0x16, 0x43, 0xfb, 0xbf, 0x06, 0x2c, 0x8d, 0x1d, 0xf7, 0x77, 0x6a, 0xcb, 0x07, 0xb0, + 0xe0, 0x93, 0x13, 0xa2, 0x3a, 0xda, 0x3c, 0x56, 0x84, 0x98, 0x65, 0xcf, 0x43, 0xca, 0xa5, 0x72, + 0x55, 0xac, 0x08, 0xa1, 0x73, 0x97, 0x70, 0xd7, 0xf3, 0x65, 0x5e, 0xaa, 0x62, 0x4d, 0x09, 0x9d, + 0x07, 0xd4, 0xd7, 0x35, 0xba, 0x18, 0x22, 0x1b, 0x0a, 0x5e, 0x70, 0x14, 0xea, 0xb0, 0x91, 0x95, + 0x8d, 0xaa, 0xf5, 0x76, 0x83, 0xa3, 0x10, 0xcb, 0x35, 0x74, 0x19, 0x8a, 0xd4, 0x0d, 0x7a, 0x24, + 0x2e, 0xd0, 0xcb, 0x82, 0x0b, 0x8b, 0x19, 0xac, 0x17, 0x6c, 0x1b, 0xaa, 0xb2, 0x2b, 0x7e, 0x44, + 0x98, 0xe8, 0xc1, 0x44, 0x58, 0x77, 0x5d, 0xee, 0x4a, 0xb3, 0xab, 0x58, 0x8e, 0xed, 0xeb, 0x80, + 0x1e, 0x7a, 0x8c, 0x3f, 0x95, 0x6f, 0x0a, 0x6c, 0x56, 0xcb, 0xbc, 0x0f, 0x17, 0xc6, 0xb8, 0xf5, + 0xb5, 0xf0, 0xe3, 0x89, 0xa6, 0xf9, 0x4a, 0x3a, 0xe3, 0xca, 0xa7, 0x0b, 0x47, 0x01, 0x27, 0x7a, + 0xe7, 0x25, 0xa8, 0x48, 0xbb, 0xd4, 0xb7, 0x6d, 0x17, 0xaa, 0x8a, 0xd4, 0xc2, 0x3f, 0x87, 0xf7, + 0x63, 0x41, 0x5f, 0x10, 0x2a, 0xdb, 0x19, 0x43, 0xfa, 0xe5, 0x7b, 0xd3, 0xbe, 0xd2, 0x1c, 0x67, + 0xc7, 0x93, 0x78, 0x9b, 0xc0, 0x05, 0xc9, 0x73, 0xdf, 0x63, 0x3c, 0xa4, 0xa7, 0xb1, 0xd5, 0xab, + 0x00, 0xdb, 0x1d, 0xee, 0x9d, 0x90, 0xc7, 0x81, 0xaf, 0xae, 0xd1, 0x12, 0x4e, 0xcc, 0xc4, 0x57, + 0x64, 0x6e, 0xd4, 0x39, 0x5e, 0x82, 0x72, 0xdb, 0xa5, 0xfe, 0x69, 0xfb, 0x85, 0xc7, 0x75, 0x03, + 0x3f, 0x9a, 0xb0, 0x7f, 0x6f, 0xc0, 0x4a, 0xf2, 0x3b, 0xed, 0x13, 0x91, 0x2e, 0xee, 0x40, 0x81, + 0xc7, 0x75, 0xcc, 0x72, 0x96, 0x11, 0x29, 0x88, 0x28, 0x75, 0xb0, 0x04, 0x25, 0x3c, 0xad, 0x0e, + 0xce, 0x95, 0xb3, 0xe1, 0x13, 0x9e, 0xfe, 0x5f, 0x09, 0x50, 0x7a, 0x39, 0xa3, 0x23, 0x4e, 0x36, + 0x88, 0xb9, 0x89, 0x06, 0xf1, 0xd9, 0x64, 0x83, 0xa8, 0xae, 0xe6, 0x5b, 0xf3, 0x68, 0x32, 0x47, + 0x9b, 0x38, 0xd6, 0xc7, 0x14, 0xce, 0xd1, 0xc7, 0xa0, 0x8d, 0xf8, 0xc6, 0x51, 0x77, 0x1d, 0x8a, + 0x73, 0x0a, 0x8d, 0x3a, 0x8e, 0xae, 0x2b, 0xf4, 0x2d, 0x74, 0xf7, 0x7c, 0xaf, 0x25, 0x85, 0xc9, + 0x97, 0x92, 0x26, 0x54, 0x76, 0xe2, 0x44, 0x79, 0x8e, 0xa7, 0x92, 0x24, 0x08, 0x6d, 0xea, 0xc2, + 0x46, 0xa5, 0xe6, 0x4b, 0x69, 0x13, 0xe3, 0x67, 0x91, 0x90, 0xea, 0xca, 0xe6, 0x28, 0xa3, 0xb4, + 0x2c, 0x4b, 0x07, 0x6d, 0xcd, 0xe5, 0xfb, 0x39, 0xeb, 0x4b, 0xf4, 0x29, 0x14, 0x31, 0x61, 0x03, + 0x9f, 0xcb, 0xf7, 0x97, 0x4a, 0xe3, 0xf2, 0x14, 0xe9, 0x8a, 0x49, 0x9e, 0x55, 0x0d, 0x40, 0x3f, + 0x87, 0x45, 0x35, 0x62, 0x66, 0x65, 0xda, 0xb3, 0x41, 0x86, 0x66, 0x1a, 0xa3, 0x1b, 0x0a, 0x4d, + 0x89, 0xe3, 0xf8, 0x19, 0x09, 0x88, 0x7e, 0x17, 0x14, 0xad, 0xf1, 0x02, 0x4e, 0xcc, 0xa0, 0x06, + 0x2c, 0x70, 0xea, 0x76, 0x88, 0xb9, 0x34, 0x87, 0x0b, 0x15, 0xab, 0x48, 0x6c, 0x91, 0x17, 0x04, + 0xa4, 0x6b, 0x2e, 0xab, 0x4a, 0x49, 0x51, 0xe8, 0xbb, 0xb0, 0x1c, 0x0c, 0xfa, 0xb2, 0x59, 0xe8, + 0xee, 0x73, 0x12, 0x31, 0xf3, 0x7d, 0xf9, 0xbd, 0x89, 0x59, 0x74, 0x05, 0x96, 0x82, 0x41, 0xff, + 0x40, 0xdc, 0xf0, 0x8a, 0xad, 0x26, 0xd9, 0xc6, 0x27, 0xd1, 0x75, 0x58, 0x11, 0xb8, 0x78, 0xb7, + 0x15, 0xe7, 0x8a, 0xe4, 0x4c, 0x2f, 0xbc, 0x83, 0x9e, 0xf9, 0x5d, 0x74, 0x04, 0xd6, 0x33, 0xa8, + 0x26, 0xf7, 0x21, 0x03, 0x7b, 0x6b, 0xbc, 0xe3, 0x9e, 0x23, 0x2e, 0x12, 0x0d, 0xc7, 0x33, 0xf8, + 0xf6, 0x93, 0xa8, 0xeb, 0x72, 0x92, 0x95, 0x79, 0xd3, 0x19, 0xe8, 0x22, 0x14, 0xf7, 0xd4, 0x46, + 0xa9, 0xf7, 0x52, 0x4d, 0x89, 0xf9, 0x16, 0x11, 0xce, 0xd3, 0xe9, 0x56, 0x53, 0xf6, 0x25, 0xb0, + 0xb2, 0xc4, 0x2b, 0x67, 0xd8, 0x7f, 0xca, 0x01, 0x8c, 0x82, 0x01, 0x7d, 0x04, 0xd0, 0x27, 0x5d, + 0xcf, 0xfd, 0x15, 0x1f, 0x35, 0x94, 0x65, 0x39, 0x23, 0xbb, 0xca, 0x51, 0xe9, 0x9f, 0x7b, 0xeb, + 0xd2, 0x1f, 0x41, 0x81, 0x79, 0x5f, 0x13, 0x5d, 0xa6, 0xc8, 0x31, 0x7a, 0x0c, 0x15, 0x37, 0x08, + 0x42, 0x2e, 0xc3, 0x38, 0x6e, 0xb6, 0x6f, 0x9c, 0x15, 0xbe, 0xce, 0xf6, 0x88, 0x5f, 0x9d, 0x92, + 0xa4, 0x04, 0xeb, 0x2e, 0xd4, 0x26, 0x19, 0xce, 0xd5, 0x0c, 0xfe, 0x2d, 0x07, 0xef, 0x4f, 0x6c, + 0x1d, 0xba, 0x0f, 0x35, 0x45, 0x4d, 0x3c, 0x90, 0xcc, 0x3a, 0x68, 0x29, 0x14, 0xba, 0x07, 0xd5, + 0x6d, 0xce, 0x45, 0x26, 0x54, 0xf6, 0xaa, 0x16, 0xf0, 0x6c, 0x29, 0x63, 0x08, 0x74, 0x7f, 0x94, + 0x56, 0xf2, 0xd3, 0x1a, 0xfd, 0x09, 0xfd, 0xb3, 0x73, 0x8a, 0xf5, 0x8b, 0x99, 0x41, 0xde, 0x18, + 0x0f, 0xf2, 0x19, 0x59, 0x25, 0xf1, 0xc8, 0x64, 0x40, 0x29, 0x3e, 0x84, 0x99, 0x6f, 0x15, 0x77, + 0xc6, 0xdf, 0x2a, 0xae, 0x4e, 0xbf, 0xd4, 0xd2, 0x4f, 0x14, 0xe8, 0x22, 0xe4, 0x3c, 0xfd, 0xc4, + 0xdc, 0x2c, 0xbe, 0x7e, 0xb5, 0x96, 0xdb, 0x6d, 0xe1, 0x9c, 0xd7, 0x7d, 0xf3, 0xa7, 0x8b, 0x6b, + 0x3f, 0x85, 0x0f, 0x33, 0x0b, 0x0d, 0x54, 0x81, 0xc5, 0xfd, 0x83, 0x6d, 0x7c, 0xd0, 0x6e, 0xd5, + 0xde, 0x43, 0x55, 0x28, 0xed, 0x3c, 0x7e, 0xb4, 0xf7, 0xb0, 0x7d, 0xd0, 0xae, 0x19, 0x62, 0xa9, + 0xd5, 0x16, 0xe3, 0x56, 0x2d, 0xd7, 0xf8, 0x5d, 0x11, 0x16, 0x77, 0xd4, 0x3f, 0x66, 0xe8, 0x00, + 0xca, 0xc3, 0xbf, 0x52, 0x90, 0x9d, 0xe1, 0xb2, 0x89, 0xff, 0x64, 0xac, 0x8f, 0xcf, 0xe4, 0xd1, + 0x17, 0xd1, 0x7d, 0x58, 0x90, 0x7f, 0x2a, 0xa1, 0x8c, 0xe7, 0x82, 0xe4, 0xbf, 0x4d, 0xd6, 0xd9, + 0x7f, 0xd2, 0x6c, 0x1a, 0x42, 0x92, 0x7c, 0x6b, 0xc9, 0x92, 0x94, 0x7c, 0xd0, 0xb5, 0xd6, 0x66, + 0x3c, 0xd2, 0xa0, 0x47, 0x50, 0xd4, 0x0d, 0x68, 0x16, 0x6b, 0xf2, 0x45, 0xc5, 0x5a, 0x9f, 0xce, + 0xa0, 0x84, 0x6d, 0x1a, 0xe8, 0xd1, 0xf0, 0x8d, 0x3e, 0x4b, 0xb5, 0x64, 0xf5, 0x6e, 0xcd, 0x58, + 0xdf, 0x30, 0x36, 0x0d, 0xf4, 0x25, 0x54, 0x12, 0xf5, 0x39, 0xca, 0xa8, 0x0e, 0xd3, 0xc5, 0xbe, + 0x75, 0x75, 0x06, 0x97, 0xb6, 0xbc, 0x0d, 0x05, 0x99, 0x18, 0x32, 0x9c, 0x9d, 0x28, 0xdf, 0xb3, + 0xd4, 0x1c, 0x2b, 0xe7, 0x0f, 0x55, 0xc3, 0x41, 0x82, 0x64, 0xf4, 0xa1, 0xab, 0xb3, 0xea, 0x84, + 0xa9, 0x61, 0x93, 0x0a, 0xe2, 0x4d, 0x03, 0x85, 0x80, 0xd2, 0x97, 0x01, 0xfa, 0x7e, 0x46, 0x94, + 0x4c, 0xbb, 0x91, 0xac, 0xeb, 0xf3, 0x31, 0x2b, 0xa3, 0x9a, 0xd5, 0x97, 0xaf, 0x57, 0x8d, 0x7f, + 0xbc, 0x5e, 0x35, 0xfe, 0xf5, 0x7a, 0xd5, 0x38, 0x2c, 0xca, 0x0a, 0xf0, 0x07, 0xff, 0x0f, 0x00, + 0x00, 0xff, 0xff, 0xc2, 0xc1, 0xf2, 0x3d, 0x51, 0x1e, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -2892,6 +2928,20 @@ func (m *SolveRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if len(m.Exporters) > 0 { + for iNdEx := len(m.Exporters) - 1; iNdEx >= 0; iNdEx-- { + { + size, err := m.Exporters[iNdEx].MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x6a + } + } if m.SourcePolicy != nil { { size, err := m.SourcePolicy.MarshalToSizedBuffer(dAtA[:i]) @@ -2992,9 +3042,9 @@ func (m *SolveRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x2a } - if len(m.ExporterAttrs) > 0 { - for k := range m.ExporterAttrs { - v := m.ExporterAttrs[k] + if len(m.ExporterAttrsDeprecated) > 0 { + for k := range m.ExporterAttrsDeprecated { + v := m.ExporterAttrsDeprecated[k] baseI := i i -= len(v) copy(dAtA[i:], v) @@ -3011,10 +3061,10 @@ func (m *SolveRequest) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x22 } } - if len(m.Exporter) > 0 { - i -= len(m.Exporter) - copy(dAtA[i:], m.Exporter) - i = encodeVarintControl(dAtA, i, uint64(len(m.Exporter))) + if len(m.ExporterDeprecated) > 0 { + i -= len(m.ExporterDeprecated) + copy(dAtA[i:], m.ExporterDeprecated) + i = encodeVarintControl(dAtA, i, uint64(len(m.ExporterDeprecated))) i-- dAtA[i] = 0x1a } @@ -4339,6 +4389,32 @@ func (m *BuildResultInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if len(m.Results) > 0 { + for k := range m.Results { + v := m.Results[k] + baseI := i + if v != nil { + { + size, err := v.MarshalToSizedBuffer(dAtA[:i]) + if err != nil { + return 0, err + } + i -= size + i = encodeVarintControl(dAtA, i, uint64(size)) + } + i-- + dAtA[i] = 0x12 + } + i -= len(k) + copy(dAtA[i:], k) + i = encodeVarintControl(dAtA, i, uint64(len(k))) + i-- + dAtA[i] = 0xa + i = encodeVarintControl(dAtA, i, uint64(baseI-i)) + i-- + dAtA[i] = 0x1a + } + } if len(m.Attestations) > 0 { for iNdEx := len(m.Attestations) - 1; iNdEx >= 0; iNdEx-- { { @@ -4353,9 +4429,9 @@ func (m *BuildResultInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { dAtA[i] = 0x12 } } - if m.Result != nil { + if m.ResultDeprecated != nil { { - size, err := m.Result.MarshalToSizedBuffer(dAtA[:i]) + size, err := m.ResultDeprecated.MarshalToSizedBuffer(dAtA[:i]) if err != nil { return 0, err } @@ -4392,6 +4468,13 @@ func (m *Exporter) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } + if len(m.ID) > 0 { + i -= len(m.ID) + copy(dAtA[i:], m.ID) + i = encodeVarintControl(dAtA, i, uint64(len(m.ID))) + i-- + dAtA[i] = 0x1a + } if len(m.Attrs) > 0 { for k := range m.Attrs { v := m.Attrs[k] @@ -4564,12 +4647,12 @@ func (m *SolveRequest) Size() (n int) { l = m.Definition.Size() n += 1 + l + sovControl(uint64(l)) } - l = len(m.Exporter) + l = len(m.ExporterDeprecated) if l > 0 { n += 1 + l + sovControl(uint64(l)) } - if len(m.ExporterAttrs) > 0 { - for k, v := range m.ExporterAttrs { + if len(m.ExporterAttrsDeprecated) > 0 { + for k, v := range m.ExporterAttrsDeprecated { _ = k _ = v mapEntrySize := 1 + len(k) + sovControl(uint64(len(k))) + 1 + len(v) + sovControl(uint64(len(v))) @@ -4620,6 +4703,12 @@ func (m *SolveRequest) Size() (n int) { l = m.SourcePolicy.Size() n += 1 + l + sovControl(uint64(l)) } + if len(m.Exporters) > 0 { + for _, e := range m.Exporters { + l = e.Size() + n += 1 + l + sovControl(uint64(l)) + } + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -5203,8 +5292,8 @@ func (m *BuildResultInfo) Size() (n int) { } var l int _ = l - if m.Result != nil { - l = m.Result.Size() + if m.ResultDeprecated != nil { + l = m.ResultDeprecated.Size() n += 1 + l + sovControl(uint64(l)) } if len(m.Attestations) > 0 { @@ -5213,6 +5302,19 @@ func (m *BuildResultInfo) Size() (n int) { n += 1 + l + sovControl(uint64(l)) } } + if len(m.Results) > 0 { + for k, v := range m.Results { + _ = k + _ = v + l = 0 + if v != nil { + l = v.Size() + l += 1 + sovControl(uint64(l)) + } + mapEntrySize := 1 + len(k) + sovControl(uint64(len(k))) + l + n += mapEntrySize + 1 + sovControl(uint64(mapEntrySize)) + } + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -5237,6 +5339,10 @@ func (m *Exporter) Size() (n int) { n += mapEntrySize + 1 + sovControl(uint64(mapEntrySize)) } } + l = len(m.ID) + if l > 0 { + n += 1 + l + sovControl(uint64(l)) + } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -6035,7 +6141,7 @@ func (m *SolveRequest) Unmarshal(dAtA []byte) error { iNdEx = postIndex case 3: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Exporter", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ExporterDeprecated", wireType) } var stringLen uint64 for shift := uint(0); ; shift += 7 { @@ -6063,11 +6169,11 @@ func (m *SolveRequest) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - m.Exporter = string(dAtA[iNdEx:postIndex]) + m.ExporterDeprecated = string(dAtA[iNdEx:postIndex]) iNdEx = postIndex case 4: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field ExporterAttrs", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ExporterAttrsDeprecated", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -6094,8 +6200,8 @@ func (m *SolveRequest) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.ExporterAttrs == nil { - m.ExporterAttrs = make(map[string]string) + if m.ExporterAttrsDeprecated == nil { + m.ExporterAttrsDeprecated = make(map[string]string) } var mapkey string var mapvalue string @@ -6190,7 +6296,7 @@ func (m *SolveRequest) Unmarshal(dAtA []byte) error { iNdEx += skippy } } - m.ExporterAttrs[mapkey] = mapvalue + m.ExporterAttrsDeprecated[mapkey] = mapvalue iNdEx = postIndex case 5: if wireType != 2 { @@ -6633,6 +6739,40 @@ func (m *SolveRequest) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 13: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Exporters", 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 + } + m.Exporters = append(m.Exporters, &Exporter{}) + if err := m.Exporters[len(m.Exporters)-1].Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + return err + } + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipControl(dAtA[iNdEx:]) @@ -10589,7 +10729,7 @@ func (m *BuildResultInfo) Unmarshal(dAtA []byte) error { switch fieldNum { case 1: if wireType != 2 { - return fmt.Errorf("proto: wrong wireType = %d for field Result", wireType) + return fmt.Errorf("proto: wrong wireType = %d for field ResultDeprecated", wireType) } var msglen int for shift := uint(0); ; shift += 7 { @@ -10616,10 +10756,10 @@ func (m *BuildResultInfo) Unmarshal(dAtA []byte) error { if postIndex > l { return io.ErrUnexpectedEOF } - if m.Result == nil { - m.Result = &Descriptor{} + if m.ResultDeprecated == nil { + m.ResultDeprecated = &Descriptor{} } - if err := m.Result.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { + if err := m.ResultDeprecated.Unmarshal(dAtA[iNdEx:postIndex]); err != nil { return err } iNdEx = postIndex @@ -10657,6 +10797,135 @@ func (m *BuildResultInfo) Unmarshal(dAtA []byte) error { return err } iNdEx = postIndex + case 3: + if wireType != 2 { + return fmt.Errorf("proto: wrong wireType = %d for field Results", 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.Results == nil { + m.Results = make(map[string]*Descriptor) + } + var mapkey string + var mapvalue *Descriptor + for iNdEx < postIndex { + entryPreIndex := iNdEx + var wire uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + wire |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + fieldNum := int32(wire >> 3) + if fieldNum == 1 { + var stringLenmapkey uint64 + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + stringLenmapkey |= uint64(b&0x7F) << shift + if b < 0x80 { + break + } + } + intStringLenmapkey := int(stringLenmapkey) + if intStringLenmapkey < 0 { + return ErrInvalidLengthControl + } + postStringIndexmapkey := iNdEx + intStringLenmapkey + if postStringIndexmapkey < 0 { + return ErrInvalidLengthControl + } + if postStringIndexmapkey > l { + return io.ErrUnexpectedEOF + } + mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) + iNdEx = postStringIndexmapkey + } else if fieldNum == 2 { + var mapmsglen int + for shift := uint(0); ; shift += 7 { + if shift >= 64 { + return ErrIntOverflowControl + } + if iNdEx >= l { + return io.ErrUnexpectedEOF + } + b := dAtA[iNdEx] + iNdEx++ + mapmsglen |= int(b&0x7F) << shift + if b < 0x80 { + break + } + } + if mapmsglen < 0 { + return ErrInvalidLengthControl + } + postmsgIndex := iNdEx + mapmsglen + if postmsgIndex < 0 { + return ErrInvalidLengthControl + } + if postmsgIndex > l { + return io.ErrUnexpectedEOF + } + mapvalue = &Descriptor{} + if err := mapvalue.Unmarshal(dAtA[iNdEx:postmsgIndex]); err != nil { + return err + } + iNdEx = postmsgIndex + } else { + iNdEx = entryPreIndex + skippy, err := skipControl(dAtA[iNdEx:]) + if err != nil { + return err + } + if (skippy < 0) || (iNdEx+skippy) < 0 { + return ErrInvalidLengthControl + } + if (iNdEx + skippy) > postIndex { + return io.ErrUnexpectedEOF + } + iNdEx += skippy + } + } + m.Results[mapkey] = mapvalue + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipControl(dAtA[iNdEx:]) @@ -10867,6 +11136,38 @@ func (m *Exporter) Unmarshal(dAtA []byte) error { } m.Attrs[mapkey] = mapvalue iNdEx = postIndex + case 3: + 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 ErrIntOverflowControl + } + 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 ErrInvalidLengthControl + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthControl + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = string(dAtA[iNdEx:postIndex]) + 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 ccc9980de3a0..b863d478842d 100644 --- a/api/services/control/control.proto +++ b/api/services/control/control.proto @@ -60,8 +60,11 @@ message UsageRecord { message SolveRequest { string Ref = 1; pb.Definition Definition = 2; - string Exporter = 3; - map ExporterAttrs = 4; + // ExporterDeprecated and ExporterAttrsDeprecated are deprecated in favor + // of the new Exporters. If these fields are set, then they will be + // appended to the Exporters field if Exporters was not explicitly set. + string ExporterDeprecated = 3; + map ExporterAttrsDeprecated = 4; string Session = 5; string Frontend = 6; map FrontendAttrs = 7; @@ -70,6 +73,7 @@ message SolveRequest { map FrontendInputs = 10; bool Internal = 11; // Internal builds are not recorded in build history moby.buildkit.v1.sourcepolicy.Policy SourcePolicy = 12; + repeated Exporter Exporters = 13; } message CacheOptions { @@ -227,11 +231,17 @@ message Descriptor { } message BuildResultInfo { - Descriptor Result = 1; + Descriptor ResultDeprecated = 1; repeated Descriptor Attestations = 2; + map Results = 3; } +// Exporter describes the output exporter message Exporter { + // Type identifies the exporter string Type = 1; + // Attrs specifies exporter configuration map Attrs = 2; + // ID identifies the exporter in the wire protocol + string id = 3 [(gogoproto.customname) = "ID"]; } diff --git a/client/client_test.go b/client/client_test.go index 5eab27b7ec81..150ad37af4a8 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -153,6 +153,7 @@ var allTests = []func(t *testing.T, sb integration.Sandbox){ testTarExporterWithSocketCopy, testTarExporterSymlink, testMultipleRegistryCacheImportExport, + testMultipleExporters, testSourceMap, testSourceMapFromRef, testLazyImagePush, @@ -2570,6 +2571,78 @@ func testUser(t *testing.T, sb integration.Sandbox) { checkAllReleasable(t, c, sb, true) } +func testMultipleExporters(t *testing.T, sb integration.Sandbox) { + requiresLinux(t) + + c, err := New(sb.Context(), sb.Address()) + require.NoError(t, err) + defer c.Close() + + def, err := llb.Scratch().File(llb.Mkfile("foo.txt", 0o755, nil)).Marshal(context.TODO()) + require.NoError(t, err) + + destDir, destDir2 := t.TempDir(), t.TempDir() + out := filepath.Join(destDir, "out.tar") + outW, err := os.Create(out) + require.NoError(t, err) + defer outW.Close() + + out2 := filepath.Join(destDir, "out2.tar") + outW2, err := os.Create(out2) + require.NoError(t, err) + defer outW2.Close() + + registry, err := sb.NewRegistry() + if errors.Is(err, integration.ErrRequirements) { + t.Skip(err.Error()) + } + require.NoError(t, err) + + target1, target2 := registry+"/buildkit/build/exporter:image", + registry+"/buildkit/build/alternative:image" + + imageExporter := ExporterImage + if workers.IsTestDockerd() { + imageExporter = "moby" + } + + resp, err := c.Solve(sb.Context(), def, SolveOpt{ + Exports: []ExportEntry{ + // Ensure that multiple local exporter destinations are written properly + { + Type: ExporterLocal, + OutputDir: destDir, + }, + { + Type: ExporterLocal, + OutputDir: destDir2, + }, + // Ensure that multiple instances of the same exporter are possible + { + Type: ExporterTar, + Output: fixedWriteCloser(outW), + }, + { + Type: ExporterTar, + Output: fixedWriteCloser(outW2), + }, + { + Type: imageExporter, + Attrs: map[string]string{ + "name": strings.Join([]string{target1, target2}, ","), + }, + }, + }, + }, nil) + require.NoError(t, err) + + require.Equal(t, resp.ExporterResponse["image.name"], target1+","+target2) + require.FileExists(t, filepath.Join(destDir, "out.tar")) + require.FileExists(t, filepath.Join(destDir, "out2.tar")) + require.FileExists(t, filepath.Join(destDir, "foo.txt")) + require.FileExists(t, filepath.Join(destDir2, "foo.txt")) +} + func testOCIExporter(t *testing.T, sb integration.Sandbox) { workers.CheckFeatureCompat(t, sb, workers.FeatureOCIExporter) requiresLinux(t) @@ -6980,7 +7053,7 @@ func testMergeOpCache(t *testing.T, sb integration.Sandbox, mode string) { for i, layer := range manifest.Layers { _, err = contentStore.Info(ctx, layer.Digest) - require.ErrorIs(t, err, ctderrdefs.ErrNotFound, "unexpected error %v for index %d", err, i) + require.ErrorIs(t, err, ctderrdefs.ErrNotFound, "unexpected error %v for index %d (%s)", err, i, layer.Digest) } // re-run the build with a change only to input1 using the remote cache diff --git a/client/solve.go b/client/solve.go index be214e55fdd6..386777285f04 100644 --- a/client/solve.go +++ b/client/solve.go @@ -4,6 +4,7 @@ import ( "context" "encoding/base64" "encoding/json" + "fmt" "io" "os" "path/filepath" @@ -130,12 +131,23 @@ func (c *Client) solve(ctx context.Context, def *llb.Definition, runGateway runG return nil, err } - var ex ExportEntry - if len(opt.Exports) > 1 { - return nil, errors.New("currently only single Exports can be specified") + type exporter struct { + ExportEntry + id string } - if len(opt.Exports) == 1 { - ex = opt.Exports[0] + + var exporters []exporter + ids := make(map[string]int) + for _, exp := range opt.Exports { + if id, ok := ids[exp.Type]; !ok { + ids[exp.Type] = 1 + } else { + ids[exp.Type] = id + 1 + } + exporters = append(exporters, exporter{ + ExportEntry: exp, + id: fmt.Sprint(exp.Type, ids[exp.Type]), + }) } storesToUpdate := []string{} @@ -161,51 +173,52 @@ func (c *Client) solve(ctx context.Context, def *llb.Definition, runGateway runG contentStores[key2] = store } - var supportFile bool - var supportDir bool - switch ex.Type { - case ExporterLocal: - supportDir = true - case ExporterTar: - supportFile = true - case ExporterOCI, ExporterDocker: - supportDir = ex.OutputDir != "" - supportFile = ex.Output != nil - } - - if supportFile && supportDir { - return nil, errors.Errorf("both file and directory output is not supported by %s exporter", ex.Type) - } - if !supportFile && ex.Output != nil { - return nil, errors.Errorf("output file writer is not supported by %s exporter", ex.Type) - } - if !supportDir && ex.OutputDir != "" { - return nil, errors.Errorf("output directory is not supported by %s exporter", ex.Type) - } - - if supportFile { - if ex.Output == nil { - return nil, errors.Errorf("output file writer is required for %s exporter", ex.Type) - } - s.Allow(filesync.NewFSSyncTarget(filesync.WithFSSync("", ex.Output))) - } - if supportDir { - if ex.OutputDir == "" { - return nil, errors.Errorf("output directory is required for %s exporter", ex.Type) - } + var syncTargets []filesync.FSSyncTarget + for _, ex := range exporters { + var supportFile bool + var supportDir bool switch ex.Type { + case ExporterLocal: + supportDir = true + case ExporterTar: + supportFile = true case ExporterOCI, ExporterDocker: - if err := os.MkdirAll(ex.OutputDir, 0755); err != nil { - return nil, err + supportDir = ex.OutputDir != "" + supportFile = ex.Output != nil + } + if supportFile && supportDir { + return nil, errors.Errorf("both file and directory output is not supported by %s exporter", ex.Type) + } + if !supportFile && ex.Output != nil { + return nil, errors.Errorf("output file writer is not supported by %s exporter", ex.Type) + } + if !supportDir && ex.OutputDir != "" { + return nil, errors.Errorf("output directory is not supported by %s exporter", ex.Type) + } + if supportFile { + if ex.Output == nil { + return nil, errors.Errorf("output file writer is required for %s exporter", ex.Type) } - cs, err := contentlocal.NewStore(ex.OutputDir) - if err != nil { - return nil, err + syncTargets = append(syncTargets, filesync.WithFSSync(ex.id, ex.Output)) + } + if supportDir { + if ex.OutputDir == "" { + return nil, errors.Errorf("output directory is required for %s exporter", ex.Type) + } + switch ex.Type { + case ExporterOCI, ExporterDocker: + if err := os.MkdirAll(ex.OutputDir, 0755); err != nil { + return nil, err + } + cs, err := contentlocal.NewStore(ex.OutputDir) + if err != nil { + return nil, err + } + contentStores["export"] = cs + storesToUpdate = append(storesToUpdate, ex.OutputDir) + default: + syncTargets = append(syncTargets, filesync.WithFSSyncDir(ex.id, ex.OutputDir)) } - contentStores["export"] = cs - storesToUpdate = append(storesToUpdate, ex.OutputDir) - default: - s.Allow(filesync.NewFSSyncTarget(filesync.WithFSSyncDir("", ex.OutputDir))) } } @@ -213,6 +226,10 @@ func (c *Client) solve(ctx context.Context, def *llb.Definition, runGateway runG s.Allow(sessioncontent.NewAttachable(contentStores)) } + if len(syncTargets) > 0 { + s.Allow(filesync.NewFSSyncTarget(syncTargets...)) + } + eg.Go(func() error { sd := c.sessionDialer if sd == nil { @@ -260,19 +277,35 @@ func (c *Client) solve(ctx context.Context, def *llb.Definition, runGateway runG frontendInputs[key] = def.ToPB() } + exports := make([]*controlapi.Exporter, 0, len(opt.Exports)) + exportDeprecated := "" + exportAttrDeprecated := map[string]string{} + for i, exp := range exporters { + if i == 0 { + exportDeprecated = exp.Type + exportAttrDeprecated = exp.Attrs + } + exports = append(exports, &controlapi.Exporter{ + ID: exp.id, + Type: exp.Type, + Attrs: exp.Attrs, + }) + } + resp, err := c.ControlClient().Solve(ctx, &controlapi.SolveRequest{ - Ref: ref, - Definition: pbd, - Exporter: ex.Type, - ExporterAttrs: ex.Attrs, - Session: s.ID(), - Frontend: opt.Frontend, - FrontendAttrs: frontendAttrs, - FrontendInputs: frontendInputs, - Cache: cacheOpt.options, - Entitlements: opt.AllowedEntitlements, - Internal: opt.Internal, - SourcePolicy: opt.SourcePolicy, + Ref: ref, + Definition: pbd, + Exporters: exports, + ExporterDeprecated: exportDeprecated, + ExporterAttrsDeprecated: exportAttrDeprecated, + Session: s.ID(), + Frontend: opt.Frontend, + FrontendAttrs: frontendAttrs, + FrontendInputs: frontendInputs, + Cache: cacheOpt.options, + Entitlements: opt.AllowedEntitlements, + Internal: opt.Internal, + SourcePolicy: opt.SourcePolicy, }) if err != nil { return errors.Wrap(err, "failed to solve") diff --git a/cmd/buildctl/debug/monitor.go b/cmd/buildctl/debug/monitor.go index 2cf83e9326bf..0ecbe9284c41 100644 --- a/cmd/buildctl/debug/monitor.go +++ b/cmd/buildctl/debug/monitor.go @@ -59,16 +59,24 @@ func monitor(clicontext *cli.Context) error { } if ev.Record.Result != nil { - if ev.Record.Result.Result != nil { - fmt.Printf(" descriptor: %s\n", ev.Record.Result.Result) + if len(ev.Record.Result.Results) > 0 { + for _, res := range ev.Record.Result.Results { + fmt.Printf(" descriptor: %s\n", res) + } + } else if ev.Record.Result.ResultDeprecated != nil { + fmt.Printf(" descriptor: %s\n", ev.Record.Result.ResultDeprecated) } for _, att := range ev.Record.Result.Attestations { fmt.Printf(" attestation: %s\n", att) } } for k, res := range ev.Record.Results { - if res.Result != nil { - fmt.Printf(" [%s] descriptor: %s\n", k, res.Result) + if len(ev.Record.Result.Results) > 0 { + for _, res := range res.Results { + fmt.Printf(" [%s] descriptor: %s\n", k, res) + } + } else if res.ResultDeprecated != nil { + fmt.Printf(" [%s] descriptor: %s\n", k, res.ResultDeprecated) } for _, att := range res.Attestations { fmt.Printf(" [%s] attestation: %s\n", k, att) diff --git a/control/control.go b/control/control.go index ac09ec5fb525..1901c6f7ab66 100644 --- a/control/control.go +++ b/control/control.go @@ -313,6 +313,7 @@ func translateLegacySolveRequest(req *controlapi.SolveRequest) { req.Cache.ExportRefDeprecated = "" req.Cache.ExportAttrsDeprecated = nil } + // translates ImportRefs to new Imports (v0.4.0) for _, legacyImportRef := range req.Cache.ImportRefsDeprecated { im := &controlapi.CacheOptionsEntry{ @@ -323,6 +324,16 @@ func translateLegacySolveRequest(req *controlapi.SolveRequest) { req.Cache.Imports = append(req.Cache.Imports, im) } req.Cache.ImportRefsDeprecated = nil + + // translate single exporter to a slice (v0.12.0) + if len(req.Exporters) == 0 && req.ExporterDeprecated != "" { + req.Exporters = append(req.Exporters, &controlapi.Exporter{ + Type: req.ExporterDeprecated, + Attrs: req.ExporterAttrsDeprecated, + }) + req.ExporterDeprecated = "" + req.ExporterAttrsDeprecated = nil + } } func (c *Controller) Solve(ctx context.Context, req *controlapi.SolveRequest) (*controlapi.SolveResponse, error) { @@ -335,7 +346,6 @@ func (c *Controller) Solve(ctx context.Context, req *controlapi.SolveRequest) (* time.AfterFunc(time.Second, c.throttledGC) }() - var expi exporter.ExporterInstance // TODO: multiworker // This is actually tricky, as the exporter should come from the worker that has the returned reference. We may need to delay this so that the solver loads this. w, err := c.opt.WorkerController.GetDefault() @@ -343,25 +353,29 @@ func (c *Controller) Solve(ctx context.Context, req *controlapi.SolveRequest) (* return nil, err } - // if SOURCE_DATE_EPOCH is set, enable it for the exporter + // if SOURCE_DATE_EPOCH is set, enable it for the exporters if v, ok := epoch.ParseBuildArgs(req.FrontendAttrs); ok { - if _, ok := req.ExporterAttrs[string(exptypes.OptKeySourceDateEpoch)]; !ok { - if req.ExporterAttrs == nil { - req.ExporterAttrs = make(map[string]string) + for _, ex := range req.Exporters { + if _, ok := ex.Attrs[string(exptypes.OptKeySourceDateEpoch)]; !ok { + if ex.Attrs == nil { + ex.Attrs = make(map[string]string) + } + ex.Attrs[string(exptypes.OptKeySourceDateEpoch)] = v } - req.ExporterAttrs[string(exptypes.OptKeySourceDateEpoch)] = v } } - if req.Exporter != "" { - exp, err := w.Exporter(req.Exporter, c.opt.SessionManager) + var expis []exporter.ExporterInstance + for _, ex := range req.Exporters { + exp, err := w.Exporter(ex.Type, c.opt.SessionManager) if err != nil { return nil, err } - expi, err = exp.Resolve(ctx, "", req.ExporterAttrs) + expi, err := exp.Resolve(ctx, ex.ID, ex.Attrs) if err != nil { return nil, err } + expis = append(expis, expi) } if c, err := findDuplicateCacheOptions(req.Cache.Exports); err != nil { @@ -453,10 +467,8 @@ func (c *Controller) Solve(ctx context.Context, req *controlapi.SolveRequest) (* FrontendInputs: req.FrontendInputs, CacheImports: cacheImports, }, llbsolver.ExporterRequest{ - Exporter: expi, + Exporters: expis, CacheExporters: cacheExporters, - Type: req.Exporter, - Attrs: req.ExporterAttrs, }, req.Entitlements, procs, req.Internal, req.SourcePolicy) if err != nil { return nil, err diff --git a/solver/llbsolver/history.go b/solver/llbsolver/history.go index e3a69f7dcd88..8a099ced0e46 100644 --- a/solver/llbsolver/history.go +++ b/solver/llbsolver/history.go @@ -526,9 +526,14 @@ func (h *HistoryQueue) update(ctx context.Context, rec controlapi.BuildHistoryRe return err } if rec.Result != nil { - if err := h.addResource(ctx, l, rec.Result.Result, true); err != nil { + if err := h.addResource(ctx, l, rec.Result.ResultDeprecated, true); err != nil { return err } + for _, res := range rec.Result.Results { + if err := h.addResource(ctx, l, res, true); err != nil { + return err + } + } for _, att := range rec.Result.Attestations { if err := h.addResource(ctx, l, att, false); err != nil { return err @@ -536,9 +541,14 @@ func (h *HistoryQueue) update(ctx context.Context, rec controlapi.BuildHistoryRe } } for _, r := range rec.Results { - if err := h.addResource(ctx, l, r.Result, true); err != nil { + if err := h.addResource(ctx, l, r.ResultDeprecated, true); err != nil { return err } + for _, res := range r.Results { + if err := h.addResource(ctx, l, res, true); err != nil { + return err + } + } for _, att := range r.Attestations { if err := h.addResource(ctx, l, att, false); err != nil { return err diff --git a/solver/llbsolver/solver.go b/solver/llbsolver/solver.go index d4c93f1610df..e13be476c89f 100644 --- a/solver/llbsolver/solver.go +++ b/solver/llbsolver/solver.go @@ -53,7 +53,7 @@ const ( type ExporterRequest struct { Type string Attrs map[string]string - Exporter exporter.ExporterInstance + Exporters []exporter.ExporterInstance CacheExporters []RemoteCacheExporter } @@ -149,7 +149,7 @@ func (s *Solver) Bridge(b solver.Builder) frontend.FrontendLLBBridge { return s.bridge(b) } -func (s *Solver) recordBuildHistory(ctx context.Context, id string, req frontend.SolveRequest, exp ExporterRequest, j *solver.Job, usage *resources.SysSampler) (func(*Result, exporter.DescriptorReference, error) error, error) { +func (s *Solver) recordBuildHistory(ctx context.Context, id string, req frontend.SolveRequest, exp ExporterRequest, j *solver.Job, usage *resources.SysSampler) (func(*Result, map[string]exporter.DescriptorReference, error) error, error) { var stopTrace func() []tracetest.SpanStub if s := trace.SpanFromContext(ctx); s.SpanContext().IsValid() { @@ -182,7 +182,7 @@ func (s *Solver) recordBuildHistory(ctx context.Context, id string, req frontend return nil, err } - return func(res *Result, descref exporter.DescriptorReference, err error) error { + return func(res *Result, descrefs map[string]exporter.DescriptorReference, err error) error { en := time.Now() rec.CompletedAt = &en @@ -314,19 +314,27 @@ func (s *Solver) recordBuildHistory(ctx context.Context, id string, req frontend return j.Status(ctx2, ch) }) - if descref != nil { + for k, descref := range descrefs { + k, descref := k, descref eg.Go(func() error { mu.Lock() if rec.Result == nil { rec.Result = &controlapi.BuildResultInfo{} } + if rec.Result.Results == nil { + rec.Result.Results = make(map[string]*controlapi.Descriptor) + } desc := descref.Descriptor() - rec.Result.Result = &controlapi.Descriptor{ + controlDesc := &controlapi.Descriptor{ Digest: desc.Digest, Size_: desc.Size, MediaType: desc.MediaType, Annotations: desc.Annotations, } + if rec.Result.ResultDeprecated == nil { + rec.Result.ResultDeprecated = controlDesc + } + rec.Result.Results[k] = controlDesc mu.Unlock() return nil }) @@ -425,14 +433,14 @@ func (s *Solver) Solve(ctx context.Context, id string, sessionID string, req fro var res *frontend.Result var resProv *Result - var descref exporter.DescriptorReference + var descrefs map[string]exporter.DescriptorReference var releasers []func() defer func() { for _, f := range releasers { f() } - if descref != nil { + for _, descref := range descrefs { descref.Release() } }() @@ -475,7 +483,7 @@ func (s *Solver) Solve(ctx context.Context, id string, sessionID string, req fro return nil, err1 } defer func() { - err = rec(resProv, descref, err) + err = rec(resProv, descrefs, err) }() } @@ -554,7 +562,7 @@ func (s *Solver) Solve(ctx context.Context, id string, sessionID string, req fro cacheExporters, inlineCacheExporter := splitCacheExporters(exp.CacheExporters) var exporterResponse map[string]string - exporterResponse, descref, err = s.runExporter(ctx, exp.Exporter, inlineCacheExporter, j, cached, inp) + exporterResponse, descrefs, err = s.runExporters(ctx, exp.Exporters, inlineCacheExporter, j, cached, inp) if err != nil { return nil, err } @@ -624,6 +632,9 @@ func runCacheExporters(ctx context.Context, exporters []RemoteCacheExporter, j * if err := eg.Wait(); err != nil { return nil, err } + + // TODO: separate these out, and return multiple cache exporter responses + // to the client for _, resp := range resps { for k, v := range resp { if cacheExporterResponse == nil { @@ -640,47 +651,67 @@ func runInlineCacheExporter(ctx context.Context, e exporter.ExporterInstance, in return nil, nil } - var res *result.Result[*exptypes.InlineCacheEntry] - if err := inBuilderContext(ctx, j, "preparing layers for inline cache", j.SessionID+"-cache-inline", func(ctx context.Context, _ session.Group) error { - var err error - res, err = result.ConvertResult(cached, func(res solver.CachedResult) (*exptypes.InlineCacheEntry, error) { - dtic, err := inlineCache(ctx, inlineExporter, res, e.Config().Compression(), session.NewGroup(j.SessionID)) - if err != nil { - return nil, err - } - if dtic == nil { - return nil, nil - } - return &exptypes.InlineCacheEntry{Data: dtic}, nil + done := progress.OneOff(ctx, "preparing layers for inline cache") + res, err := result.ConvertResult(cached, func(res solver.CachedResult) (*exptypes.InlineCacheEntry, error) { + dtic, err := inlineCache(ctx, inlineExporter, res, e.Config().Compression(), session.NewGroup(j.SessionID)) + if err != nil { + return nil, err + } + if dtic == nil { + return nil, nil + } + return &exptypes.InlineCacheEntry{Data: dtic}, nil + }) + return res, done(err) +} + +func (s *Solver) runExporters(ctx context.Context, exporters []exporter.ExporterInstance, inlineCacheExporter inlineCacheExporter, job *solver.Job, cached *result.Result[solver.CachedResult], inp *result.Result[cache.ImmutableRef]) (exporterResponse map[string]string, descrefs map[string]exporter.DescriptorReference, err error) { + eg, ctx := errgroup.WithContext(ctx) + resps := make([]map[string]string, len(exporters)) + descs := make([]exporter.DescriptorReference, len(exporters)) + for i, exp := range exporters { + i, exp := i, exp + eg.Go(func() error { + id := fmt.Sprint(job.SessionID, "-export-", i) + return inBuilderContext(ctx, job, exp.Name(), id, func(ctx context.Context, _ session.Group) error { + inlineCache := exptypes.InlineCache(func(ctx context.Context) (*result.Result[*exptypes.InlineCacheEntry], error) { + return runInlineCacheExporter(ctx, exp, inlineCacheExporter, job, cached) + }) + + resps[i], descs[i], err = exp.Export(ctx, inp, inlineCache, job.SessionID) + if err != nil { + return err + } + return nil + }) }) - return err - }); err != nil { - return nil, err } - return res, nil -} + if err := eg.Wait(); err != nil { + return nil, nil, err + } -func (s *Solver) runExporter(ctx context.Context, exp exporter.ExporterInstance, inlineCacheExporter inlineCacheExporter, job *solver.Job, cached *result.Result[solver.CachedResult], inp *result.Result[cache.ImmutableRef]) (exporterResponse map[string]string, descref exporter.DescriptorReference, err error) { - sessionID := job.SessionID - inlineCache := exptypes.InlineCache(func(ctx context.Context) (*result.Result[*exptypes.InlineCacheEntry], error) { - return runInlineCacheExporter(ctx, exp, inlineCacheExporter, job, cached) - }) + // TODO: separate these out, and return multiple exporter responses to the + // client + for _, resp := range resps { + for k, v := range resp { + if exporterResponse == nil { + exporterResponse = make(map[string]string) + } + exporterResponse[k] = v + } + } - err = inBuilderContext(ctx, job, exp.Name(), job.SessionID+"-export", func(ctx context.Context, _ session.Group) error { - var dref exporter.DescriptorReference - exporterResponse, dref, err = exp.Export(ctx, inp, inlineCache, sessionID) - if err != nil { - return err + for i, desc := range descs { + if desc == nil { + continue } - if dref != nil { - descref = dref + if descrefs == nil { + descrefs = make(map[string]exporter.DescriptorReference) } - return nil - }) - if err != nil { - return nil, nil, err + descrefs[exporters[i].ID()] = desc } - return exporterResponse, descref, nil + + return exporterResponse, descrefs, nil } func splitCacheExporters(exporters []RemoteCacheExporter) (rest []RemoteCacheExporter, inline inlineCacheExporter) { From 88221133afa0a228b3bacbbaaee6701fa9e8cf94 Mon Sep 17 00:00:00 2001 From: Justin Chadwell Date: Wed, 9 Aug 2023 16:24:06 +0100 Subject: [PATCH 10/12] solver: add cap for multiple exporters Higher-level clients should be able to check server-side caps to ensure that when they attempt to use multiple exporters, that the feature is actually supported. Signed-off-by: Justin Chadwell --- solver/pb/caps.go | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/solver/pb/caps.go b/solver/pb/caps.go index 5e1963ff8faa..5b99ab17d1b6 100644 --- a/solver/pb/caps.go +++ b/solver/pb/caps.go @@ -85,6 +85,8 @@ const ( // CapSourceDateEpoch is the capability to automatically handle the date epoch CapSourceDateEpoch apicaps.CapID = "exporter.sourcedateepoch" + CapMultipleExporters apicaps.CapID = "exporter.multiple" + CapSourcePolicy apicaps.CapID = "source.policy" ) @@ -454,6 +456,12 @@ func init() { Status: apicaps.CapStatusExperimental, }) + Caps.Init(apicaps.Cap{ + ID: CapMultipleExporters, + Enabled: true, + Status: apicaps.CapStatusExperimental, + }) + Caps.Init(apicaps.Cap{ ID: CapSourcePolicy, Enabled: true, From 1c1777b7c02f9b6d262845546468b138938d8864 Mon Sep 17 00:00:00 2001 From: Justin Chadwell Date: Wed, 13 Sep 2023 14:20:27 +0100 Subject: [PATCH 11/12] exporter: use implicit ids for exporters We can derive exporter ids from their place in the exporter array in a SolveRequest - this removes the need to manually generate and handle multiple sets of IDs. Signed-off-by: Justin Chadwell --- api/services/control/control.pb.go | 395 ++++++++++++----------------- api/services/control/control.proto | 4 +- client/client_test.go | 42 ++- client/solve.go | 29 +-- control/control.go | 4 +- exporter/containerimage/export.go | 6 +- exporter/exporter.go | 4 +- exporter/local/export.go | 6 +- exporter/oci/export.go | 6 +- exporter/tar/export.go | 6 +- session/filesync/filesync.go | 48 ++-- solver/llbsolver/solver.go | 53 ++-- 12 files changed, 267 insertions(+), 336 deletions(-) diff --git a/api/services/control/control.pb.go b/api/services/control/control.pb.go index cdfcb25c0501..3c24ebb5b5cc 100644 --- a/api/services/control/control.pb.go +++ b/api/services/control/control.pb.go @@ -1843,12 +1843,12 @@ func (m *Descriptor) GetAnnotations() map[string]string { } type BuildResultInfo struct { - ResultDeprecated *Descriptor `protobuf:"bytes,1,opt,name=ResultDeprecated,proto3" json:"ResultDeprecated,omitempty"` - Attestations []*Descriptor `protobuf:"bytes,2,rep,name=Attestations,proto3" json:"Attestations,omitempty"` - Results map[string]*Descriptor `protobuf:"bytes,3,rep,name=Results,proto3" json:"Results,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + ResultDeprecated *Descriptor `protobuf:"bytes,1,opt,name=ResultDeprecated,proto3" json:"ResultDeprecated,omitempty"` + Attestations []*Descriptor `protobuf:"bytes,2,rep,name=Attestations,proto3" json:"Attestations,omitempty"` + Results map[int64]*Descriptor `protobuf:"bytes,3,rep,name=Results,proto3" json:"Results,omitempty" protobuf_key:"varint,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *BuildResultInfo) Reset() { *m = BuildResultInfo{} } @@ -1898,7 +1898,7 @@ func (m *BuildResultInfo) GetAttestations() []*Descriptor { return nil } -func (m *BuildResultInfo) GetResults() map[string]*Descriptor { +func (m *BuildResultInfo) GetResults() map[int64]*Descriptor { if m != nil { return m.Results } @@ -1910,12 +1910,10 @@ type Exporter struct { // Type identifies the exporter Type string `protobuf:"bytes,1,opt,name=Type,proto3" json:"Type,omitempty"` // Attrs specifies exporter configuration - Attrs map[string]string `protobuf:"bytes,2,rep,name=Attrs,proto3" json:"Attrs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` - // ID identifies the exporter in the wire protocol - ID string `protobuf:"bytes,3,opt,name=id,proto3" json:"id,omitempty"` - XXX_NoUnkeyedLiteral struct{} `json:"-"` - XXX_unrecognized []byte `json:"-"` - XXX_sizecache int32 `json:"-"` + Attrs map[string]string `protobuf:"bytes,2,rep,name=Attrs,proto3" json:"Attrs,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + XXX_NoUnkeyedLiteral struct{} `json:"-"` + XXX_unrecognized []byte `json:"-"` + XXX_sizecache int32 `json:"-"` } func (m *Exporter) Reset() { *m = Exporter{} } @@ -1965,13 +1963,6 @@ func (m *Exporter) GetAttrs() map[string]string { return nil } -func (m *Exporter) GetID() string { - if m != nil { - return m.ID - } - return "" -} - func init() { proto.RegisterEnum("moby.buildkit.v1.BuildHistoryEventType", BuildHistoryEventType_name, BuildHistoryEventType_value) proto.RegisterType((*PruneRequest)(nil), "moby.buildkit.v1.PruneRequest") @@ -2010,7 +2001,7 @@ func init() { proto.RegisterType((*Descriptor)(nil), "moby.buildkit.v1.Descriptor") proto.RegisterMapType((map[string]string)(nil), "moby.buildkit.v1.Descriptor.AnnotationsEntry") proto.RegisterType((*BuildResultInfo)(nil), "moby.buildkit.v1.BuildResultInfo") - proto.RegisterMapType((map[string]*Descriptor)(nil), "moby.buildkit.v1.BuildResultInfo.ResultsEntry") + proto.RegisterMapType((map[int64]*Descriptor)(nil), "moby.buildkit.v1.BuildResultInfo.ResultsEntry") proto.RegisterType((*Exporter)(nil), "moby.buildkit.v1.Exporter") proto.RegisterMapType((map[string]string)(nil), "moby.buildkit.v1.Exporter.AttrsEntry") } @@ -2018,153 +2009,152 @@ func init() { func init() { proto.RegisterFile("control.proto", fileDescriptor_0c5120591600887d) } var fileDescriptor_0c5120591600887d = []byte{ - // 2331 bytes of a gzipped FileDescriptorProto + // 2315 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x59, 0x4f, 0x73, 0x1b, 0x49, - 0x15, 0xdf, 0x91, 0x64, 0x59, 0x7a, 0x92, 0xbd, 0x72, 0x67, 0x37, 0x0c, 0x43, 0xd6, 0x76, 0x66, - 0x13, 0x70, 0x85, 0x64, 0xe4, 0x15, 0x84, 0x64, 0x1d, 0x08, 0xb1, 0x2c, 0xb1, 0x71, 0x48, 0x2a, - 0xde, 0xb6, 0xb3, 0xa1, 0xb6, 0x2a, 0x50, 0x63, 0xa9, 0xad, 0x4c, 0x79, 0x34, 0x33, 0x74, 0xb7, - 0xbc, 0xf1, 0x9e, 0x38, 0x51, 0xc5, 0x85, 0xe2, 0xc6, 0x85, 0x3b, 0x27, 0xce, 0x9c, 0x39, 0x50, - 0x95, 0x23, 0xe7, 0x3d, 0x04, 0x2a, 0x1f, 0x80, 0xe2, 0x08, 0x37, 0xaa, 0xff, 0x8c, 0x34, 0xd2, - 0x8c, 0x2c, 0x39, 0xc9, 0x49, 0xfd, 0xba, 0xdf, 0xef, 0xcd, 0x7b, 0xaf, 0x5f, 0xbf, 0x7e, 0xaf, - 0x05, 0x4b, 0x9d, 0x30, 0xe0, 0x34, 0xf4, 0x9d, 0x88, 0x86, 0x3c, 0x44, 0xb5, 0x7e, 0x78, 0x78, - 0xea, 0x1c, 0x0e, 0x3c, 0xbf, 0x7b, 0xec, 0x71, 0xe7, 0xe4, 0x13, 0xab, 0xd1, 0xf3, 0xf8, 0xf3, - 0xc1, 0xa1, 0xd3, 0x09, 0xfb, 0xf5, 0x5e, 0xd8, 0x0b, 0xeb, 0xbd, 0x30, 0xec, 0xf9, 0xc4, 0x8d, - 0x3c, 0xa6, 0x87, 0x75, 0x1a, 0x75, 0xea, 0x8c, 0xbb, 0x7c, 0xc0, 0x94, 0x14, 0xeb, 0xc6, 0x24, - 0x46, 0x4e, 0x1f, 0x0e, 0x8e, 0x24, 0x25, 0x09, 0x39, 0xd2, 0xec, 0xf5, 0x04, 0xbb, 0xf8, 0x7e, - 0x3d, 0xfe, 0x7e, 0xdd, 0x8d, 0xbc, 0x3a, 0x3f, 0x8d, 0x08, 0xab, 0x7f, 0x15, 0xd2, 0x63, 0x42, - 0x35, 0xe0, 0xfa, 0x54, 0x00, 0x0b, 0xfd, 0x13, 0x42, 0xeb, 0xd1, 0x61, 0x3d, 0x8c, 0x62, 0x6d, - 0x6e, 0x9e, 0xc1, 0x3d, 0xa0, 0x1d, 0x12, 0x85, 0xbe, 0xd7, 0x39, 0x15, 0x18, 0x35, 0xd2, 0xb0, - 0x35, 0x6d, 0xdd, 0x50, 0x77, 0xee, 0xf5, 0x09, 0xe3, 0x6e, 0x3f, 0x52, 0x0c, 0xf6, 0x6f, 0x0d, - 0xa8, 0xee, 0xd1, 0x41, 0x40, 0x30, 0xf9, 0xf5, 0x80, 0x30, 0x8e, 0x2e, 0x42, 0xf1, 0xc8, 0xf3, - 0x39, 0xa1, 0xa6, 0xb1, 0x9e, 0xdf, 0x28, 0x63, 0x4d, 0xa1, 0x1a, 0xe4, 0x5d, 0xdf, 0x37, 0x73, - 0xeb, 0xc6, 0x46, 0x09, 0x8b, 0x21, 0xda, 0x80, 0xea, 0x31, 0x21, 0x51, 0x6b, 0x40, 0x5d, 0xee, - 0x85, 0x81, 0x99, 0x5f, 0x37, 0x36, 0xf2, 0xcd, 0xc2, 0xcb, 0x57, 0x6b, 0x06, 0x1e, 0x5b, 0x41, - 0x36, 0x94, 0x05, 0xdd, 0x3c, 0xe5, 0x84, 0x99, 0x85, 0x04, 0xdb, 0x68, 0xda, 0xbe, 0x06, 0xb5, - 0x96, 0xc7, 0x8e, 0x9f, 0x30, 0xb7, 0x37, 0x4b, 0x17, 0xfb, 0x01, 0xac, 0x24, 0x78, 0x59, 0x14, - 0x06, 0x8c, 0xa0, 0x9b, 0x50, 0xa4, 0xa4, 0x13, 0xd2, 0xae, 0x64, 0xae, 0x34, 0x3e, 0x72, 0x26, - 0xc3, 0xc0, 0xd1, 0x00, 0xc1, 0x84, 0x35, 0xb3, 0xfd, 0xc7, 0x3c, 0x54, 0x12, 0xf3, 0x68, 0x19, - 0x72, 0xbb, 0x2d, 0xd3, 0x58, 0x37, 0x36, 0xca, 0x38, 0xb7, 0xdb, 0x42, 0x26, 0x2c, 0x3e, 0x1a, - 0x70, 0xf7, 0xd0, 0x27, 0xda, 0xf6, 0x98, 0x44, 0x1f, 0xc0, 0xc2, 0x6e, 0xf0, 0x84, 0x11, 0x69, - 0x78, 0x09, 0x2b, 0x02, 0x21, 0x28, 0xec, 0x7b, 0x5f, 0x13, 0x65, 0x26, 0x96, 0x63, 0x64, 0x41, - 0x71, 0xcf, 0xa5, 0x24, 0xe0, 0xe6, 0x82, 0x90, 0xdb, 0xcc, 0x99, 0x06, 0xd6, 0x33, 0xa8, 0x09, - 0xe5, 0x1d, 0x4a, 0x5c, 0x4e, 0xba, 0xdb, 0xdc, 0x2c, 0xae, 0x1b, 0x1b, 0x95, 0x86, 0xe5, 0xa8, - 0x5d, 0x73, 0xe2, 0x5d, 0x73, 0x0e, 0xe2, 0x5d, 0x6b, 0x96, 0x5e, 0xbe, 0x5a, 0x7b, 0xef, 0x0f, - 0xff, 0x14, 0xbe, 0x1b, 0xc2, 0xd0, 0x3d, 0x80, 0x87, 0x2e, 0xe3, 0x4f, 0x98, 0x14, 0xb2, 0x38, - 0x53, 0x48, 0x41, 0x0a, 0x48, 0x60, 0xd0, 0x2a, 0x80, 0x74, 0xc2, 0x4e, 0x38, 0x08, 0xb8, 0x59, - 0x92, 0xba, 0x27, 0x66, 0xd0, 0x3a, 0x54, 0x5a, 0x84, 0x75, 0xa8, 0x17, 0xc9, 0xad, 0x2e, 0x4b, - 0xf7, 0x24, 0xa7, 0x84, 0x04, 0xe5, 0xc1, 0x83, 0xd3, 0x88, 0x98, 0x20, 0x19, 0x12, 0x33, 0x62, - 0x2f, 0xf7, 0x9f, 0xbb, 0x94, 0x74, 0xcd, 0x8a, 0x74, 0x97, 0xa6, 0x84, 0x7f, 0x95, 0x27, 0x98, - 0x59, 0x95, 0x9b, 0x1c, 0x93, 0xf6, 0x6f, 0x4a, 0x50, 0xdd, 0x17, 0x47, 0x21, 0x0e, 0x87, 0x1a, - 0xe4, 0x31, 0x39, 0xd2, 0x7b, 0x23, 0x86, 0xc8, 0x01, 0x68, 0x91, 0x23, 0x2f, 0xf0, 0xa4, 0x56, - 0x39, 0x69, 0xf8, 0xb2, 0x13, 0x1d, 0x3a, 0xa3, 0x59, 0x9c, 0xe0, 0x40, 0x0e, 0xa0, 0xf6, 0x8b, - 0x28, 0xa4, 0x9c, 0xd0, 0x16, 0x89, 0x28, 0xe9, 0x08, 0x07, 0xca, 0xfd, 0x2b, 0xe3, 0x8c, 0x15, - 0x34, 0x80, 0x6f, 0xc5, 0xb3, 0xdb, 0x9c, 0x53, 0x96, 0x00, 0x15, 0x64, 0x90, 0xdd, 0x49, 0x07, - 0x59, 0x52, 0x65, 0x67, 0x0a, 0xba, 0x1d, 0x70, 0x7a, 0x8a, 0xa7, 0xc9, 0x16, 0x3e, 0xd9, 0x27, - 0x8c, 0x09, 0x9b, 0x64, 0xc0, 0xe0, 0x98, 0x44, 0x16, 0x94, 0x7e, 0x46, 0xc3, 0x80, 0x93, 0xa0, - 0x2b, 0x83, 0xa5, 0x8c, 0x87, 0x34, 0x7a, 0x0a, 0x4b, 0xf1, 0x58, 0x0a, 0x34, 0x17, 0xa5, 0x8a, - 0x9f, 0xcc, 0x50, 0x71, 0x0c, 0xa3, 0x14, 0x1b, 0x97, 0x83, 0xb6, 0x60, 0x61, 0xc7, 0xed, 0x3c, - 0x27, 0x32, 0x2e, 0x2a, 0x8d, 0xd5, 0xb4, 0x40, 0xb9, 0xfc, 0x58, 0x06, 0x02, 0x93, 0x47, 0xfb, - 0x3d, 0xac, 0x20, 0xe8, 0x97, 0x50, 0x6d, 0x07, 0xdc, 0xe3, 0x3e, 0xe9, 0xcb, 0x3d, 0x2e, 0x8b, - 0x3d, 0x6e, 0x6e, 0x7d, 0xf3, 0x6a, 0xed, 0x47, 0x53, 0x33, 0xda, 0x80, 0x7b, 0x7e, 0x9d, 0x24, - 0x50, 0x4e, 0x42, 0x04, 0x1e, 0x93, 0x87, 0xbe, 0x84, 0xe5, 0x58, 0xd9, 0xdd, 0x20, 0x1a, 0x70, - 0x66, 0x82, 0xb4, 0xba, 0x31, 0xa7, 0xd5, 0x0a, 0xa4, 0xcc, 0x9e, 0x90, 0x24, 0x9c, 0xbd, 0x1b, - 0x70, 0x42, 0x03, 0xd7, 0xd7, 0x41, 0x3b, 0xa4, 0xd1, 0xae, 0x88, 0x4d, 0x91, 0x78, 0xf7, 0x64, - 0xba, 0x35, 0xab, 0xd2, 0x35, 0x57, 0xd3, 0x5f, 0x4d, 0xa6, 0x67, 0x47, 0x31, 0xe3, 0x31, 0x28, - 0xba, 0x0d, 0xe5, 0x38, 0x10, 0x98, 0xb9, 0x24, 0xb5, 0xb7, 0xd2, 0x72, 0x62, 0x16, 0x3c, 0x62, - 0xb6, 0x1e, 0xc0, 0xa5, 0xb3, 0x02, 0x4c, 0x1c, 0x98, 0x63, 0x72, 0x1a, 0x1f, 0x98, 0x63, 0x72, - 0x2a, 0x72, 0xd6, 0x89, 0xeb, 0x0f, 0x54, 0x2e, 0x2b, 0x63, 0x45, 0x6c, 0xe5, 0x6e, 0x1b, 0xd6, - 0x3d, 0x40, 0xe9, 0x48, 0x38, 0x97, 0x84, 0xcf, 0xe1, 0x42, 0x86, 0x57, 0x33, 0x44, 0x5c, 0x49, - 0x8a, 0x48, 0x1f, 0xd8, 0x91, 0x48, 0xfb, 0x2f, 0x79, 0xa8, 0x26, 0x63, 0x0b, 0x6d, 0xc2, 0x05, - 0x65, 0x31, 0x26, 0x47, 0x89, 0xc3, 0xa8, 0x84, 0x67, 0x2d, 0xa1, 0x06, 0x7c, 0xb0, 0xdb, 0xd7, - 0xd3, 0xc9, 0xf3, 0x9b, 0x93, 0xc9, 0x26, 0x73, 0x0d, 0x85, 0xf0, 0xa1, 0x12, 0x35, 0x79, 0xe8, - 0xf3, 0x72, 0x77, 0x3e, 0x3d, 0xfb, 0x00, 0x38, 0x99, 0x58, 0x15, 0x62, 0xd9, 0x72, 0xd1, 0x4f, - 0x60, 0x51, 0x2d, 0x30, 0x9d, 0x57, 0x3e, 0x3e, 0xfb, 0x13, 0x4a, 0x58, 0x8c, 0x11, 0x70, 0x65, - 0x07, 0x33, 0x17, 0xce, 0x01, 0xd7, 0x18, 0xeb, 0x3e, 0x58, 0xd3, 0x55, 0x3e, 0x4f, 0x08, 0xd8, - 0x7f, 0x36, 0x60, 0x25, 0xf5, 0x21, 0x71, 0x25, 0xca, 0x4b, 0x41, 0x89, 0x90, 0x63, 0xd4, 0x82, - 0x05, 0x95, 0xa4, 0x72, 0x52, 0x61, 0x67, 0x0e, 0x85, 0x9d, 0x44, 0x86, 0x52, 0x60, 0xeb, 0x36, - 0xc0, 0x9b, 0x05, 0xab, 0xfd, 0x57, 0x03, 0x96, 0x74, 0x42, 0xd0, 0xf5, 0x83, 0x0b, 0xb5, 0xe1, - 0x19, 0xd3, 0x73, 0xba, 0x92, 0xb8, 0x39, 0x35, 0x97, 0x28, 0x36, 0x67, 0x12, 0xa7, 0x74, 0x4c, - 0x89, 0xb3, 0x76, 0xe2, 0xb8, 0x9a, 0x60, 0x3d, 0x97, 0xe6, 0x97, 0x61, 0x69, 0x5f, 0xd6, 0xa9, - 0x53, 0xaf, 0x45, 0xfb, 0x3f, 0x06, 0x2c, 0xc7, 0x3c, 0xda, 0xba, 0x1f, 0x42, 0xe9, 0x84, 0x50, - 0x4e, 0x5e, 0x10, 0xa6, 0xad, 0x32, 0xd3, 0x56, 0x7d, 0x21, 0x39, 0xf0, 0x90, 0x13, 0x6d, 0x41, - 0x49, 0xd5, 0xc4, 0x24, 0xde, 0xa8, 0xd5, 0x69, 0x28, 0xfd, 0xbd, 0x21, 0x3f, 0xaa, 0x43, 0xc1, - 0x0f, 0x7b, 0x4c, 0x9f, 0x99, 0xef, 0x4c, 0xc3, 0x3d, 0x0c, 0x7b, 0x58, 0x32, 0xa2, 0x3b, 0x50, - 0xfa, 0xca, 0xa5, 0x81, 0x17, 0xf4, 0xe2, 0x53, 0xb0, 0x36, 0x0d, 0xf4, 0x54, 0xf1, 0xe1, 0x21, - 0x40, 0x94, 0x71, 0x45, 0xb5, 0x86, 0x1e, 0x40, 0xb1, 0xeb, 0xf5, 0x08, 0xe3, 0xca, 0x25, 0xcd, - 0x86, 0xb8, 0x8f, 0xbe, 0x79, 0xb5, 0x76, 0x2d, 0x71, 0xe1, 0x84, 0x11, 0x09, 0x44, 0xd3, 0xe0, - 0x7a, 0x01, 0xa1, 0xa2, 0x07, 0xb8, 0xa1, 0x20, 0x4e, 0x4b, 0xfe, 0x60, 0x2d, 0x41, 0xc8, 0xf2, - 0xd4, 0xb5, 0x22, 0xf3, 0xc5, 0x9b, 0xc9, 0x52, 0x12, 0xc4, 0x31, 0x08, 0xdc, 0x3e, 0xd1, 0xe5, - 0x86, 0x1c, 0x8b, 0xaa, 0xa8, 0x23, 0xe2, 0xbc, 0x2b, 0xeb, 0xc5, 0x12, 0xd6, 0x14, 0xda, 0x82, - 0x45, 0xc6, 0x5d, 0x2a, 0x72, 0xce, 0xc2, 0x9c, 0xe5, 0x5c, 0x0c, 0x40, 0x77, 0xa1, 0xdc, 0x09, - 0xfb, 0x91, 0x4f, 0x04, 0xba, 0x38, 0x27, 0x7a, 0x04, 0x11, 0xa1, 0x47, 0x28, 0x0d, 0xa9, 0x2c, - 0x24, 0xcb, 0x58, 0x11, 0xe8, 0x16, 0x2c, 0x45, 0x34, 0xec, 0x51, 0xc2, 0xd8, 0x67, 0x34, 0x1c, - 0x44, 0xba, 0x18, 0x58, 0x11, 0xc9, 0x7b, 0x2f, 0xb9, 0x80, 0xc7, 0xf9, 0xec, 0x7f, 0xe7, 0xa0, - 0x9a, 0x0c, 0x91, 0x54, 0x85, 0xfd, 0x00, 0x8a, 0x2a, 0xe0, 0x54, 0xac, 0xbf, 0x99, 0x8f, 0x95, - 0x84, 0x4c, 0x1f, 0x9b, 0xb0, 0xd8, 0x19, 0x50, 0x59, 0x7e, 0xab, 0xa2, 0x3c, 0x26, 0x85, 0xa5, - 0x3c, 0xe4, 0xae, 0x2f, 0x7d, 0x9c, 0xc7, 0x8a, 0x10, 0x15, 0xf9, 0xb0, 0x4b, 0x3a, 0x5f, 0x45, - 0x3e, 0x84, 0x25, 0xf7, 0x6f, 0xf1, 0xad, 0xf6, 0xaf, 0x74, 0xee, 0xfd, 0xb3, 0xff, 0x6e, 0x40, - 0x79, 0x78, 0xb6, 0x12, 0xde, 0x35, 0xde, 0xda, 0xbb, 0x63, 0x9e, 0xc9, 0xbd, 0x99, 0x67, 0x2e, - 0x42, 0x91, 0x71, 0x4a, 0xdc, 0xbe, 0xea, 0x17, 0xb1, 0xa6, 0x44, 0x16, 0xeb, 0xb3, 0x9e, 0xdc, - 0xa1, 0x2a, 0x16, 0x43, 0xfb, 0xbf, 0x06, 0x2c, 0x8d, 0x1d, 0xf7, 0x77, 0x6a, 0xcb, 0x07, 0xb0, - 0xe0, 0x93, 0x13, 0xa2, 0x3a, 0xda, 0x3c, 0x56, 0x84, 0x98, 0x65, 0xcf, 0x43, 0xca, 0xa5, 0x72, - 0x55, 0xac, 0x08, 0xa1, 0x73, 0x97, 0x70, 0xd7, 0xf3, 0x65, 0x5e, 0xaa, 0x62, 0x4d, 0x09, 0x9d, - 0x07, 0xd4, 0xd7, 0x35, 0xba, 0x18, 0x22, 0x1b, 0x0a, 0x5e, 0x70, 0x14, 0xea, 0xb0, 0x91, 0x95, - 0x8d, 0xaa, 0xf5, 0x76, 0x83, 0xa3, 0x10, 0xcb, 0x35, 0x74, 0x19, 0x8a, 0xd4, 0x0d, 0x7a, 0x24, - 0x2e, 0xd0, 0xcb, 0x82, 0x0b, 0x8b, 0x19, 0xac, 0x17, 0x6c, 0x1b, 0xaa, 0xb2, 0x2b, 0x7e, 0x44, - 0x98, 0xe8, 0xc1, 0x44, 0x58, 0x77, 0x5d, 0xee, 0x4a, 0xb3, 0xab, 0x58, 0x8e, 0xed, 0xeb, 0x80, - 0x1e, 0x7a, 0x8c, 0x3f, 0x95, 0x6f, 0x0a, 0x6c, 0x56, 0xcb, 0xbc, 0x0f, 0x17, 0xc6, 0xb8, 0xf5, - 0xb5, 0xf0, 0xe3, 0x89, 0xa6, 0xf9, 0x4a, 0x3a, 0xe3, 0xca, 0xa7, 0x0b, 0x47, 0x01, 0x27, 0x7a, - 0xe7, 0x25, 0xa8, 0x48, 0xbb, 0xd4, 0xb7, 0x6d, 0x17, 0xaa, 0x8a, 0xd4, 0xc2, 0x3f, 0x87, 0xf7, - 0x63, 0x41, 0x5f, 0x10, 0x2a, 0xdb, 0x19, 0x43, 0xfa, 0xe5, 0x7b, 0xd3, 0xbe, 0xd2, 0x1c, 0x67, - 0xc7, 0x93, 0x78, 0x9b, 0xc0, 0x05, 0xc9, 0x73, 0xdf, 0x63, 0x3c, 0xa4, 0xa7, 0xb1, 0xd5, 0xab, - 0x00, 0xdb, 0x1d, 0xee, 0x9d, 0x90, 0xc7, 0x81, 0xaf, 0xae, 0xd1, 0x12, 0x4e, 0xcc, 0xc4, 0x57, - 0x64, 0x6e, 0xd4, 0x39, 0x5e, 0x82, 0x72, 0xdb, 0xa5, 0xfe, 0x69, 0xfb, 0x85, 0xc7, 0x75, 0x03, - 0x3f, 0x9a, 0xb0, 0x7f, 0x6f, 0xc0, 0x4a, 0xf2, 0x3b, 0xed, 0x13, 0x91, 0x2e, 0xee, 0x40, 0x81, - 0xc7, 0x75, 0xcc, 0x72, 0x96, 0x11, 0x29, 0x88, 0x28, 0x75, 0xb0, 0x04, 0x25, 0x3c, 0xad, 0x0e, - 0xce, 0x95, 0xb3, 0xe1, 0x13, 0x9e, 0xfe, 0x5f, 0x09, 0x50, 0x7a, 0x39, 0xa3, 0x23, 0x4e, 0x36, - 0x88, 0xb9, 0x89, 0x06, 0xf1, 0xd9, 0x64, 0x83, 0xa8, 0xae, 0xe6, 0x5b, 0xf3, 0x68, 0x32, 0x47, - 0x9b, 0x38, 0xd6, 0xc7, 0x14, 0xce, 0xd1, 0xc7, 0xa0, 0x8d, 0xf8, 0xc6, 0x51, 0x77, 0x1d, 0x8a, - 0x73, 0x0a, 0x8d, 0x3a, 0x8e, 0xae, 0x2b, 0xf4, 0x2d, 0x74, 0xf7, 0x7c, 0xaf, 0x25, 0x85, 0xc9, - 0x97, 0x92, 0x26, 0x54, 0x76, 0xe2, 0x44, 0x79, 0x8e, 0xa7, 0x92, 0x24, 0x08, 0x6d, 0xea, 0xc2, - 0x46, 0xa5, 0xe6, 0x4b, 0x69, 0x13, 0xe3, 0x67, 0x91, 0x90, 0xea, 0xca, 0xe6, 0x28, 0xa3, 0xb4, - 0x2c, 0x4b, 0x07, 0x6d, 0xcd, 0xe5, 0xfb, 0x39, 0xeb, 0x4b, 0xf4, 0x29, 0x14, 0x31, 0x61, 0x03, - 0x9f, 0xcb, 0xf7, 0x97, 0x4a, 0xe3, 0xf2, 0x14, 0xe9, 0x8a, 0x49, 0x9e, 0x55, 0x0d, 0x40, 0x3f, - 0x87, 0x45, 0x35, 0x62, 0x66, 0x65, 0xda, 0xb3, 0x41, 0x86, 0x66, 0x1a, 0xa3, 0x1b, 0x0a, 0x4d, - 0x89, 0xe3, 0xf8, 0x19, 0x09, 0x88, 0x7e, 0x17, 0x14, 0xad, 0xf1, 0x02, 0x4e, 0xcc, 0xa0, 0x06, - 0x2c, 0x70, 0xea, 0x76, 0x88, 0xb9, 0x34, 0x87, 0x0b, 0x15, 0xab, 0x48, 0x6c, 0x91, 0x17, 0x04, - 0xa4, 0x6b, 0x2e, 0xab, 0x4a, 0x49, 0x51, 0xe8, 0xbb, 0xb0, 0x1c, 0x0c, 0xfa, 0xb2, 0x59, 0xe8, - 0xee, 0x73, 0x12, 0x31, 0xf3, 0x7d, 0xf9, 0xbd, 0x89, 0x59, 0x74, 0x05, 0x96, 0x82, 0x41, 0xff, - 0x40, 0xdc, 0xf0, 0x8a, 0xad, 0x26, 0xd9, 0xc6, 0x27, 0xd1, 0x75, 0x58, 0x11, 0xb8, 0x78, 0xb7, - 0x15, 0xe7, 0x8a, 0xe4, 0x4c, 0x2f, 0xbc, 0x83, 0x9e, 0xf9, 0x5d, 0x74, 0x04, 0xd6, 0x33, 0xa8, - 0x26, 0xf7, 0x21, 0x03, 0x7b, 0x6b, 0xbc, 0xe3, 0x9e, 0x23, 0x2e, 0x12, 0x0d, 0xc7, 0x33, 0xf8, - 0xf6, 0x93, 0xa8, 0xeb, 0x72, 0x92, 0x95, 0x79, 0xd3, 0x19, 0xe8, 0x22, 0x14, 0xf7, 0xd4, 0x46, - 0xa9, 0xf7, 0x52, 0x4d, 0x89, 0xf9, 0x16, 0x11, 0xce, 0xd3, 0xe9, 0x56, 0x53, 0xf6, 0x25, 0xb0, - 0xb2, 0xc4, 0x2b, 0x67, 0xd8, 0x7f, 0xca, 0x01, 0x8c, 0x82, 0x01, 0x7d, 0x04, 0xd0, 0x27, 0x5d, - 0xcf, 0xfd, 0x15, 0x1f, 0x35, 0x94, 0x65, 0x39, 0x23, 0xbb, 0xca, 0x51, 0xe9, 0x9f, 0x7b, 0xeb, - 0xd2, 0x1f, 0x41, 0x81, 0x79, 0x5f, 0x13, 0x5d, 0xa6, 0xc8, 0x31, 0x7a, 0x0c, 0x15, 0x37, 0x08, - 0x42, 0x2e, 0xc3, 0x38, 0x6e, 0xb6, 0x6f, 0x9c, 0x15, 0xbe, 0xce, 0xf6, 0x88, 0x5f, 0x9d, 0x92, - 0xa4, 0x04, 0xeb, 0x2e, 0xd4, 0x26, 0x19, 0xce, 0xd5, 0x0c, 0xfe, 0x2d, 0x07, 0xef, 0x4f, 0x6c, - 0x1d, 0xba, 0x0f, 0x35, 0x45, 0x4d, 0x3c, 0x90, 0xcc, 0x3a, 0x68, 0x29, 0x14, 0xba, 0x07, 0xd5, - 0x6d, 0xce, 0x45, 0x26, 0x54, 0xf6, 0xaa, 0x16, 0xf0, 0x6c, 0x29, 0x63, 0x08, 0x74, 0x7f, 0x94, - 0x56, 0xf2, 0xd3, 0x1a, 0xfd, 0x09, 0xfd, 0xb3, 0x73, 0x8a, 0xf5, 0x8b, 0x99, 0x41, 0xde, 0x18, - 0x0f, 0xf2, 0x19, 0x59, 0x25, 0xf1, 0xc8, 0x64, 0x40, 0x29, 0x3e, 0x84, 0x99, 0x6f, 0x15, 0x77, - 0xc6, 0xdf, 0x2a, 0xae, 0x4e, 0xbf, 0xd4, 0xd2, 0x4f, 0x14, 0xe8, 0x22, 0xe4, 0x3c, 0xfd, 0xc4, - 0xdc, 0x2c, 0xbe, 0x7e, 0xb5, 0x96, 0xdb, 0x6d, 0xe1, 0x9c, 0xd7, 0x7d, 0xf3, 0xa7, 0x8b, 0x6b, - 0x3f, 0x85, 0x0f, 0x33, 0x0b, 0x0d, 0x54, 0x81, 0xc5, 0xfd, 0x83, 0x6d, 0x7c, 0xd0, 0x6e, 0xd5, - 0xde, 0x43, 0x55, 0x28, 0xed, 0x3c, 0x7e, 0xb4, 0xf7, 0xb0, 0x7d, 0xd0, 0xae, 0x19, 0x62, 0xa9, - 0xd5, 0x16, 0xe3, 0x56, 0x2d, 0xd7, 0xf8, 0x5d, 0x11, 0x16, 0x77, 0xd4, 0x3f, 0x66, 0xe8, 0x00, - 0xca, 0xc3, 0xbf, 0x52, 0x90, 0x9d, 0xe1, 0xb2, 0x89, 0xff, 0x64, 0xac, 0x8f, 0xcf, 0xe4, 0xd1, - 0x17, 0xd1, 0x7d, 0x58, 0x90, 0x7f, 0x2a, 0xa1, 0x8c, 0xe7, 0x82, 0xe4, 0xbf, 0x4d, 0xd6, 0xd9, - 0x7f, 0xd2, 0x6c, 0x1a, 0x42, 0x92, 0x7c, 0x6b, 0xc9, 0x92, 0x94, 0x7c, 0xd0, 0xb5, 0xd6, 0x66, - 0x3c, 0xd2, 0xa0, 0x47, 0x50, 0xd4, 0x0d, 0x68, 0x16, 0x6b, 0xf2, 0x45, 0xc5, 0x5a, 0x9f, 0xce, - 0xa0, 0x84, 0x6d, 0x1a, 0xe8, 0xd1, 0xf0, 0x8d, 0x3e, 0x4b, 0xb5, 0x64, 0xf5, 0x6e, 0xcd, 0x58, - 0xdf, 0x30, 0x36, 0x0d, 0xf4, 0x25, 0x54, 0x12, 0xf5, 0x39, 0xca, 0xa8, 0x0e, 0xd3, 0xc5, 0xbe, - 0x75, 0x75, 0x06, 0x97, 0xb6, 0xbc, 0x0d, 0x05, 0x99, 0x18, 0x32, 0x9c, 0x9d, 0x28, 0xdf, 0xb3, - 0xd4, 0x1c, 0x2b, 0xe7, 0x0f, 0x55, 0xc3, 0x41, 0x82, 0x64, 0xf4, 0xa1, 0xab, 0xb3, 0xea, 0x84, - 0xa9, 0x61, 0x93, 0x0a, 0xe2, 0x4d, 0x03, 0x85, 0x80, 0xd2, 0x97, 0x01, 0xfa, 0x7e, 0x46, 0x94, - 0x4c, 0xbb, 0x91, 0xac, 0xeb, 0xf3, 0x31, 0x2b, 0xa3, 0x9a, 0xd5, 0x97, 0xaf, 0x57, 0x8d, 0x7f, - 0xbc, 0x5e, 0x35, 0xfe, 0xf5, 0x7a, 0xd5, 0x38, 0x2c, 0xca, 0x0a, 0xf0, 0x07, 0xff, 0x0f, 0x00, - 0x00, 0xff, 0xff, 0xc2, 0xc1, 0xf2, 0x3d, 0x51, 0x1e, 0x00, 0x00, + 0x15, 0xcf, 0x48, 0xb2, 0x2c, 0x3d, 0xc9, 0x8e, 0xdc, 0xc9, 0x86, 0x61, 0xc8, 0xda, 0xce, 0x6c, + 0x02, 0xae, 0x90, 0x8c, 0xbc, 0x82, 0x90, 0xac, 0x03, 0x21, 0xb6, 0x25, 0x36, 0x0e, 0x49, 0xc5, + 0xdb, 0x76, 0x36, 0xd4, 0x56, 0x05, 0x6a, 0x2c, 0xb5, 0x95, 0x29, 0x8f, 0x66, 0x86, 0xee, 0x96, + 0x37, 0xde, 0x13, 0x27, 0xaa, 0xb8, 0x50, 0x5c, 0x28, 0x2e, 0xdc, 0x39, 0x71, 0xe6, 0xcc, 0x81, + 0xaa, 0x1c, 0x39, 0xef, 0x21, 0x50, 0xf9, 0x00, 0x14, 0x47, 0xb8, 0x6d, 0xf5, 0x9f, 0x91, 0x46, + 0x9a, 0x91, 0x2d, 0x25, 0x39, 0xa9, 0x5f, 0xf7, 0xfb, 0xbd, 0x79, 0xef, 0xf5, 0xeb, 0xd7, 0xef, + 0xb5, 0x60, 0xa1, 0x1d, 0x06, 0x9c, 0x86, 0xbe, 0x13, 0xd1, 0x90, 0x87, 0xa8, 0xd6, 0x0b, 0x0f, + 0x4e, 0x9c, 0x83, 0xbe, 0xe7, 0x77, 0x8e, 0x3c, 0xee, 0x1c, 0x7f, 0x6c, 0x35, 0xba, 0x1e, 0x7f, + 0xd1, 0x3f, 0x70, 0xda, 0x61, 0xaf, 0xde, 0x0d, 0xbb, 0x61, 0xbd, 0x1b, 0x86, 0x5d, 0x9f, 0xb8, + 0x91, 0xc7, 0xf4, 0xb0, 0x4e, 0xa3, 0x76, 0x9d, 0x71, 0x97, 0xf7, 0x99, 0x92, 0x62, 0xdd, 0x1c, + 0xc7, 0xc8, 0xe9, 0x83, 0xfe, 0xa1, 0xa4, 0x24, 0x21, 0x47, 0x9a, 0xbd, 0x9e, 0x60, 0x17, 0xdf, + 0xaf, 0xc7, 0xdf, 0xaf, 0xbb, 0x91, 0x57, 0xe7, 0x27, 0x11, 0x61, 0xf5, 0x2f, 0x43, 0x7a, 0x44, + 0xa8, 0x06, 0xdc, 0x98, 0x08, 0x60, 0xa1, 0x7f, 0x4c, 0x68, 0x3d, 0x3a, 0xa8, 0x87, 0x51, 0xac, + 0xcd, 0xad, 0x53, 0xb8, 0xfb, 0xb4, 0x4d, 0xa2, 0xd0, 0xf7, 0xda, 0x27, 0x02, 0xa3, 0x46, 0x1a, + 0xb6, 0xa2, 0xad, 0x1b, 0xe8, 0xce, 0xbd, 0x1e, 0x61, 0xdc, 0xed, 0x45, 0x8a, 0xc1, 0xfe, 0xad, + 0x01, 0xd5, 0x5d, 0xda, 0x0f, 0x08, 0x26, 0xbf, 0xee, 0x13, 0xc6, 0xd1, 0x25, 0x28, 0x1e, 0x7a, + 0x3e, 0x27, 0xd4, 0x34, 0x56, 0xf3, 0x6b, 0x65, 0xac, 0x29, 0x54, 0x83, 0xbc, 0xeb, 0xfb, 0x66, + 0x6e, 0xd5, 0x58, 0x2b, 0x61, 0x31, 0x44, 0x6b, 0x50, 0x3d, 0x22, 0x24, 0x6a, 0xf6, 0xa9, 0xcb, + 0xbd, 0x30, 0x30, 0xf3, 0xab, 0xc6, 0x5a, 0x7e, 0xab, 0xf0, 0xea, 0xf5, 0x8a, 0x81, 0x47, 0x56, + 0x90, 0x0d, 0x65, 0x41, 0x6f, 0x9d, 0x70, 0xc2, 0xcc, 0x42, 0x82, 0x6d, 0x38, 0x6d, 0x5f, 0x87, + 0x5a, 0xd3, 0x63, 0x47, 0x4f, 0x99, 0xdb, 0x3d, 0x4b, 0x17, 0xfb, 0x21, 0x2c, 0x25, 0x78, 0x59, + 0x14, 0x06, 0x8c, 0xa0, 0x5b, 0x50, 0xa4, 0xa4, 0x1d, 0xd2, 0x8e, 0x64, 0xae, 0x34, 0x3e, 0x74, + 0xc6, 0xc3, 0xc0, 0xd1, 0x00, 0xc1, 0x84, 0x35, 0xb3, 0xfd, 0xa7, 0x3c, 0x54, 0x12, 0xf3, 0x68, + 0x11, 0x72, 0x3b, 0x4d, 0xd3, 0x58, 0x35, 0xd6, 0xca, 0x38, 0xb7, 0xd3, 0x44, 0x26, 0xcc, 0x3f, + 0xee, 0x73, 0xf7, 0xc0, 0x27, 0xda, 0xf6, 0x98, 0x44, 0x17, 0x61, 0x6e, 0x27, 0x78, 0xca, 0x88, + 0x34, 0xbc, 0x84, 0x15, 0x81, 0x10, 0x14, 0xf6, 0xbc, 0xaf, 0x88, 0x32, 0x13, 0xcb, 0x31, 0xb2, + 0xa0, 0xb8, 0xeb, 0x52, 0x12, 0x70, 0x73, 0x4e, 0xc8, 0xdd, 0xca, 0x99, 0x06, 0xd6, 0x33, 0x68, + 0x0b, 0xca, 0xdb, 0x94, 0xb8, 0x9c, 0x74, 0x36, 0xb9, 0x59, 0x5c, 0x35, 0xd6, 0x2a, 0x0d, 0xcb, + 0x51, 0xbb, 0xe6, 0xc4, 0xbb, 0xe6, 0xec, 0xc7, 0xbb, 0xb6, 0x55, 0x7a, 0xf5, 0x7a, 0xe5, 0xdc, + 0x1f, 0xfe, 0x25, 0x7c, 0x37, 0x80, 0xa1, 0xfb, 0x00, 0x8f, 0x5c, 0xc6, 0x9f, 0x32, 0x29, 0x64, + 0xfe, 0x4c, 0x21, 0x05, 0x29, 0x20, 0x81, 0x41, 0xcb, 0x00, 0xd2, 0x09, 0xdb, 0x61, 0x3f, 0xe0, + 0x66, 0x49, 0xea, 0x9e, 0x98, 0x41, 0xab, 0x50, 0x69, 0x12, 0xd6, 0xa6, 0x5e, 0x24, 0xb7, 0xba, + 0x2c, 0xdd, 0x93, 0x9c, 0x12, 0x12, 0x94, 0x07, 0xf7, 0x4f, 0x22, 0x62, 0x82, 0x64, 0x48, 0xcc, + 0x88, 0xbd, 0xdc, 0x7b, 0xe1, 0x52, 0xd2, 0x31, 0x2b, 0xd2, 0x5d, 0x9a, 0x12, 0xfe, 0x55, 0x9e, + 0x60, 0x66, 0x55, 0x6e, 0x72, 0x4c, 0xda, 0xbf, 0x29, 0x41, 0x75, 0x4f, 0x1c, 0x85, 0x38, 0x1c, + 0x6a, 0x90, 0xc7, 0xe4, 0x50, 0xef, 0x8d, 0x18, 0x22, 0x07, 0xa0, 0x49, 0x0e, 0xbd, 0xc0, 0x93, + 0x5a, 0xe5, 0xa4, 0xe1, 0x8b, 0x4e, 0x74, 0xe0, 0x0c, 0x67, 0x71, 0x82, 0x03, 0x39, 0x80, 0x5a, + 0x2f, 0xa3, 0x90, 0x72, 0x42, 0x9b, 0x24, 0xa2, 0xa4, 0x2d, 0x1c, 0x28, 0xf7, 0xaf, 0x8c, 0x33, + 0x56, 0x50, 0x1f, 0xbe, 0x15, 0xcf, 0x6e, 0x72, 0x4e, 0x59, 0x02, 0x54, 0x90, 0x41, 0x76, 0x37, + 0x1d, 0x64, 0x49, 0x95, 0x9d, 0x09, 0xe8, 0x56, 0xc0, 0xe9, 0x09, 0x9e, 0x24, 0x5b, 0xf8, 0x64, + 0x8f, 0x30, 0x26, 0x6c, 0x92, 0x01, 0x83, 0x63, 0x12, 0x59, 0x50, 0xfa, 0x19, 0x0d, 0x03, 0x4e, + 0x82, 0x8e, 0x0c, 0x96, 0x32, 0x1e, 0xd0, 0xe8, 0x19, 0x2c, 0xc4, 0x63, 0x29, 0xd0, 0x9c, 0x97, + 0x2a, 0x7e, 0x7c, 0x86, 0x8a, 0x23, 0x18, 0xa5, 0xd8, 0xa8, 0x1c, 0xb4, 0x01, 0x73, 0xdb, 0x6e, + 0xfb, 0x05, 0x91, 0x71, 0x51, 0x69, 0x2c, 0xa7, 0x05, 0xca, 0xe5, 0x27, 0x32, 0x10, 0x98, 0x3c, + 0xda, 0xe7, 0xb0, 0x82, 0xa0, 0x5f, 0x42, 0xb5, 0x15, 0x70, 0x8f, 0xfb, 0xa4, 0x27, 0xf7, 0xb8, + 0x2c, 0xf6, 0x78, 0x6b, 0xe3, 0xeb, 0xd7, 0x2b, 0x3f, 0x9a, 0x98, 0xd1, 0xfa, 0xdc, 0xf3, 0xeb, + 0x24, 0x81, 0x72, 0x12, 0x22, 0xf0, 0x88, 0x3c, 0xf4, 0x05, 0x2c, 0xc6, 0xca, 0xee, 0x04, 0x51, + 0x9f, 0x33, 0x13, 0xa4, 0xd5, 0x8d, 0x29, 0xad, 0x56, 0x20, 0x65, 0xf6, 0x98, 0x24, 0xe1, 0xec, + 0x9d, 0x80, 0x13, 0x1a, 0xb8, 0xbe, 0x0e, 0xda, 0x01, 0x8d, 0x76, 0x44, 0x6c, 0x8a, 0xc4, 0xbb, + 0x2b, 0xd3, 0xad, 0x59, 0x95, 0xae, 0xb9, 0x96, 0xfe, 0x6a, 0x32, 0x3d, 0x3b, 0x8a, 0x19, 0x8f, + 0x40, 0xd1, 0x1d, 0x28, 0xc7, 0x81, 0xc0, 0xcc, 0x05, 0xa9, 0xbd, 0x95, 0x96, 0x13, 0xb3, 0xe0, + 0x21, 0xb3, 0xf5, 0x10, 0x2e, 0x9f, 0x16, 0x60, 0xe2, 0xc0, 0x1c, 0x91, 0x93, 0xf8, 0xc0, 0x1c, + 0x91, 0x13, 0x91, 0xb3, 0x8e, 0x5d, 0xbf, 0xaf, 0x72, 0x59, 0x19, 0x2b, 0x62, 0x23, 0x77, 0xc7, + 0xb0, 0xee, 0x03, 0x4a, 0x47, 0xc2, 0x4c, 0x12, 0x3e, 0x83, 0x0b, 0x19, 0x5e, 0xcd, 0x10, 0x71, + 0x35, 0x29, 0x22, 0x7d, 0x60, 0x87, 0x22, 0xed, 0xbf, 0xe6, 0xa1, 0x9a, 0x8c, 0x2d, 0xb4, 0x0e, + 0x17, 0x94, 0xc5, 0x98, 0x1c, 0x26, 0x0e, 0xa3, 0x12, 0x9e, 0xb5, 0x84, 0x1a, 0x70, 0x71, 0xa7, + 0xa7, 0xa7, 0x93, 0xe7, 0x37, 0x27, 0x93, 0x4d, 0xe6, 0x1a, 0x0a, 0xe1, 0x03, 0x25, 0x6a, 0xfc, + 0xd0, 0xe7, 0xe5, 0xee, 0x7c, 0x72, 0xfa, 0x01, 0x70, 0x32, 0xb1, 0x2a, 0xc4, 0xb2, 0xe5, 0xa2, + 0x9f, 0xc0, 0xbc, 0x5a, 0x60, 0x3a, 0xaf, 0x7c, 0x74, 0xfa, 0x27, 0x94, 0xb0, 0x18, 0x23, 0xe0, + 0xca, 0x0e, 0x66, 0xce, 0xcd, 0x00, 0xd7, 0x18, 0xeb, 0x01, 0x58, 0x93, 0x55, 0x9e, 0x25, 0x04, + 0xec, 0xbf, 0x18, 0xb0, 0x94, 0xfa, 0x90, 0xb8, 0x12, 0xe5, 0xa5, 0xa0, 0x44, 0xc8, 0x31, 0x6a, + 0xc2, 0x9c, 0x4a, 0x52, 0x39, 0xa9, 0xb0, 0x33, 0x85, 0xc2, 0x4e, 0x22, 0x43, 0x29, 0xb0, 0x75, + 0x07, 0xe0, 0xed, 0x82, 0xd5, 0xfe, 0x9b, 0x01, 0x0b, 0x3a, 0x21, 0xe8, 0xfa, 0xc1, 0x85, 0xda, + 0xe0, 0x8c, 0xe9, 0x39, 0x5d, 0x49, 0xdc, 0x9a, 0x98, 0x4b, 0x14, 0x9b, 0x33, 0x8e, 0x53, 0x3a, + 0xa6, 0xc4, 0x59, 0xdb, 0x71, 0x5c, 0x8d, 0xb1, 0xce, 0xa4, 0xf9, 0x15, 0x58, 0xd8, 0x93, 0x75, + 0xea, 0xc4, 0x6b, 0xd1, 0xfe, 0xaf, 0x01, 0x8b, 0x31, 0x8f, 0xb6, 0xee, 0x87, 0x50, 0x3a, 0x26, + 0x94, 0x93, 0x97, 0x84, 0x69, 0xab, 0xcc, 0xb4, 0x55, 0x9f, 0x4b, 0x0e, 0x3c, 0xe0, 0x44, 0x1b, + 0x50, 0x52, 0x35, 0x31, 0x89, 0x37, 0x6a, 0x79, 0x12, 0x4a, 0x7f, 0x6f, 0xc0, 0x8f, 0xea, 0x50, + 0xf0, 0xc3, 0x2e, 0xd3, 0x67, 0xe6, 0x3b, 0x93, 0x70, 0x8f, 0xc2, 0x2e, 0x96, 0x8c, 0xe8, 0x2e, + 0x94, 0xbe, 0x74, 0x69, 0xe0, 0x05, 0xdd, 0xf8, 0x14, 0xac, 0x4c, 0x02, 0x3d, 0x53, 0x7c, 0x78, + 0x00, 0x10, 0x65, 0x5c, 0x51, 0xad, 0xa1, 0x87, 0x50, 0xec, 0x78, 0x5d, 0xc2, 0xb8, 0x72, 0xc9, + 0x56, 0x43, 0xdc, 0x47, 0x5f, 0xbf, 0x5e, 0xb9, 0x9e, 0xb8, 0x70, 0xc2, 0x88, 0x04, 0xa2, 0x69, + 0x70, 0xbd, 0x80, 0x50, 0xd1, 0x03, 0xdc, 0x54, 0x10, 0xa7, 0x29, 0x7f, 0xb0, 0x96, 0x20, 0x64, + 0x79, 0xea, 0x5a, 0x91, 0xf9, 0xe2, 0xed, 0x64, 0x29, 0x09, 0xe2, 0x18, 0x04, 0x6e, 0x8f, 0xe8, + 0x72, 0x43, 0x8e, 0x45, 0x55, 0xd4, 0x16, 0x71, 0xde, 0x91, 0xf5, 0x62, 0x09, 0x6b, 0x0a, 0x6d, + 0xc0, 0x3c, 0xe3, 0x2e, 0x15, 0x39, 0x67, 0x6e, 0xca, 0x72, 0x2e, 0x06, 0xa0, 0x7b, 0x50, 0x6e, + 0x87, 0xbd, 0xc8, 0x27, 0x02, 0x5d, 0x9c, 0x12, 0x3d, 0x84, 0x88, 0xd0, 0x23, 0x94, 0x86, 0x54, + 0x16, 0x92, 0x65, 0xac, 0x08, 0x74, 0x1b, 0x16, 0x22, 0x1a, 0x76, 0x29, 0x61, 0xec, 0x53, 0x1a, + 0xf6, 0x23, 0x5d, 0x0c, 0x2c, 0x89, 0xe4, 0xbd, 0x9b, 0x5c, 0xc0, 0xa3, 0x7c, 0xf6, 0x7f, 0x72, + 0x50, 0x4d, 0x86, 0x48, 0xaa, 0xc2, 0x7e, 0x08, 0x45, 0x15, 0x70, 0x2a, 0xd6, 0xdf, 0xce, 0xc7, + 0x4a, 0x42, 0xa6, 0x8f, 0x4d, 0x98, 0x6f, 0xf7, 0xa9, 0x2c, 0xbf, 0x55, 0x51, 0x1e, 0x93, 0xc2, + 0x52, 0x1e, 0x72, 0xd7, 0x97, 0x3e, 0xce, 0x63, 0x45, 0x88, 0x8a, 0x7c, 0xd0, 0x25, 0xcd, 0x56, + 0x91, 0x0f, 0x60, 0xc9, 0xfd, 0x9b, 0x7f, 0xa7, 0xfd, 0x2b, 0xcd, 0xbc, 0x7f, 0xf6, 0x3f, 0x0c, + 0x28, 0x0f, 0xce, 0x56, 0xc2, 0xbb, 0xc6, 0x3b, 0x7b, 0x77, 0xc4, 0x33, 0xb9, 0xb7, 0xf3, 0xcc, + 0x25, 0x28, 0x32, 0x4e, 0x89, 0xdb, 0x53, 0xfd, 0x22, 0xd6, 0x94, 0xc8, 0x62, 0x3d, 0xd6, 0x95, + 0x3b, 0x54, 0xc5, 0x62, 0x68, 0xff, 0xcf, 0x80, 0x85, 0x91, 0xe3, 0xfe, 0x5e, 0x6d, 0xb9, 0x08, + 0x73, 0x3e, 0x39, 0x26, 0xaa, 0xa3, 0xcd, 0x63, 0x45, 0x88, 0x59, 0xf6, 0x22, 0xa4, 0x5c, 0x2a, + 0x57, 0xc5, 0x8a, 0x10, 0x3a, 0x77, 0x08, 0x77, 0x3d, 0x5f, 0xe6, 0xa5, 0x2a, 0xd6, 0x94, 0xd0, + 0xb9, 0x4f, 0x7d, 0x5d, 0xa3, 0x8b, 0x21, 0xb2, 0xa1, 0xe0, 0x05, 0x87, 0xa1, 0x0e, 0x1b, 0x59, + 0xd9, 0xa8, 0x5a, 0x6f, 0x27, 0x38, 0x0c, 0xb1, 0x5c, 0x43, 0x57, 0xa0, 0x48, 0xdd, 0xa0, 0x4b, + 0xe2, 0x02, 0xbd, 0x2c, 0xb8, 0xb0, 0x98, 0xc1, 0x7a, 0xc1, 0xb6, 0xa1, 0x2a, 0xbb, 0xe2, 0xc7, + 0x84, 0x89, 0x1e, 0x4c, 0x84, 0x75, 0xc7, 0xe5, 0xae, 0x34, 0xbb, 0x8a, 0xe5, 0xd8, 0xbe, 0x01, + 0xe8, 0x91, 0xc7, 0xf8, 0x33, 0xf9, 0xa6, 0xc0, 0xce, 0x6a, 0x99, 0xf7, 0xe0, 0xc2, 0x08, 0xb7, + 0xbe, 0x16, 0x7e, 0x3c, 0xd6, 0x34, 0x5f, 0x4d, 0x67, 0x5c, 0xf9, 0x74, 0xe1, 0x28, 0xe0, 0x58, + 0xef, 0xbc, 0x00, 0x15, 0x69, 0x97, 0xfa, 0xb6, 0xed, 0x42, 0x55, 0x91, 0x5a, 0xf8, 0x67, 0x70, + 0x3e, 0x16, 0xf4, 0x39, 0xa1, 0xb2, 0x9d, 0x31, 0xa4, 0x5f, 0xbe, 0x37, 0xe9, 0x2b, 0x5b, 0xa3, + 0xec, 0x78, 0x1c, 0x6f, 0x13, 0xb8, 0x20, 0x79, 0x1e, 0x78, 0x8c, 0x87, 0xf4, 0x24, 0xb6, 0x7a, + 0x19, 0x60, 0xb3, 0xcd, 0xbd, 0x63, 0xf2, 0x24, 0xf0, 0xd5, 0x35, 0x5a, 0xc2, 0x89, 0x99, 0xf8, + 0x8a, 0xcc, 0x0d, 0x3b, 0xc7, 0xcb, 0x50, 0x6e, 0xb9, 0xd4, 0x3f, 0x69, 0xbd, 0xf4, 0xb8, 0x6e, + 0xe0, 0x87, 0x13, 0xf6, 0xef, 0x0d, 0x58, 0x4a, 0x7e, 0xa7, 0x75, 0x2c, 0xd2, 0xc5, 0x5d, 0x28, + 0xf0, 0xb8, 0x8e, 0x59, 0xcc, 0x32, 0x22, 0x05, 0x11, 0xa5, 0x0e, 0x96, 0xa0, 0x84, 0xa7, 0xd5, + 0xc1, 0xb9, 0x7a, 0x3a, 0x7c, 0xcc, 0xd3, 0xff, 0x2f, 0x01, 0x4a, 0x2f, 0x67, 0x74, 0xc4, 0xc9, + 0x06, 0x31, 0x37, 0xd6, 0x20, 0x3e, 0x1f, 0x6f, 0x10, 0xd5, 0xd5, 0x7c, 0x7b, 0x1a, 0x4d, 0xa6, + 0x68, 0x13, 0x47, 0xfa, 0x98, 0xc2, 0x0c, 0x7d, 0x0c, 0x5a, 0x8b, 0x6f, 0x1c, 0x75, 0xd7, 0xa1, + 0x38, 0xa7, 0xd0, 0xa8, 0xed, 0xe8, 0xba, 0x42, 0xdf, 0x42, 0xf7, 0x66, 0x7b, 0x2d, 0x29, 0x8c, + 0xbf, 0x94, 0x6c, 0x41, 0x65, 0x3b, 0x4e, 0x94, 0x33, 0x3c, 0x95, 0x24, 0x41, 0x68, 0x5d, 0x17, + 0x36, 0x2a, 0x35, 0x5f, 0x4e, 0x9b, 0x18, 0x3f, 0x8b, 0x84, 0x54, 0x57, 0x36, 0x87, 0x19, 0xa5, + 0x65, 0x59, 0x3a, 0x68, 0x63, 0x2a, 0xdf, 0x4f, 0x59, 0x5f, 0xa2, 0x4f, 0xa0, 0x88, 0x09, 0xeb, + 0xfb, 0x5c, 0xbe, 0xbf, 0x54, 0x1a, 0x57, 0x26, 0x48, 0x57, 0x4c, 0xf2, 0xac, 0x6a, 0x00, 0xfa, + 0x39, 0xcc, 0xab, 0x11, 0x33, 0x2b, 0x93, 0x9e, 0x0d, 0x32, 0x34, 0xd3, 0x18, 0xdd, 0x50, 0x68, + 0x4a, 0x1c, 0xc7, 0x4f, 0x49, 0x40, 0xf4, 0xbb, 0xa0, 0x68, 0x8d, 0xe7, 0x70, 0x62, 0x06, 0x35, + 0x60, 0x8e, 0x53, 0xb7, 0x4d, 0xcc, 0x85, 0x29, 0x5c, 0xa8, 0x58, 0x45, 0x62, 0x8b, 0xbc, 0x20, + 0x20, 0x1d, 0x73, 0x51, 0x55, 0x4a, 0x8a, 0x42, 0xdf, 0x85, 0xc5, 0xa0, 0xdf, 0x93, 0xcd, 0x42, + 0x67, 0x8f, 0x93, 0x88, 0x99, 0xe7, 0xe5, 0xf7, 0xc6, 0x66, 0xd1, 0x55, 0x58, 0x08, 0xfa, 0xbd, + 0x7d, 0x71, 0xc3, 0x2b, 0xb6, 0x9a, 0x64, 0x1b, 0x9d, 0x44, 0x37, 0x60, 0x49, 0xe0, 0xe2, 0xdd, + 0x56, 0x9c, 0x4b, 0x92, 0x33, 0xbd, 0xf0, 0x1e, 0x7a, 0xe6, 0xf7, 0xd1, 0x11, 0x58, 0xcf, 0xa1, + 0x9a, 0xdc, 0x87, 0x0c, 0xec, 0xed, 0xd1, 0x8e, 0x7b, 0x8a, 0xb8, 0x48, 0x34, 0x1c, 0xcf, 0xe1, + 0xdb, 0x4f, 0xa3, 0x8e, 0xcb, 0x49, 0x56, 0xe6, 0x4d, 0x67, 0xa0, 0x4b, 0x50, 0xdc, 0x55, 0x1b, + 0xa5, 0xde, 0x4b, 0x35, 0x25, 0xe6, 0x9b, 0x44, 0x38, 0x4f, 0xa7, 0x5b, 0x4d, 0xd9, 0x97, 0xc1, + 0xca, 0x12, 0xaf, 0x9c, 0x61, 0xff, 0x39, 0x07, 0x30, 0x0c, 0x06, 0xf4, 0x21, 0x40, 0x8f, 0x74, + 0x3c, 0xf7, 0x57, 0x7c, 0xd8, 0x50, 0x96, 0xe5, 0x8c, 0xec, 0x2a, 0x87, 0xa5, 0x7f, 0xee, 0x9d, + 0x4b, 0x7f, 0x04, 0x05, 0xe6, 0x7d, 0x45, 0x74, 0x99, 0x22, 0xc7, 0xe8, 0x09, 0x54, 0xdc, 0x20, + 0x08, 0xb9, 0x0c, 0xe3, 0xb8, 0xd9, 0xbe, 0x79, 0x5a, 0xf8, 0x3a, 0x9b, 0x43, 0x7e, 0x75, 0x4a, + 0x92, 0x12, 0xac, 0x7b, 0x50, 0x1b, 0x67, 0x98, 0xa9, 0x19, 0xfc, 0x7b, 0x0e, 0xce, 0x8f, 0x6d, + 0x1d, 0x7a, 0x00, 0x35, 0x45, 0x8d, 0x3d, 0x90, 0x9c, 0x75, 0xd0, 0x52, 0x28, 0x74, 0x1f, 0xaa, + 0x9b, 0x9c, 0x8b, 0x4c, 0xa8, 0xec, 0x55, 0x2d, 0xe0, 0xe9, 0x52, 0x46, 0x10, 0xe8, 0xc1, 0x30, + 0xad, 0xe4, 0x27, 0x35, 0xfa, 0x63, 0xfa, 0x67, 0xe7, 0x14, 0xeb, 0x17, 0x93, 0x83, 0x3c, 0xaf, + 0xbc, 0xd4, 0x18, 0x0d, 0xf2, 0x33, 0xb2, 0xca, 0xd0, 0x87, 0x7f, 0x34, 0xa0, 0x14, 0x1f, 0xc2, + 0xcc, 0xb7, 0x8a, 0xbb, 0xa3, 0x6f, 0x15, 0xd7, 0x26, 0x5f, 0x6a, 0xef, 0xf3, 0x89, 0xe2, 0xfa, + 0x4f, 0xe1, 0x83, 0xcc, 0x82, 0x02, 0x55, 0x60, 0x7e, 0x6f, 0x7f, 0x13, 0xef, 0xb7, 0x9a, 0xb5, + 0x73, 0xa8, 0x0a, 0xa5, 0xed, 0x27, 0x8f, 0x77, 0x1f, 0xb5, 0xf6, 0x5b, 0x35, 0x43, 0x2c, 0x35, + 0x5b, 0x62, 0xdc, 0xac, 0xe5, 0x1a, 0xbf, 0x2b, 0xc2, 0xfc, 0xb6, 0xfa, 0x67, 0x0c, 0xed, 0x43, + 0x79, 0xf0, 0x97, 0x09, 0xb2, 0x33, 0x5c, 0x33, 0xf6, 0xdf, 0x8b, 0xf5, 0xd1, 0xa9, 0x3c, 0xfa, + 0xc2, 0x79, 0x00, 0x73, 0xf2, 0xcf, 0x23, 0x94, 0xf1, 0x2c, 0x90, 0xfc, 0x57, 0xc9, 0x3a, 0xfd, + 0xcf, 0x98, 0x75, 0x43, 0x48, 0x92, 0x6f, 0x2a, 0x59, 0x92, 0x92, 0x0f, 0xb7, 0xd6, 0xca, 0x19, + 0x8f, 0x31, 0xe8, 0x31, 0x14, 0x75, 0xa3, 0x99, 0xc5, 0x9a, 0x7c, 0x39, 0xb1, 0x56, 0x27, 0x33, + 0x28, 0x61, 0xeb, 0x06, 0x7a, 0x3c, 0x78, 0x8b, 0xcf, 0x52, 0x2d, 0x59, 0xa5, 0x5b, 0x67, 0xac, + 0xaf, 0x19, 0xeb, 0x06, 0xfa, 0x02, 0x2a, 0x89, 0x3a, 0x1c, 0x65, 0x54, 0x81, 0xe9, 0xa2, 0xde, + 0xba, 0x76, 0x06, 0x97, 0xb6, 0xbc, 0x05, 0x05, 0x99, 0x00, 0x32, 0x9c, 0x9d, 0x28, 0xd3, 0xb3, + 0xd4, 0x1c, 0x29, 0xdb, 0x0f, 0x54, 0x63, 0x41, 0x82, 0x64, 0xf4, 0xa1, 0x6b, 0x67, 0xd5, 0x03, + 0x13, 0xc3, 0x26, 0x15, 0xc4, 0xeb, 0x06, 0x0a, 0x01, 0xa5, 0x93, 0x3e, 0xfa, 0x7e, 0x46, 0x94, + 0x4c, 0xba, 0x79, 0xac, 0x1b, 0xd3, 0x31, 0x2b, 0xa3, 0xb6, 0xaa, 0xaf, 0xde, 0x2c, 0x1b, 0xff, + 0x7c, 0xb3, 0x6c, 0xfc, 0xfb, 0xcd, 0xb2, 0x71, 0x50, 0x94, 0x95, 0xde, 0x0f, 0xbe, 0x09, 0x00, + 0x00, 0xff, 0xff, 0x16, 0xc8, 0xe5, 0x4c, 0x39, 0x1e, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -4405,11 +4395,9 @@ func (m *BuildResultInfo) MarshalToSizedBuffer(dAtA []byte) (int, error) { i-- dAtA[i] = 0x12 } - i -= len(k) - copy(dAtA[i:], k) - i = encodeVarintControl(dAtA, i, uint64(len(k))) + i = encodeVarintControl(dAtA, i, uint64(k)) i-- - dAtA[i] = 0xa + dAtA[i] = 0x8 i = encodeVarintControl(dAtA, i, uint64(baseI-i)) i-- dAtA[i] = 0x1a @@ -4468,13 +4456,6 @@ func (m *Exporter) MarshalToSizedBuffer(dAtA []byte) (int, error) { i -= len(m.XXX_unrecognized) copy(dAtA[i:], m.XXX_unrecognized) } - if len(m.ID) > 0 { - i -= len(m.ID) - copy(dAtA[i:], m.ID) - i = encodeVarintControl(dAtA, i, uint64(len(m.ID))) - i-- - dAtA[i] = 0x1a - } if len(m.Attrs) > 0 { for k := range m.Attrs { v := m.Attrs[k] @@ -5311,7 +5292,7 @@ func (m *BuildResultInfo) Size() (n int) { l = v.Size() l += 1 + sovControl(uint64(l)) } - mapEntrySize := 1 + len(k) + sovControl(uint64(len(k))) + l + mapEntrySize := 1 + sovControl(uint64(k)) + l n += mapEntrySize + 1 + sovControl(uint64(mapEntrySize)) } } @@ -5339,10 +5320,6 @@ func (m *Exporter) Size() (n int) { n += mapEntrySize + 1 + sovControl(uint64(mapEntrySize)) } } - l = len(m.ID) - if l > 0 { - n += 1 + l + sovControl(uint64(l)) - } if m.XXX_unrecognized != nil { n += len(m.XXX_unrecognized) } @@ -10827,9 +10804,9 @@ func (m *BuildResultInfo) Unmarshal(dAtA []byte) error { return io.ErrUnexpectedEOF } if m.Results == nil { - m.Results = make(map[string]*Descriptor) + m.Results = make(map[int64]*Descriptor) } - var mapkey string + var mapkey int64 var mapvalue *Descriptor for iNdEx < postIndex { entryPreIndex := iNdEx @@ -10850,7 +10827,6 @@ func (m *BuildResultInfo) Unmarshal(dAtA []byte) error { } fieldNum := int32(wire >> 3) if fieldNum == 1 { - var stringLenmapkey uint64 for shift := uint(0); ; shift += 7 { if shift >= 64 { return ErrIntOverflowControl @@ -10860,24 +10836,11 @@ func (m *BuildResultInfo) Unmarshal(dAtA []byte) error { } b := dAtA[iNdEx] iNdEx++ - stringLenmapkey |= uint64(b&0x7F) << shift + mapkey |= int64(b&0x7F) << shift if b < 0x80 { break } } - intStringLenmapkey := int(stringLenmapkey) - if intStringLenmapkey < 0 { - return ErrInvalidLengthControl - } - postStringIndexmapkey := iNdEx + intStringLenmapkey - if postStringIndexmapkey < 0 { - return ErrInvalidLengthControl - } - if postStringIndexmapkey > l { - return io.ErrUnexpectedEOF - } - mapkey = string(dAtA[iNdEx:postStringIndexmapkey]) - iNdEx = postStringIndexmapkey } else if fieldNum == 2 { var mapmsglen int for shift := uint(0); ; shift += 7 { @@ -11136,38 +11099,6 @@ func (m *Exporter) Unmarshal(dAtA []byte) error { } m.Attrs[mapkey] = mapvalue iNdEx = postIndex - case 3: - 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 ErrIntOverflowControl - } - 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 ErrInvalidLengthControl - } - postIndex := iNdEx + intStringLen - if postIndex < 0 { - return ErrInvalidLengthControl - } - if postIndex > l { - return io.ErrUnexpectedEOF - } - m.ID = string(dAtA[iNdEx:postIndex]) - 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 b863d478842d..919b1dd8c387 100644 --- a/api/services/control/control.proto +++ b/api/services/control/control.proto @@ -233,7 +233,7 @@ message Descriptor { message BuildResultInfo { Descriptor ResultDeprecated = 1; repeated Descriptor Attestations = 2; - map Results = 3; + map Results = 3; } // Exporter describes the output exporter @@ -242,6 +242,4 @@ message Exporter { string Type = 1; // Attrs specifies exporter configuration map Attrs = 2; - // ID identifies the exporter in the wire protocol - string id = 3 [(gogoproto.customname) = "ID"]; } diff --git a/client/client_test.go b/client/client_test.go index 150ad37af4a8..b364a3673bf8 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -2606,8 +2606,23 @@ func testMultipleExporters(t *testing.T, sb integration.Sandbox) { imageExporter = "moby" } + ref := identity.NewID() resp, err := c.Solve(sb.Context(), def, SolveOpt{ + Ref: ref, Exports: []ExportEntry{ + { + Type: imageExporter, + Attrs: map[string]string{ + "name": target1, + }, + }, + { + Type: imageExporter, + Attrs: map[string]string{ + "name": target2, + "oci-mediatypes": "true", + }, + }, // Ensure that multiple local exporter destinations are written properly { Type: ExporterLocal, @@ -2626,21 +2641,34 @@ func testMultipleExporters(t *testing.T, sb integration.Sandbox) { Type: ExporterTar, Output: fixedWriteCloser(outW2), }, - { - Type: imageExporter, - Attrs: map[string]string{ - "name": strings.Join([]string{target1, target2}, ","), - }, - }, }, }, nil) require.NoError(t, err) - require.Equal(t, resp.ExporterResponse["image.name"], target1+","+target2) + require.Equal(t, resp.ExporterResponse["image.name"], target2) require.FileExists(t, filepath.Join(destDir, "out.tar")) require.FileExists(t, filepath.Join(destDir, "out2.tar")) require.FileExists(t, filepath.Join(destDir, "foo.txt")) require.FileExists(t, filepath.Join(destDir2, "foo.txt")) + + history, err := c.ControlClient().ListenBuildHistory(sb.Context(), &controlapi.BuildHistoryRequest{ + Ref: ref, + EarlyExit: true, + }) + require.NoError(t, err) + for { + ev, err := history.Recv() + if err != nil { + require.Equal(t, io.EOF, err) + break + } + require.Equal(t, ref, ev.Record.Ref) + + require.Len(t, ev.Record.Result.Results, 2) + require.Equal(t, images.MediaTypeDockerSchema2Manifest, ev.Record.Result.Results[0].MediaType) + require.Equal(t, ocispecs.MediaTypeImageManifest, ev.Record.Result.Results[1].MediaType) + require.Equal(t, ev.Record.Result.Results[0], ev.Record.Result.ResultDeprecated) + } } func testOCIExporter(t *testing.T, sb integration.Sandbox) { diff --git a/client/solve.go b/client/solve.go index 386777285f04..6c4dcff67c41 100644 --- a/client/solve.go +++ b/client/solve.go @@ -4,7 +4,6 @@ import ( "context" "encoding/base64" "encoding/json" - "fmt" "io" "os" "path/filepath" @@ -131,25 +130,6 @@ func (c *Client) solve(ctx context.Context, def *llb.Definition, runGateway runG return nil, err } - type exporter struct { - ExportEntry - id string - } - - var exporters []exporter - ids := make(map[string]int) - for _, exp := range opt.Exports { - if id, ok := ids[exp.Type]; !ok { - ids[exp.Type] = 1 - } else { - ids[exp.Type] = id + 1 - } - exporters = append(exporters, exporter{ - ExportEntry: exp, - id: fmt.Sprint(exp.Type, ids[exp.Type]), - }) - } - storesToUpdate := []string{} if !opt.SessionPreInitialized { @@ -174,7 +154,7 @@ func (c *Client) solve(ctx context.Context, def *llb.Definition, runGateway runG } var syncTargets []filesync.FSSyncTarget - for _, ex := range exporters { + for exID, ex := range opt.Exports { var supportFile bool var supportDir bool switch ex.Type { @@ -199,7 +179,7 @@ func (c *Client) solve(ctx context.Context, def *llb.Definition, runGateway runG if ex.Output == nil { return nil, errors.Errorf("output file writer is required for %s exporter", ex.Type) } - syncTargets = append(syncTargets, filesync.WithFSSync(ex.id, ex.Output)) + syncTargets = append(syncTargets, filesync.WithFSSync(exID, ex.Output)) } if supportDir { if ex.OutputDir == "" { @@ -217,7 +197,7 @@ func (c *Client) solve(ctx context.Context, def *llb.Definition, runGateway runG contentStores["export"] = cs storesToUpdate = append(storesToUpdate, ex.OutputDir) default: - syncTargets = append(syncTargets, filesync.WithFSSyncDir(ex.id, ex.OutputDir)) + syncTargets = append(syncTargets, filesync.WithFSSyncDir(exID, ex.OutputDir)) } } } @@ -280,13 +260,12 @@ func (c *Client) solve(ctx context.Context, def *llb.Definition, runGateway runG exports := make([]*controlapi.Exporter, 0, len(opt.Exports)) exportDeprecated := "" exportAttrDeprecated := map[string]string{} - for i, exp := range exporters { + for i, exp := range opt.Exports { if i == 0 { exportDeprecated = exp.Type exportAttrDeprecated = exp.Attrs } exports = append(exports, &controlapi.Exporter{ - ID: exp.id, Type: exp.Type, Attrs: exp.Attrs, }) diff --git a/control/control.go b/control/control.go index 1901c6f7ab66..cae84984d683 100644 --- a/control/control.go +++ b/control/control.go @@ -366,12 +366,12 @@ func (c *Controller) Solve(ctx context.Context, req *controlapi.SolveRequest) (* } var expis []exporter.ExporterInstance - for _, ex := range req.Exporters { + for i, ex := range req.Exporters { exp, err := w.Exporter(ex.Type, c.opt.SessionManager) if err != nil { return nil, err } - expi, err := exp.Resolve(ctx, ex.ID, ex.Attrs) + expi, err := exp.Resolve(ctx, i, ex.Attrs) if err != nil { return nil, err } diff --git a/exporter/containerimage/export.go b/exporter/containerimage/export.go index c8550b8f7eac..26afb842dcc7 100644 --- a/exporter/containerimage/export.go +++ b/exporter/containerimage/export.go @@ -64,7 +64,7 @@ func New(opt Opt) (exporter.Exporter, error) { return im, nil } -func (e *imageExporter) Resolve(ctx context.Context, id string, opt map[string]string) (exporter.ExporterInstance, error) { +func (e *imageExporter) Resolve(ctx context.Context, id int, opt map[string]string) (exporter.ExporterInstance, error) { i := &imageExporterInstance{ imageExporter: e, id: id, @@ -168,7 +168,7 @@ func (e *imageExporter) Resolve(ctx context.Context, id string, opt map[string]s type imageExporterInstance struct { *imageExporter - id string + id int opts ImageCommitOpts push bool @@ -182,7 +182,7 @@ type imageExporterInstance struct { meta map[string][]byte } -func (e *imageExporterInstance) ID() string { +func (e *imageExporterInstance) ID() int { return e.id } diff --git a/exporter/exporter.go b/exporter/exporter.go index eb19ddc6bfd0..bbaf4b8a49a0 100644 --- a/exporter/exporter.go +++ b/exporter/exporter.go @@ -15,11 +15,11 @@ type Source = result.Result[cache.ImmutableRef] type Attestation = result.Attestation[cache.ImmutableRef] type Exporter interface { - Resolve(context.Context, string, map[string]string) (ExporterInstance, error) + Resolve(context.Context, int, map[string]string) (ExporterInstance, error) } type ExporterInstance interface { - ID() string + ID() int Name() string Config() *Config Export(ctx context.Context, src *Source, inlineCache exptypes.InlineCache, sessionID string) (map[string]string, DescriptorReference, error) diff --git a/exporter/local/export.go b/exporter/local/export.go index a45ba920617a..b6fa6866cb96 100644 --- a/exporter/local/export.go +++ b/exporter/local/export.go @@ -35,7 +35,7 @@ func New(opt Opt) (exporter.Exporter, error) { return le, nil } -func (e *localExporter) Resolve(ctx context.Context, id string, opt map[string]string) (exporter.ExporterInstance, error) { +func (e *localExporter) Resolve(ctx context.Context, id int, opt map[string]string) (exporter.ExporterInstance, error) { i := &localExporterInstance{ id: id, localExporter: e, @@ -50,12 +50,12 @@ func (e *localExporter) Resolve(ctx context.Context, id string, opt map[string]s type localExporterInstance struct { *localExporter - id string + id int opts CreateFSOpts } -func (e *localExporterInstance) ID() string { +func (e *localExporterInstance) ID() int { return e.id } diff --git a/exporter/oci/export.go b/exporter/oci/export.go index a788706b9e5e..3748f536caa4 100644 --- a/exporter/oci/export.go +++ b/exporter/oci/export.go @@ -58,7 +58,7 @@ func New(opt Opt) (exporter.Exporter, error) { return im, nil } -func (e *imageExporter) Resolve(ctx context.Context, id string, opt map[string]string) (exporter.ExporterInstance, error) { +func (e *imageExporter) Resolve(ctx context.Context, id int, opt map[string]string) (exporter.ExporterInstance, error) { i := &imageExporterInstance{ imageExporter: e, id: id, @@ -100,14 +100,14 @@ func (e *imageExporter) Resolve(ctx context.Context, id string, opt map[string]s type imageExporterInstance struct { *imageExporter - id string + id int opts containerimage.ImageCommitOpts tar bool meta map[string][]byte } -func (e *imageExporterInstance) ID() string { +func (e *imageExporterInstance) ID() int { return e.id } diff --git a/exporter/tar/export.go b/exporter/tar/export.go index 1d19f4b2e826..4f6afbd24f24 100644 --- a/exporter/tar/export.go +++ b/exporter/tar/export.go @@ -33,7 +33,7 @@ func New(opt Opt) (exporter.Exporter, error) { return le, nil } -func (e *localExporter) Resolve(ctx context.Context, id string, opt map[string]string) (exporter.ExporterInstance, error) { +func (e *localExporter) Resolve(ctx context.Context, id int, opt map[string]string) (exporter.ExporterInstance, error) { li := &localExporterInstance{ localExporter: e, id: id, @@ -49,12 +49,12 @@ func (e *localExporter) Resolve(ctx context.Context, id string, opt map[string]s type localExporterInstance struct { *localExporter - id string + id int opts local.CreateFSOpts } -func (e *localExporterInstance) ID() string { +func (e *localExporterInstance) ID() int { return e.id } diff --git a/session/filesync/filesync.go b/session/filesync/filesync.go index d781e02aee99..dc3402f2d3b8 100644 --- a/session/filesync/filesync.go +++ b/session/filesync/filesync.go @@ -240,7 +240,7 @@ type FSSyncTarget interface { } type fsSyncTarget struct { - id string + id int outdir string f FileOutputFunc } @@ -249,14 +249,14 @@ func (target *fsSyncTarget) target() *fsSyncTarget { return target } -func WithFSSync(id string, f FileOutputFunc) FSSyncTarget { +func WithFSSync(id int, f FileOutputFunc) FSSyncTarget { return &fsSyncTarget{ id: id, f: f, } } -func WithFSSyncDir(id string, outdir string) FSSyncTarget { +func WithFSSyncDir(id int, outdir string) FSSyncTarget { return &fsSyncTarget{ id: id, outdir: outdir, @@ -264,9 +264,8 @@ func WithFSSyncDir(id string, outdir string) FSSyncTarget { } func NewFSSyncTarget(targets ...FSSyncTarget) session.Attachable { - fs := make(map[string]FileOutputFunc) - outdirs := make(map[string]string) - defaultID := "" + fs := make(map[int]FileOutputFunc) + outdirs := make(map[int]string) for _, t := range targets { t := t.target() if t.f != nil { @@ -275,39 +274,36 @@ func NewFSSyncTarget(targets ...FSSyncTarget) session.Attachable { if t.outdir != "" { outdirs[t.id] = t.outdir } - if defaultID == "" { - defaultID = t.id - } } return &fsSyncAttachable{ - fs: fs, - outdirs: outdirs, - defaultID: defaultID, + fs: fs, + outdirs: outdirs, } } type fsSyncAttachable struct { - fs map[string]FileOutputFunc - outdirs map[string]string - - defaultID string + fs map[int]FileOutputFunc + outdirs map[int]string } func (sp *fsSyncAttachable) Register(server *grpc.Server) { RegisterFileSendServer(server, sp) } -func (sp *fsSyncAttachable) chooser(ctx context.Context) string { +func (sp *fsSyncAttachable) chooser(ctx context.Context) int { md, ok := metadata.FromIncomingContext(ctx) if !ok { - return sp.defaultID + return 0 } values := md[keyExporterID] if len(values) == 0 { - return sp.defaultID + return 0 + } + id, err := strconv.ParseInt(values[0], 10, 64) + if err != nil { + return 0 } - id := values[0] - return id + return int(id) } func (sp *fsSyncAttachable) DiffCopy(stream FileSend_DiffCopyServer) (err error) { @@ -317,7 +313,7 @@ func (sp *fsSyncAttachable) DiffCopy(stream FileSend_DiffCopyServer) (err error) } f, ok := sp.fs[id] if !ok { - return errors.Errorf("exporter %q not found", id) + return errors.Errorf("exporter %d not found", id) } opts, _ := metadata.FromIncomingContext(stream.Context()) // if no metadata continue with empty object @@ -343,7 +339,7 @@ func (sp *fsSyncAttachable) DiffCopy(stream FileSend_DiffCopyServer) (err error) return writeTargetFile(stream, wc) } -func CopyToCaller(ctx context.Context, fs fsutil.FS, id string, c session.Caller, progress func(int, bool)) error { +func CopyToCaller(ctx context.Context, fs fsutil.FS, id int, c session.Caller, progress func(int, bool)) error { method := session.MethodURL(_FileSend_serviceDesc.ServiceName, "diffcopy") if !c.Supports(method) { return errors.Errorf("method %s not supported by the client", method) @@ -352,7 +348,7 @@ func CopyToCaller(ctx context.Context, fs fsutil.FS, id string, c session.Caller client := NewFileSendClient(c.Conn()) opts := map[string][]string{ - keyExporterID: {id}, + keyExporterID: {fmt.Sprint(id)}, } ctx = metadata.NewOutgoingContext(ctx, opts) @@ -364,7 +360,7 @@ func CopyToCaller(ctx context.Context, fs fsutil.FS, id string, c session.Caller return sendDiffCopy(cc, fs, progress) } -func CopyFileWriter(ctx context.Context, md map[string]string, id string, c session.Caller) (io.WriteCloser, error) { +func CopyFileWriter(ctx context.Context, md map[string]string, id int, c session.Caller) (io.WriteCloser, error) { method := session.MethodURL(_FileSend_serviceDesc.ServiceName, "diffcopy") if !c.Supports(method) { return nil, errors.Errorf("method %s not supported by the client", method) @@ -386,7 +382,7 @@ func CopyFileWriter(ctx context.Context, md map[string]string, id string, c sess if existingVal, ok := opts[keyExporterID]; ok { bklog.G(ctx).Warnf("overwriting grpc metadata key %q from value %+v to %+v", keyExporterID, existingVal, id) } - opts[keyExporterID] = []string{id} + opts[keyExporterID] = []string{fmt.Sprint(id)} ctx = metadata.NewOutgoingContext(ctx, opts) cc, err := client.DiffCopy(ctx) diff --git a/solver/llbsolver/solver.go b/solver/llbsolver/solver.go index e13be476c89f..8b516223a783 100644 --- a/solver/llbsolver/solver.go +++ b/solver/llbsolver/solver.go @@ -149,7 +149,7 @@ func (s *Solver) Bridge(b solver.Builder) frontend.FrontendLLBBridge { return s.bridge(b) } -func (s *Solver) recordBuildHistory(ctx context.Context, id string, req frontend.SolveRequest, exp ExporterRequest, j *solver.Job, usage *resources.SysSampler) (func(*Result, map[string]exporter.DescriptorReference, error) error, error) { +func (s *Solver) recordBuildHistory(ctx context.Context, id string, req frontend.SolveRequest, exp ExporterRequest, j *solver.Job, usage *resources.SysSampler) (func(*Result, []exporter.DescriptorReference, error) error, error) { var stopTrace func() []tracetest.SpanStub if s := trace.SpanFromContext(ctx); s.SpanContext().IsValid() { @@ -182,7 +182,7 @@ func (s *Solver) recordBuildHistory(ctx context.Context, id string, req frontend return nil, err } - return func(res *Result, descrefs map[string]exporter.DescriptorReference, err error) error { + return func(res *Result, descrefs []exporter.DescriptorReference, err error) error { en := time.Now() rec.CompletedAt = &en @@ -314,16 +314,16 @@ func (s *Solver) recordBuildHistory(ctx context.Context, id string, req frontend return j.Status(ctx2, ch) }) - for k, descref := range descrefs { - k, descref := k, descref + setDeprecated := true + for i, descref := range descrefs { + i, descref := i, descref + if descref == nil { + continue + } + deprecate := setDeprecated + setDeprecated = false eg.Go(func() error { mu.Lock() - if rec.Result == nil { - rec.Result = &controlapi.BuildResultInfo{} - } - if rec.Result.Results == nil { - rec.Result.Results = make(map[string]*controlapi.Descriptor) - } desc := descref.Descriptor() controlDesc := &controlapi.Descriptor{ Digest: desc.Digest, @@ -331,15 +331,22 @@ func (s *Solver) recordBuildHistory(ctx context.Context, id string, req frontend MediaType: desc.MediaType, Annotations: desc.Annotations, } - if rec.Result.ResultDeprecated == nil { + if rec.Result == nil { + rec.Result = &controlapi.BuildResultInfo{} + } + if rec.Result.Results == nil { + rec.Result.Results = make(map[int64]*controlapi.Descriptor) + } + if deprecate { + // write the first available descriptor to the deprecated + // field for legacy clients rec.Result.ResultDeprecated = controlDesc } - rec.Result.Results[k] = controlDesc + rec.Result.Results[int64(i)] = controlDesc mu.Unlock() return nil }) } - if err1 := eg.Wait(); err == nil { err = err1 } @@ -433,7 +440,7 @@ func (s *Solver) Solve(ctx context.Context, id string, sessionID string, req fro var res *frontend.Result var resProv *Result - var descrefs map[string]exporter.DescriptorReference + var descrefs []exporter.DescriptorReference var releasers []func() defer func() { @@ -441,7 +448,9 @@ func (s *Solver) Solve(ctx context.Context, id string, sessionID string, req fro f() } for _, descref := range descrefs { - descref.Release() + if descref != nil { + descref.Release() + } } }() @@ -665,7 +674,7 @@ func runInlineCacheExporter(ctx context.Context, e exporter.ExporterInstance, in return res, done(err) } -func (s *Solver) runExporters(ctx context.Context, exporters []exporter.ExporterInstance, inlineCacheExporter inlineCacheExporter, job *solver.Job, cached *result.Result[solver.CachedResult], inp *result.Result[cache.ImmutableRef]) (exporterResponse map[string]string, descrefs map[string]exporter.DescriptorReference, err error) { +func (s *Solver) runExporters(ctx context.Context, exporters []exporter.ExporterInstance, inlineCacheExporter inlineCacheExporter, job *solver.Job, cached *result.Result[solver.CachedResult], inp *result.Result[cache.ImmutableRef]) (exporterResponse map[string]string, descrefs []exporter.DescriptorReference, err error) { eg, ctx := errgroup.WithContext(ctx) resps := make([]map[string]string, len(exporters)) descs := make([]exporter.DescriptorReference, len(exporters)) @@ -701,17 +710,7 @@ func (s *Solver) runExporters(ctx context.Context, exporters []exporter.Exporter } } - for i, desc := range descs { - if desc == nil { - continue - } - if descrefs == nil { - descrefs = make(map[string]exporter.DescriptorReference) - } - descrefs[exporters[i].ID()] = desc - } - - return exporterResponse, descrefs, nil + return exporterResponse, descs, nil } func splitCacheExporters(exporters []RemoteCacheExporter) (rest []RemoteCacheExporter, inline inlineCacheExporter) { From cfd320c5b7d4eedc80c932d97f692272d65f4180 Mon Sep 17 00:00:00 2001 From: Justin Chadwell Date: Fri, 5 Jan 2024 16:09:38 +0000 Subject: [PATCH 12/12] control: fix typo in exporter slice comment Signed-off-by: Justin Chadwell --- control/control.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/control/control.go b/control/control.go index cae84984d683..56a96901de36 100644 --- a/control/control.go +++ b/control/control.go @@ -325,7 +325,7 @@ func translateLegacySolveRequest(req *controlapi.SolveRequest) { } req.Cache.ImportRefsDeprecated = nil - // translate single exporter to a slice (v0.12.0) + // translate single exporter to a slice (v0.13.0) if len(req.Exporters) == 0 && req.ExporterDeprecated != "" { req.Exporters = append(req.Exporters, &controlapi.Exporter{ Type: req.ExporterDeprecated,