From 6d6f5058d62d0b30b7fd00e4471e1a29c60e8a81 Mon Sep 17 00:00:00 2001 From: fahed dorgaa Date: Tue, 10 Nov 2020 21:35:02 +0100 Subject: [PATCH 1/7] enable concurrent exports. In order to support multiple local exporters, a new gRPC API has been added in order to distinguish the origin of the message packet. Also, I felt that the solver/result package was slightly confusing with its use of the mutex to protect some metadata access patterns while not providing enough protection for potential data races. Instead, where necessary to protect against data races, the result values are explicitly duplicated (including the metadata). Signed-off-by: fahed dorgaa Signed-off-by: a-palchikov --- api/services/control/control.pb.go | 487 ++++++++++++++-------- api/services/control/control.proto | 12 +- client/client_test.go | 75 +++- client/solve.go | 143 ++++--- control/control.go | 32 +- exporter/containerimage/export.go | 26 +- exporter/containerimage/exptypes/types.go | 6 + exporter/containerimage/opts.go | 1 + exporter/containerimage/writer.go | 8 +- exporter/oci/export.go | 32 +- exporter/tar/export.go | 4 +- session/filesync/diffcopy.go | 78 +++- session/filesync/filesync.go | 72 ++-- session/filesync/filesync.pb.go | 199 ++++++++- session/filesync/filesync.proto | 8 +- solver/llbsolver/solver.go | 112 +++-- solver/result/result.go | 13 - worker/base/worker.go | 5 +- worker/worker.go | 2 +- 19 files changed, 938 insertions(+), 377 deletions(-) diff --git a/api/services/control/control.pb.go b/api/services/control/control.pb.go index 2567a0d9700a..b9251d17f1ab 100644 --- a/api/services/control/control.pb.go +++ b/api/services/control/control.pb.go @@ -367,21 +367,22 @@ 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 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 +432,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 +495,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 @@ -1886,12 +1894,17 @@ func (m *BuildResultInfo) GetAttestations() []*Descriptor { 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 +1954,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 +1968,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") @@ -1986,149 +2006,151 @@ func init() { func init() { proto.RegisterFile("control.proto", fileDescriptor_0c5120591600887d) } var fileDescriptor_0c5120591600887d = []byte{ - // 2261 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, 0x75, 0xab, 0xeb, 0xf1, - 0x17, 0xfd, 0x43, 0xa7, 0x1d, 0xf6, 0x6a, 0xdd, 0xb0, 0x1b, 0xd6, 0x24, 0xe3, 0x61, 0xff, 0x48, - 0x52, 0x92, 0x90, 0x23, 0x25, 0xc0, 0x5a, 0xed, 0x86, 0x61, 0xd7, 0x27, 0x23, 0x2e, 0xee, 0xf5, - 0x08, 0xe3, 0x6e, 0x2f, 0xd2, 0x0c, 0x37, 0x13, 0xf2, 0xc4, 0xc7, 0x6a, 0xf1, 0xc7, 0x6a, 0x2c, - 0xf4, 0x4f, 0x08, 0xad, 0x45, 0x87, 0xb5, 0x30, 0x62, 0x9a, 0xbb, 0x36, 0x95, 0xdb, 0x8d, 0xbc, - 0x1a, 0x1f, 0x44, 0x84, 0xd5, 0xbe, 0x0e, 0xe9, 0x31, 0xa1, 0x1a, 0x50, 0x9f, 0x54, 0x57, 0xe9, - 0xe3, 0x46, 0x1e, 0xd3, 0xc3, 0x1a, 0x8d, 0xda, 0x35, 0xc6, 0x5d, 0xde, 0x8f, 0x3f, 0x72, 0xfb, - 0x14, 0x95, 0xfa, 0xb4, 0x4d, 0xa2, 0xd0, 0xf7, 0xda, 0x03, 0xa1, 0x98, 0x1a, 0x29, 0x98, 0xfd, - 0x5b, 0x03, 0xaa, 0x7b, 0xb4, 0x1f, 0x10, 0x4c, 0x7e, 0xd3, 0x27, 0x8c, 0xa3, 0xcb, 0x50, 0x38, - 0xf2, 0x7c, 0x4e, 0xa8, 0x69, 0xac, 0xe5, 0xd7, 0xcb, 0x58, 0x53, 0x68, 0x09, 0xf2, 0xae, 0xef, - 0x9b, 0xb9, 0x35, 0x63, 0xbd, 0x84, 0xc5, 0x10, 0xad, 0x43, 0xf5, 0x98, 0x90, 0xa8, 0xd9, 0xa7, - 0x2e, 0xf7, 0xc2, 0xc0, 0xcc, 0xaf, 0x19, 0xeb, 0xf9, 0xc6, 0xdc, 0xab, 0xd7, 0xab, 0x06, 0x1e, - 0x5b, 0x41, 0x36, 0x94, 0x05, 0xdd, 0x18, 0x70, 0xc2, 0xcc, 0xb9, 0x04, 0xdb, 0x68, 0xda, 0xbe, - 0x01, 0x4b, 0x4d, 0x8f, 0x1d, 0x3f, 0x65, 0x6e, 0xf7, 0x2c, 0x5d, 0xec, 0x47, 0xb0, 0x9c, 0xe0, - 0x65, 0x51, 0x18, 0x30, 0x82, 0x6e, 0x43, 0x81, 0x92, 0x76, 0x48, 0x3b, 0x92, 0xb9, 0x52, 0xff, - 0xc8, 0x99, 0x0c, 0x03, 0x47, 0x03, 0x04, 0x13, 0xd6, 0xcc, 0xf6, 0x9f, 0xf2, 0x50, 0x49, 0xcc, - 0xa3, 0x45, 0xc8, 0xed, 0x34, 0x4d, 0x63, 0xcd, 0x58, 0x2f, 0xe3, 0xdc, 0x4e, 0x13, 0x99, 0x50, - 0xdc, 0xed, 0x73, 0xf7, 0xd0, 0x27, 0xda, 0xf6, 0x98, 0x44, 0x97, 0x60, 0x7e, 0x27, 0x78, 0xca, - 0x88, 0x34, 0xbc, 0x84, 0x15, 0x81, 0x10, 0xcc, 0xed, 0x7b, 0xdf, 0x10, 0x65, 0x26, 0x96, 0x63, - 0x64, 0x41, 0x61, 0xcf, 0xa5, 0x24, 0xe0, 0xe6, 0xbc, 0x90, 0xdb, 0xc8, 0x99, 0x06, 0xd6, 0x33, - 0xa8, 0x01, 0xe5, 0x6d, 0x4a, 0x5c, 0x4e, 0x3a, 0x5b, 0xdc, 0x2c, 0xac, 0x19, 0xeb, 0x95, 0xba, - 0xe5, 0xa8, 0x4d, 0x76, 0xe2, 0xf8, 0x73, 0x0e, 0xe2, 0xf8, 0x6b, 0x94, 0x5e, 0xbd, 0x5e, 0xfd, - 0xe0, 0x0f, 0xff, 0x14, 0xbe, 0x1b, 0xc2, 0xd0, 0x03, 0x80, 0xc7, 0x2e, 0xe3, 0x4f, 0x99, 0x14, - 0x52, 0x3c, 0x53, 0xc8, 0x9c, 0x14, 0x90, 0xc0, 0xa0, 0x15, 0x00, 0xe9, 0x84, 0xed, 0xb0, 0x1f, - 0x70, 0xb3, 0x24, 0x75, 0x4f, 0xcc, 0xa0, 0x35, 0xa8, 0x34, 0x09, 0x6b, 0x53, 0x2f, 0x92, 0x5b, - 0x5d, 0x96, 0xee, 0x49, 0x4e, 0x09, 0x09, 0xca, 0x83, 0x07, 0x83, 0x88, 0x98, 0x20, 0x19, 0x12, - 0x33, 0x62, 0x2f, 0xf7, 0x5f, 0xb8, 0x94, 0x74, 0xcc, 0x8a, 0x74, 0x97, 0xa6, 0x84, 0x7f, 0x95, - 0x27, 0x98, 0x59, 0x95, 0x9b, 0x1c, 0x93, 0xf6, 0xef, 0x8a, 0x50, 0xdd, 0x17, 0xc7, 0x29, 0x0e, - 0x87, 0x25, 0xc8, 0x63, 0x72, 0xa4, 0xf7, 0x46, 0x0c, 0x91, 0x03, 0xd0, 0x24, 0x47, 0x5e, 0xe0, - 0x49, 0xad, 0x72, 0xd2, 0xf0, 0x45, 0x27, 0x3a, 0x74, 0x46, 0xb3, 0x38, 0xc1, 0x81, 0x2c, 0x28, - 0xb5, 0x5e, 0x46, 0x21, 0x15, 0x21, 0x95, 0x97, 0x62, 0x86, 0x34, 0x7a, 0x06, 0x0b, 0xf1, 0x78, - 0x8b, 0x73, 0x2a, 0x02, 0x55, 0x84, 0xd1, 0x27, 0xe9, 0x30, 0x4a, 0x2a, 0xe5, 0x8c, 0x61, 0x5a, - 0x01, 0xa7, 0x03, 0x3c, 0x2e, 0x47, 0x58, 0xb8, 0x4f, 0x18, 0x13, 0x1a, 0xca, 0xed, 0xc7, 0x31, - 0x29, 0xd4, 0xf9, 0x05, 0x0d, 0x03, 0x4e, 0x82, 0x8e, 0xdc, 0xfa, 0x32, 0x1e, 0xd2, 0x42, 0x9d, - 0x78, 0xac, 0xd4, 0x29, 0xce, 0xa4, 0xce, 0x18, 0x46, 0xab, 0x33, 0x36, 0x87, 0x36, 0x61, 0x7e, - 0xdb, 0x6d, 0xbf, 0x20, 0x72, 0x97, 0x2b, 0xf5, 0x95, 0xb4, 0x40, 0xb9, 0xfc, 0x44, 0x6e, 0x2b, - 0x93, 0x07, 0xf5, 0x03, 0xac, 0x20, 0xe8, 0x57, 0x50, 0x6d, 0x05, 0xdc, 0xe3, 0x3e, 0xe9, 0xc9, - 0x1d, 0x2b, 0x8b, 0x1d, 0x6b, 0x6c, 0x7e, 0xf7, 0x7a, 0xf5, 0x27, 0x53, 0xd3, 0x4f, 0x9f, 0x7b, - 0x7e, 0x8d, 0x24, 0x50, 0x4e, 0x42, 0x04, 0x1e, 0x93, 0x87, 0xbe, 0x82, 0xc5, 0x58, 0xd9, 0x9d, - 0x20, 0xea, 0x73, 0x66, 0x82, 0xb4, 0xba, 0x3e, 0xa3, 0xd5, 0x0a, 0xa4, 0xcc, 0x9e, 0x90, 0x24, - 0x9c, 0xbd, 0x13, 0x70, 0x42, 0x03, 0xd7, 0xd7, 0x21, 0x38, 0xa4, 0xd1, 0x8e, 0x88, 0x34, 0x91, - 0x25, 0xf7, 0x64, 0x6e, 0x34, 0xab, 0xd2, 0x35, 0xd7, 0xd3, 0x5f, 0x4d, 0xe6, 0x52, 0x47, 0x31, - 0xe3, 0x31, 0xa8, 0xf5, 0x00, 0x50, 0x3a, 0x24, 0x44, 0xe8, 0x1e, 0x93, 0x41, 0x1c, 0xba, 0xc7, - 0x64, 0x20, 0xb2, 0xc7, 0x89, 0xeb, 0xf7, 0x55, 0x56, 0x29, 0x63, 0x45, 0x6c, 0xe6, 0xee, 0x1a, - 0x42, 0x42, 0x7a, 0x17, 0xcf, 0x25, 0xe1, 0x0b, 0xb8, 0x98, 0xe1, 0x91, 0x0c, 0x11, 0xd7, 0x92, - 0x22, 0xd2, 0x47, 0x67, 0x24, 0xd2, 0xfe, 0x6b, 0x1e, 0xaa, 0xc9, 0xb8, 0x40, 0x1b, 0x70, 0x51, - 0xd9, 0x89, 0xc9, 0x51, 0x93, 0x44, 0x94, 0xb4, 0x45, 0x32, 0xd2, 0xc2, 0xb3, 0x96, 0x50, 0x1d, - 0x2e, 0xed, 0xf4, 0xf4, 0x34, 0x4b, 0x40, 0x72, 0xf2, 0xd8, 0x67, 0xae, 0xa1, 0x10, 0x3e, 0x54, - 0xa2, 0xa4, 0x27, 0x12, 0xa0, 0xbc, 0x8c, 0x8b, 0xcf, 0x4e, 0x0f, 0x5e, 0x27, 0x13, 0xab, 0xc2, - 0x23, 0x5b, 0x2e, 0xfa, 0x19, 0x14, 0xd5, 0x42, 0x7c, 0xfe, 0x3f, 0x3e, 0xfd, 0x13, 0x4a, 0x58, - 0x8c, 0x11, 0x70, 0x65, 0x07, 0x33, 0xe7, 0xcf, 0x01, 0xd7, 0x18, 0xeb, 0x21, 0x58, 0xd3, 0x55, - 0x3e, 0x4f, 0x08, 0xd8, 0x7f, 0x31, 0x60, 0x39, 0xf5, 0x21, 0x71, 0x39, 0xc9, 0xf4, 0xac, 0x44, - 0xc8, 0x31, 0x6a, 0xc2, 0xbc, 0x4a, 0x30, 0x39, 0xa9, 0xb0, 0x33, 0x83, 0xc2, 0x4e, 0x22, 0xbb, - 0x28, 0xb0, 0x75, 0x17, 0xe0, 0xed, 0x82, 0xd5, 0xfe, 0x9b, 0x01, 0x0b, 0xfa, 0x30, 0xeb, 0x9b, - 0xdc, 0x85, 0xa5, 0xf8, 0x08, 0xc5, 0x73, 0xfa, 0x4e, 0xbf, 0x3d, 0x35, 0x0f, 0x28, 0x36, 0x67, - 0x12, 0xa7, 0x74, 0x4c, 0x89, 0xb3, 0xb6, 0xe3, 0xb8, 0x9a, 0x60, 0x3d, 0x97, 0xe6, 0x57, 0x61, - 0x61, 0x5f, 0x96, 0x60, 0x53, 0x2f, 0x28, 0xfb, 0x3f, 0x06, 0x2c, 0xc6, 0x3c, 0xda, 0xba, 0x4f, - 0xa1, 0x74, 0x42, 0x28, 0x27, 0x2f, 0x09, 0xd3, 0x56, 0x99, 0x69, 0xab, 0xbe, 0x94, 0x1c, 0x78, - 0xc8, 0x89, 0x36, 0xa1, 0xa4, 0xca, 0x3d, 0x12, 0x6f, 0xd4, 0xca, 0x34, 0x94, 0xfe, 0xde, 0x90, - 0x1f, 0xd5, 0x60, 0xce, 0x0f, 0xbb, 0x4c, 0x9f, 0x99, 0xef, 0x4f, 0xc3, 0x3d, 0x0e, 0xbb, 0x58, - 0x32, 0xa2, 0x7b, 0x50, 0xfa, 0xda, 0xa5, 0x81, 0x17, 0x74, 0xe3, 0x53, 0xb0, 0x3a, 0x0d, 0xf4, - 0x4c, 0xf1, 0xe1, 0x21, 0x40, 0x14, 0x54, 0x05, 0xb5, 0x86, 0x1e, 0x41, 0xa1, 0xe3, 0x75, 0x09, - 0xe3, 0xca, 0x25, 0x8d, 0xba, 0xb8, 0x4b, 0xbe, 0x7b, 0xbd, 0x7a, 0x23, 0x71, 0x59, 0x84, 0x11, - 0x09, 0x44, 0xf9, 0xee, 0x7a, 0x01, 0xa1, 0xa2, 0xbc, 0xbd, 0xa5, 0x20, 0x4e, 0x53, 0xfe, 0x60, - 0x2d, 0x41, 0xc8, 0xf2, 0xd4, 0x95, 0x20, 0xf3, 0xc5, 0xdb, 0xc9, 0x52, 0x12, 0xc4, 0x31, 0x08, - 0xdc, 0x1e, 0xd1, 0x25, 0x80, 0x1c, 0x8b, 0xfa, 0xa4, 0x2d, 0xe2, 0xbc, 0x23, 0x2b, 0xb7, 0x12, - 0xd6, 0x14, 0xda, 0x84, 0x22, 0xe3, 0x2e, 0x15, 0x39, 0x67, 0x7e, 0xc6, 0xc2, 0x2a, 0x06, 0xa0, - 0xfb, 0x50, 0x6e, 0x87, 0xbd, 0xc8, 0x27, 0x02, 0x5d, 0x98, 0x11, 0x3d, 0x82, 0x88, 0xd0, 0x23, - 0x94, 0x86, 0x54, 0x96, 0x74, 0x65, 0xac, 0x08, 0x74, 0x07, 0x16, 0x22, 0x1a, 0x76, 0x29, 0x61, - 0xec, 0x73, 0x1a, 0xf6, 0x23, 0x7d, 0x91, 0x2f, 0x8b, 0xe4, 0xbd, 0x97, 0x5c, 0xc0, 0xe3, 0x7c, - 0xf6, 0xbf, 0x73, 0x50, 0x4d, 0x86, 0x48, 0xaa, 0xd6, 0x7d, 0x04, 0x05, 0x15, 0x70, 0x2a, 0xd6, - 0xdf, 0xce, 0xc7, 0x4a, 0x42, 0xa6, 0x8f, 0x4d, 0x28, 0xb6, 0xfb, 0x54, 0x16, 0xc2, 0xaa, 0x3c, - 0x8e, 0x49, 0x61, 0x29, 0x0f, 0xb9, 0xeb, 0x4b, 0x1f, 0xe7, 0xb1, 0x22, 0x44, 0x6d, 0x3c, 0xec, - 0xbc, 0xce, 0x57, 0x1b, 0x0f, 0x61, 0xc9, 0xfd, 0x2b, 0xbe, 0xd3, 0xfe, 0x95, 0xce, 0xbd, 0x7f, - 0xf6, 0xdf, 0x0d, 0x28, 0x0f, 0xcf, 0x56, 0xc2, 0xbb, 0xc6, 0x3b, 0x7b, 0x77, 0xcc, 0x33, 0xb9, - 0xb7, 0xf3, 0xcc, 0x65, 0x28, 0x30, 0x4e, 0x89, 0xdb, 0x53, 0x9d, 0x1b, 0xd6, 0x94, 0xc8, 0x62, - 0x3d, 0xd6, 0x95, 0x3b, 0x54, 0xc5, 0x62, 0x68, 0xff, 0xd7, 0x80, 0x85, 0xb1, 0xe3, 0xfe, 0x5e, - 0x6d, 0xb9, 0x04, 0xf3, 0x3e, 0x39, 0x21, 0xaa, 0xb7, 0xcc, 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, 0x5f, 0x8b, 0x21, 0xb2, 0x61, 0xce, 0x0b, 0x8e, 0x42, - 0x1d, 0x36, 0xb2, 0xb2, 0x51, 0x75, 0xda, 0x4e, 0x70, 0x14, 0x62, 0xb9, 0x86, 0xae, 0x42, 0x81, - 0xba, 0x41, 0x97, 0xc4, 0xc5, 0x75, 0x59, 0x70, 0x61, 0x31, 0x83, 0xf5, 0x82, 0x6d, 0x43, 0x55, - 0xf6, 0xa7, 0xbb, 0x84, 0x89, 0x6e, 0x48, 0x84, 0x75, 0xc7, 0xe5, 0xae, 0x34, 0xbb, 0x8a, 0xe5, - 0xd8, 0xbe, 0x09, 0xe8, 0xb1, 0xc7, 0xf8, 0x33, 0xd9, 0xc2, 0xb3, 0xb3, 0x9a, 0xd7, 0x7d, 0xb8, - 0x38, 0xc6, 0xad, 0xaf, 0x85, 0x9f, 0x4e, 0xb4, 0xaf, 0xd7, 0xd2, 0x19, 0x57, 0xbe, 0x14, 0x38, - 0x0a, 0x38, 0xd1, 0xc5, 0x2e, 0x40, 0x45, 0xda, 0xa5, 0xbe, 0x6d, 0xbb, 0x50, 0x55, 0xa4, 0x16, - 0xfe, 0x05, 0x5c, 0x88, 0x05, 0x7d, 0x49, 0xa8, 0x6c, 0x45, 0x0c, 0xe9, 0x97, 0x1f, 0x4e, 0xfb, - 0x4a, 0x63, 0x9c, 0x1d, 0x4f, 0xe2, 0x6d, 0x02, 0x17, 0x25, 0xcf, 0x43, 0x8f, 0xf1, 0x90, 0x0e, - 0x62, 0xab, 0x57, 0x00, 0xb6, 0xda, 0xdc, 0x3b, 0x21, 0x4f, 0x02, 0x5f, 0x5d, 0xa3, 0x25, 0x9c, - 0x98, 0x89, 0xaf, 0xc8, 0xdc, 0xa8, 0x87, 0xbb, 0x02, 0xe5, 0x96, 0x4b, 0xfd, 0x41, 0xeb, 0xa5, - 0xc7, 0x75, 0x2b, 0x3d, 0x9a, 0xb0, 0x7f, 0x6f, 0xc0, 0x72, 0xf2, 0x3b, 0xad, 0x13, 0x91, 0x2e, - 0xee, 0xc1, 0x1c, 0x8f, 0xeb, 0x98, 0xc5, 0x2c, 0x23, 0x52, 0x10, 0x51, 0xea, 0x60, 0x09, 0x4a, - 0x78, 0x5a, 0x1d, 0x9c, 0x6b, 0xa7, 0xc3, 0x27, 0x3c, 0xfd, 0xbf, 0x12, 0xa0, 0xf4, 0x72, 0x46, - 0x6f, 0x9a, 0x6c, 0xee, 0x72, 0x13, 0xcd, 0xdd, 0xf3, 0xc9, 0xe6, 0x4e, 0x5d, 0xcd, 0x77, 0x66, - 0xd1, 0x64, 0x86, 0x16, 0xef, 0x2e, 0x94, 0xe3, 0xea, 0x26, 0xbe, 0xc0, 0xad, 0xb4, 0xe8, 0x61, - 0x01, 0x34, 0x62, 0x46, 0xeb, 0xf1, 0x8d, 0xa3, 0xee, 0x3a, 0x14, 0xe7, 0x14, 0x1a, 0xb5, 0x1d, - 0x5d, 0x57, 0xe8, 0x5b, 0xe8, 0xfe, 0xf9, 0xde, 0x2d, 0xe6, 0x26, 0xdf, 0x2c, 0x1a, 0x50, 0xd9, - 0x8e, 0x13, 0xe5, 0x39, 0x1e, 0x2d, 0x92, 0x20, 0xb4, 0xa1, 0x0b, 0x1b, 0x95, 0x9a, 0xaf, 0xa4, - 0x4d, 0x8c, 0x1f, 0x28, 0x42, 0xaa, 0x2b, 0x9b, 0xa3, 0x8c, 0xd2, 0xb2, 0x2c, 0x1d, 0xb4, 0x39, - 0x93, 0xef, 0x67, 0xac, 0x2f, 0xd1, 0x67, 0x50, 0xc0, 0x84, 0xf5, 0x7d, 0x2e, 0x5f, 0x42, 0x2a, - 0xf5, 0xab, 0x53, 0xa4, 0x2b, 0x26, 0x79, 0x56, 0x35, 0x00, 0xfd, 0x12, 0x8a, 0x6a, 0xc4, 0xcc, - 0xca, 0xb4, 0x96, 0x3f, 0x43, 0x33, 0x8d, 0xd1, 0x0d, 0x85, 0xa6, 0xc4, 0x71, 0xfc, 0x9c, 0x04, - 0x44, 0xbf, 0xd0, 0x89, 0xb6, 0x76, 0x1e, 0x27, 0x66, 0x50, 0x1d, 0xe6, 0x39, 0x75, 0xdb, 0xc4, - 0x5c, 0x98, 0xc1, 0x85, 0x8a, 0x55, 0x24, 0xb6, 0xc8, 0x0b, 0x02, 0xd2, 0x31, 0x17, 0x55, 0xa5, - 0xa4, 0x28, 0xf4, 0x03, 0x58, 0x0c, 0xfa, 0x3d, 0xd9, 0x2c, 0x74, 0xf6, 0x39, 0x89, 0x98, 0x79, - 0x41, 0x7e, 0x6f, 0x62, 0x16, 0x5d, 0x83, 0x85, 0xa0, 0xdf, 0x3b, 0x10, 0x37, 0xbc, 0x62, 0x5b, - 0x92, 0x6c, 0xe3, 0x93, 0xe8, 0x26, 0x2c, 0x0b, 0x5c, 0xbc, 0xdb, 0x8a, 0x73, 0x59, 0x72, 0xa6, - 0x17, 0xde, 0x43, 0xcf, 0xfc, 0x3e, 0x3a, 0x02, 0xeb, 0x39, 0x54, 0x93, 0xfb, 0x90, 0x81, 0xbd, - 0x33, 0xde, 0x71, 0xcf, 0x10, 0x17, 0x89, 0x86, 0xe3, 0x39, 0x7c, 0xef, 0x69, 0xd4, 0x71, 0x39, - 0xc9, 0xca, 0xbc, 0xe9, 0x0c, 0x74, 0x19, 0x0a, 0x7b, 0x6a, 0xa3, 0xd4, 0xcb, 0xa5, 0xa6, 0xc4, - 0x7c, 0x93, 0x08, 0xe7, 0xe9, 0x74, 0xab, 0x29, 0xfb, 0x0a, 0x58, 0x59, 0xe2, 0x95, 0x33, 0xec, - 0x3f, 0xe7, 0x00, 0x46, 0xc1, 0x80, 0x3e, 0x02, 0xe8, 0x91, 0x8e, 0xe7, 0xfe, 0x9a, 0x8f, 0x1a, - 0xca, 0xb2, 0x9c, 0x91, 0x5d, 0xe5, 0xa8, 0xf4, 0xcf, 0xbd, 0x73, 0xe9, 0x8f, 0x60, 0x8e, 0x79, - 0xdf, 0x10, 0x5d, 0xa6, 0xc8, 0x31, 0x7a, 0x02, 0x15, 0x37, 0x08, 0x42, 0x2e, 0xc3, 0x38, 0x6e, - 0xb6, 0x6f, 0x9d, 0x16, 0xbe, 0xce, 0xd6, 0x88, 0x5f, 0x9d, 0x92, 0xa4, 0x04, 0xeb, 0x3e, 0x2c, - 0x4d, 0x32, 0x9c, 0xab, 0x19, 0xfc, 0xd6, 0x80, 0x0b, 0x13, 0x5b, 0x87, 0x3e, 0x1d, 0x66, 0x01, - 0x63, 0x86, 0xe3, 0x15, 0x27, 0x80, 0x07, 0x50, 0xdd, 0xe2, 0x5c, 0x64, 0x3d, 0x65, 0x9b, 0x6a, - 0xf7, 0x4e, 0xc7, 0x8e, 0x21, 0xec, 0x3f, 0x1a, 0xa3, 0x77, 0xce, 0xcc, 0x9e, 0xff, 0xde, 0x78, - 0xcf, 0x7f, 0x7d, 0xfa, 0xe5, 0xf0, 0x3e, 0x5b, 0xfd, 0x1b, 0x3f, 0x87, 0x0f, 0x33, 0x2f, 0x66, - 0x54, 0x81, 0xe2, 0xfe, 0xc1, 0x16, 0x3e, 0x68, 0x35, 0x97, 0x3e, 0x40, 0x55, 0x28, 0x6d, 0x3f, - 0xd9, 0xdd, 0x7b, 0xdc, 0x3a, 0x68, 0x2d, 0x19, 0x62, 0xa9, 0xd9, 0x12, 0xe3, 0xe6, 0x52, 0xae, - 0xfe, 0x6d, 0x01, 0x8a, 0xdb, 0xea, 0xbf, 0x1e, 0x74, 0x00, 0xe5, 0xe1, 0x9f, 0x00, 0xc8, 0xce, - 0xf0, 0xce, 0xc4, 0xbf, 0x09, 0xd6, 0xc7, 0xa7, 0xf2, 0xe8, 0xc4, 0xfd, 0x10, 0xe6, 0xe5, 0xdf, - 0x21, 0x28, 0xa3, 0xbd, 0x4e, 0xfe, 0x4f, 0x62, 0x9d, 0xfe, 0xf7, 0xc2, 0x86, 0x21, 0x24, 0xc9, - 0xb7, 0x89, 0x2c, 0x49, 0xc9, 0xc7, 0x4b, 0x6b, 0xf5, 0x8c, 0x47, 0x0d, 0xb4, 0x0b, 0x05, 0xdd, - 0xb0, 0x65, 0xb1, 0x26, 0x5f, 0x20, 0xac, 0xb5, 0xe9, 0x0c, 0x4a, 0xd8, 0x86, 0x81, 0x76, 0x87, - 0xef, 0xd1, 0x59, 0xaa, 0x25, 0xab, 0x5d, 0xeb, 0x8c, 0xf5, 0x75, 0x63, 0xc3, 0x40, 0x5f, 0x41, - 0x25, 0x51, 0xcf, 0xa2, 0x8c, 0x6a, 0x2a, 0x5d, 0x1c, 0x5b, 0xd7, 0xcf, 0xe0, 0xd2, 0x96, 0xb7, - 0x60, 0x4e, 0x1e, 0xa4, 0x0c, 0x67, 0x27, 0xca, 0xdd, 0x2c, 0x35, 0xc7, 0xca, 0xdf, 0x43, 0x55, - 0xa0, 0x93, 0x20, 0x19, 0x7d, 0xe8, 0xfa, 0x59, 0xf7, 0xea, 0xd4, 0xb0, 0x49, 0x05, 0xf1, 0x86, - 0x81, 0x42, 0x40, 0xe9, 0xe4, 0x89, 0x7e, 0x94, 0x11, 0x25, 0xd3, 0x32, 0xb8, 0x75, 0x73, 0x36, - 0x66, 0x65, 0x54, 0xa3, 0xfa, 0xea, 0xcd, 0x8a, 0xf1, 0x8f, 0x37, 0x2b, 0xc6, 0xbf, 0xde, 0xac, - 0x18, 0x87, 0x05, 0x59, 0x31, 0xfd, 0xf8, 0xff, 0x01, 0x00, 0x00, 0xff, 0xff, 0x7c, 0xb8, 0xc3, - 0x68, 0x0b, 0x1d, 0x00, 0x00, + // 2303 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xac, 0x59, 0x4f, 0x73, 0x13, 0xc9, + 0x15, 0x67, 0x64, 0x59, 0x96, 0x9e, 0x64, 0x63, 0x37, 0x2c, 0x99, 0x4c, 0x58, 0xdb, 0xcc, 0x42, + 0xe2, 0x22, 0x30, 0xf2, 0x3a, 0x4b, 0x60, 0x4d, 0x42, 0xb0, 0x2c, 0x65, 0x31, 0x81, 0xc2, 0xdb, + 0x36, 0x4b, 0xd5, 0x56, 0x91, 0xd4, 0x58, 0x6a, 0x8b, 0x29, 0x8f, 0x66, 0x26, 0xdd, 0x2d, 0x2f, + 0xde, 0x53, 0x4e, 0xa9, 0xda, 0x4b, 0x2a, 0xb7, 0x5c, 0x72, 0xcf, 0x69, 0xcf, 0xf9, 0x04, 0xa9, + 0xe2, 0x98, 0xf3, 0x1e, 0x48, 0x8a, 0x0f, 0x90, 0xca, 0x31, 0xb9, 0x6d, 0xf5, 0x9f, 0x91, 0x5a, + 0xd2, 0xc8, 0x96, 0x80, 0x93, 0xfa, 0x75, 0xbf, 0xdf, 0x9b, 0xf7, 0x5e, 0xbf, 0x7e, 0xfd, 0x5e, + 0x0b, 0xe6, 0x9b, 0x71, 0xc4, 0x69, 0x1c, 0x7a, 0x09, 0x8d, 0x79, 0x8c, 0x16, 0x3b, 0xf1, 0xc1, + 0x89, 0x77, 0xd0, 0x0d, 0xc2, 0xd6, 0x51, 0xc0, 0xbd, 0xe3, 0x8f, 0x9d, 0x9b, 0xed, 0x80, 0xbf, + 0xe8, 0x1e, 0x78, 0xcd, 0xb8, 0x53, 0x6d, 0xc7, 0xed, 0xb8, 0x2a, 0x19, 0x0f, 0xba, 0x87, 0x92, + 0x92, 0x84, 0x1c, 0x29, 0x01, 0xce, 0x4a, 0x3b, 0x8e, 0xdb, 0x21, 0xe9, 0x73, 0xf1, 0xa0, 0x43, + 0x18, 0xf7, 0x3b, 0x89, 0x66, 0xb8, 0x61, 0xc8, 0x13, 0x1f, 0xab, 0xa6, 0x1f, 0xab, 0xb2, 0x38, + 0x3c, 0x26, 0xb4, 0x9a, 0x1c, 0x54, 0xe3, 0x84, 0x69, 0xee, 0xea, 0x58, 0x6e, 0x3f, 0x09, 0xaa, + 0xfc, 0x24, 0x21, 0xac, 0xfa, 0x55, 0x4c, 0x8f, 0x08, 0xd5, 0x80, 0x8d, 0x61, 0x75, 0x95, 0x3e, + 0x7e, 0x12, 0x30, 0x3d, 0xac, 0xd2, 0xa4, 0x59, 0x65, 0xdc, 0xe7, 0xdd, 0xf4, 0x23, 0xb7, 0x4e, + 0x51, 0xa9, 0x4b, 0x9b, 0x24, 0x89, 0xc3, 0xa0, 0x79, 0x22, 0x14, 0x53, 0x23, 0x05, 0x73, 0xff, + 0x68, 0x41, 0x65, 0x97, 0x76, 0x23, 0x82, 0xc9, 0xef, 0xbb, 0x84, 0x71, 0x74, 0x09, 0x0a, 0x87, + 0x41, 0xc8, 0x09, 0xb5, 0xad, 0xd5, 0x99, 0xb5, 0x12, 0xd6, 0x14, 0x5a, 0x84, 0x19, 0x3f, 0x0c, + 0xed, 0xdc, 0xaa, 0xb5, 0x56, 0xc4, 0x62, 0x88, 0xd6, 0xa0, 0x72, 0x44, 0x48, 0x52, 0xef, 0x52, + 0x9f, 0x07, 0x71, 0x64, 0xcf, 0xac, 0x5a, 0x6b, 0x33, 0xb5, 0xfc, 0xab, 0xd7, 0x2b, 0x16, 0x1e, + 0x58, 0x41, 0x2e, 0x94, 0x04, 0x5d, 0x3b, 0xe1, 0x84, 0xd9, 0x79, 0x83, 0xad, 0x3f, 0xed, 0x5e, + 0x87, 0xc5, 0x7a, 0xc0, 0x8e, 0x9e, 0x32, 0xbf, 0x7d, 0x96, 0x2e, 0xee, 0x43, 0x58, 0x32, 0x78, + 0x59, 0x12, 0x47, 0x8c, 0xa0, 0x5b, 0x50, 0xa0, 0xa4, 0x19, 0xd3, 0x96, 0x64, 0x2e, 0x6f, 0x7c, + 0xe8, 0x0d, 0x87, 0x81, 0xa7, 0x01, 0x82, 0x09, 0x6b, 0x66, 0xf7, 0x2f, 0x33, 0x50, 0x36, 0xe6, + 0xd1, 0x02, 0xe4, 0x76, 0xea, 0xb6, 0xb5, 0x6a, 0xad, 0x95, 0x70, 0x6e, 0xa7, 0x8e, 0x6c, 0x98, + 0x7b, 0xdc, 0xe5, 0xfe, 0x41, 0x48, 0xb4, 0xed, 0x29, 0x89, 0x2e, 0xc2, 0xec, 0x4e, 0xf4, 0x94, + 0x11, 0x69, 0x78, 0x11, 0x2b, 0x02, 0x21, 0xc8, 0xef, 0x05, 0x5f, 0x13, 0x65, 0x26, 0x96, 0x63, + 0xe4, 0x40, 0x61, 0xd7, 0xa7, 0x24, 0xe2, 0xf6, 0xac, 0x90, 0x5b, 0xcb, 0xd9, 0x16, 0xd6, 0x33, + 0xa8, 0x06, 0xa5, 0x6d, 0x4a, 0x7c, 0x4e, 0x5a, 0x5b, 0xdc, 0x2e, 0xac, 0x5a, 0x6b, 0xe5, 0x0d, + 0xc7, 0x53, 0x9b, 0xec, 0xa5, 0xf1, 0xe7, 0xed, 0xa7, 0xf1, 0x57, 0x2b, 0xbe, 0x7a, 0xbd, 0x72, + 0xee, 0xcf, 0xff, 0x12, 0xbe, 0xeb, 0xc1, 0xd0, 0x7d, 0x80, 0x47, 0x3e, 0xe3, 0x4f, 0x99, 0x14, + 0x32, 0x77, 0xa6, 0x90, 0xbc, 0x14, 0x60, 0x60, 0xd0, 0x32, 0x80, 0x74, 0xc2, 0x76, 0xdc, 0x8d, + 0xb8, 0x5d, 0x94, 0xba, 0x1b, 0x33, 0x68, 0x15, 0xca, 0x75, 0xc2, 0x9a, 0x34, 0x48, 0xe4, 0x56, + 0x97, 0xa4, 0x7b, 0xcc, 0x29, 0x21, 0x41, 0x79, 0x70, 0xff, 0x24, 0x21, 0x36, 0x48, 0x06, 0x63, + 0x46, 0xec, 0xe5, 0xde, 0x0b, 0x9f, 0x92, 0x96, 0x5d, 0x96, 0xee, 0xd2, 0x94, 0xf0, 0xaf, 0xf2, + 0x04, 0xb3, 0x2b, 0x72, 0x93, 0x53, 0xd2, 0xfd, 0x43, 0x11, 0x2a, 0x7b, 0xe2, 0x38, 0xa5, 0xe1, + 0xb0, 0x08, 0x33, 0x98, 0x1c, 0xea, 0xbd, 0x11, 0x43, 0xe4, 0x01, 0xd4, 0xc9, 0x61, 0x10, 0x05, + 0x52, 0xab, 0x9c, 0x34, 0x7c, 0xc1, 0x4b, 0x0e, 0xbc, 0xfe, 0x2c, 0x36, 0x38, 0x90, 0x07, 0xa8, + 0xf1, 0x32, 0x89, 0x29, 0x27, 0xb4, 0x4e, 0x12, 0x4a, 0x9a, 0xc2, 0x81, 0x72, 0xff, 0x4a, 0x38, + 0x63, 0x05, 0x75, 0xe1, 0x07, 0xe9, 0xec, 0x16, 0xe7, 0x94, 0x19, 0xa0, 0xbc, 0x0c, 0xb2, 0xbb, + 0xa3, 0x41, 0x66, 0xaa, 0xec, 0x8d, 0x41, 0x37, 0x22, 0x4e, 0x4f, 0xf0, 0x38, 0xd9, 0xc2, 0x27, + 0x7b, 0x84, 0x31, 0x61, 0x93, 0x0c, 0x18, 0x9c, 0x92, 0xc8, 0x81, 0xe2, 0xaf, 0x69, 0x1c, 0x71, + 0x12, 0xb5, 0x64, 0xb0, 0x94, 0x70, 0x8f, 0x46, 0xcf, 0x60, 0x3e, 0x1d, 0x4b, 0x81, 0xf6, 0x9c, + 0x54, 0xf1, 0xe3, 0x33, 0x54, 0x1c, 0xc0, 0x28, 0xc5, 0x06, 0xe5, 0xa0, 0x4d, 0x98, 0xdd, 0xf6, + 0x9b, 0x2f, 0x88, 0x8c, 0x8b, 0xf2, 0xc6, 0xf2, 0xa8, 0x40, 0xb9, 0xfc, 0x44, 0x06, 0x02, 0x93, + 0x47, 0xfb, 0x1c, 0x56, 0x10, 0xf4, 0x5b, 0xa8, 0x34, 0x22, 0x1e, 0xf0, 0x90, 0x74, 0xe4, 0x1e, + 0x97, 0xc4, 0x1e, 0xd7, 0x36, 0xbf, 0x7b, 0xbd, 0xf2, 0xf3, 0xb1, 0x09, 0xab, 0xcb, 0x83, 0xb0, + 0x4a, 0x0c, 0x94, 0x67, 0x88, 0xc0, 0x03, 0xf2, 0xd0, 0x97, 0xb0, 0x90, 0x2a, 0xbb, 0x13, 0x25, + 0x5d, 0xce, 0x6c, 0x90, 0x56, 0x6f, 0x4c, 0x68, 0xb5, 0x02, 0x29, 0xb3, 0x87, 0x24, 0x09, 0x67, + 0xef, 0x44, 0x9c, 0xd0, 0xc8, 0x0f, 0x75, 0xd0, 0xf6, 0x68, 0xb4, 0x23, 0x62, 0x53, 0xe4, 0xd5, + 0x5d, 0x99, 0x4d, 0xed, 0x8a, 0x74, 0xcd, 0xb5, 0xd1, 0xaf, 0x9a, 0xd9, 0xd7, 0x53, 0xcc, 0x78, + 0x00, 0x8a, 0xee, 0x40, 0x29, 0x0d, 0x04, 0x66, 0xcf, 0x4b, 0xed, 0x9d, 0x51, 0x39, 0x29, 0x0b, + 0xee, 0x33, 0x3b, 0x0f, 0xe1, 0xf2, 0x69, 0x01, 0x26, 0x0e, 0xcc, 0x11, 0x39, 0x49, 0x0f, 0xcc, + 0x11, 0x39, 0x11, 0x39, 0xeb, 0xd8, 0x0f, 0xbb, 0x2a, 0x97, 0x95, 0xb0, 0x22, 0x36, 0x73, 0x77, + 0x2c, 0xe7, 0x3e, 0xa0, 0xd1, 0x48, 0x98, 0x4a, 0xc2, 0xe7, 0x70, 0x21, 0xc3, 0xab, 0x19, 0x22, + 0xae, 0x9a, 0x22, 0x46, 0x0f, 0x6c, 0x5f, 0xa4, 0xfb, 0xed, 0x0c, 0x54, 0xcc, 0xd8, 0x42, 0xeb, + 0x70, 0x41, 0x59, 0x8c, 0xc9, 0xa1, 0x71, 0x18, 0x95, 0xf0, 0xac, 0x25, 0xb4, 0x01, 0x17, 0x77, + 0x3a, 0x7a, 0xda, 0x3c, 0xbf, 0x39, 0x99, 0x6c, 0x32, 0xd7, 0x50, 0x0c, 0x1f, 0x28, 0x51, 0xc3, + 0x87, 0x7e, 0x46, 0xee, 0xce, 0xa7, 0xa7, 0x1f, 0x00, 0x2f, 0x13, 0xab, 0x42, 0x2c, 0x5b, 0x2e, + 0xfa, 0x25, 0xcc, 0xa9, 0x05, 0xa6, 0xf3, 0xca, 0x47, 0xa7, 0x7f, 0x42, 0x09, 0x4b, 0x31, 0x02, + 0xae, 0xec, 0x60, 0xf6, 0xec, 0x14, 0x70, 0x8d, 0x71, 0x1e, 0x80, 0x33, 0x5e, 0xe5, 0x69, 0x42, + 0xc0, 0xfd, 0x9b, 0x05, 0x4b, 0x23, 0x1f, 0x12, 0x57, 0xa2, 0xbc, 0x14, 0x94, 0x08, 0x39, 0x46, + 0x75, 0x98, 0x55, 0x49, 0x2a, 0x27, 0x15, 0xf6, 0x26, 0x50, 0xd8, 0x33, 0x32, 0x94, 0x02, 0x3b, + 0x77, 0x00, 0xde, 0x2e, 0x58, 0xdd, 0xbf, 0x5b, 0x30, 0xaf, 0x13, 0x82, 0xae, 0x1f, 0x7c, 0x58, + 0xec, 0x9d, 0x31, 0x3d, 0xa7, 0x2b, 0x89, 0x5b, 0x63, 0x73, 0x89, 0x62, 0xf3, 0x86, 0x71, 0x4a, + 0xc7, 0x11, 0x71, 0xce, 0x76, 0x1a, 0x57, 0x43, 0xac, 0x53, 0x69, 0x7e, 0x05, 0xe6, 0xf7, 0x64, + 0xe1, 0x37, 0xf6, 0x5a, 0x74, 0xff, 0x6b, 0xc1, 0x42, 0xca, 0xa3, 0xad, 0xfb, 0x04, 0x8a, 0xc7, + 0x84, 0x72, 0xf2, 0x92, 0x30, 0x6d, 0x95, 0x3d, 0x6a, 0xd5, 0x17, 0x92, 0x03, 0xf7, 0x38, 0xd1, + 0x26, 0x14, 0x55, 0x91, 0x49, 0xd2, 0x8d, 0x5a, 0x1e, 0x87, 0xd2, 0xdf, 0xeb, 0xf1, 0xa3, 0x2a, + 0xe4, 0xc3, 0xb8, 0xcd, 0xf4, 0x99, 0xf9, 0xd1, 0x38, 0xdc, 0xa3, 0xb8, 0x8d, 0x25, 0x23, 0xba, + 0x0b, 0xc5, 0xaf, 0x7c, 0x1a, 0x05, 0x51, 0x3b, 0x3d, 0x05, 0x2b, 0xe3, 0x40, 0xcf, 0x14, 0x1f, + 0xee, 0x01, 0x44, 0x19, 0x57, 0x50, 0x6b, 0xe8, 0x21, 0x14, 0x5a, 0x41, 0x9b, 0x30, 0xae, 0x5c, + 0x52, 0xdb, 0x10, 0xf7, 0xd1, 0x77, 0xaf, 0x57, 0xae, 0x1b, 0x17, 0x4e, 0x9c, 0x90, 0x48, 0x34, + 0x0d, 0x7e, 0x10, 0x11, 0x2a, 0x8a, 0xea, 0x9b, 0x0a, 0xe2, 0xd5, 0xe5, 0x0f, 0xd6, 0x12, 0x84, + 0xac, 0x40, 0x5d, 0x2b, 0x32, 0x5f, 0xbc, 0x9d, 0x2c, 0x25, 0x41, 0x1c, 0x83, 0xc8, 0xef, 0x10, + 0x5d, 0x6e, 0xc8, 0xb1, 0xa8, 0x8a, 0x9a, 0x22, 0xce, 0x5b, 0xb2, 0x5e, 0x2c, 0x62, 0x4d, 0xa1, + 0x4d, 0x98, 0x63, 0xdc, 0xa7, 0x22, 0xe7, 0xcc, 0x4e, 0x58, 0xce, 0xa5, 0x00, 0x74, 0x0f, 0x4a, + 0xcd, 0xb8, 0x93, 0x84, 0x44, 0xa0, 0x0b, 0x13, 0xa2, 0xfb, 0x10, 0x11, 0x7a, 0x84, 0xd2, 0x98, + 0xca, 0x42, 0xb2, 0x84, 0x15, 0x81, 0x6e, 0xc3, 0x7c, 0x42, 0xe3, 0x36, 0x25, 0x8c, 0x7d, 0x46, + 0xe3, 0x6e, 0xa2, 0x8b, 0x81, 0x25, 0x91, 0xbc, 0x77, 0xcd, 0x05, 0x3c, 0xc8, 0xe7, 0xfe, 0x27, + 0x07, 0x15, 0x33, 0x44, 0x46, 0x2a, 0xec, 0x87, 0x50, 0x50, 0x01, 0xa7, 0x62, 0xfd, 0xed, 0x7c, + 0xac, 0x24, 0x64, 0xfa, 0xd8, 0x86, 0xb9, 0x66, 0x97, 0xca, 0xf2, 0x5b, 0x15, 0xe5, 0x29, 0x29, + 0x2c, 0xe5, 0x31, 0xf7, 0x43, 0xe9, 0xe3, 0x19, 0xac, 0x08, 0x51, 0x91, 0xf7, 0xfa, 0xbd, 0xe9, + 0x2a, 0xf2, 0x1e, 0xcc, 0xdc, 0xbf, 0xb9, 0x77, 0xda, 0xbf, 0xe2, 0xd4, 0xfb, 0xe7, 0xfe, 0xc3, + 0x82, 0x52, 0xef, 0x6c, 0x19, 0xde, 0xb5, 0xde, 0xd9, 0xbb, 0x03, 0x9e, 0xc9, 0xbd, 0x9d, 0x67, + 0x2e, 0x41, 0x81, 0x71, 0x4a, 0xfc, 0x8e, 0xea, 0x17, 0xb1, 0xa6, 0x44, 0x16, 0xeb, 0xb0, 0xb6, + 0xdc, 0xa1, 0x0a, 0x16, 0x43, 0xf7, 0x7f, 0x16, 0xcc, 0x0f, 0x1c, 0xf7, 0xf7, 0x6a, 0xcb, 0x45, + 0x98, 0x0d, 0xc9, 0x31, 0x51, 0x1d, 0xed, 0x0c, 0x56, 0x84, 0x98, 0x65, 0x2f, 0x62, 0xca, 0xa5, + 0x72, 0x15, 0xac, 0x08, 0xa1, 0x73, 0x8b, 0x70, 0x3f, 0x08, 0x65, 0x5e, 0xaa, 0x60, 0x4d, 0x09, + 0x9d, 0xbb, 0x34, 0xd4, 0x35, 0xba, 0x18, 0x22, 0x17, 0xf2, 0x41, 0x74, 0x18, 0xeb, 0xb0, 0x91, + 0x95, 0x8d, 0xaa, 0xf5, 0x76, 0xa2, 0xc3, 0x18, 0xcb, 0x35, 0x74, 0x05, 0x0a, 0xd4, 0x8f, 0xda, + 0x24, 0x2d, 0xd0, 0x4b, 0x82, 0x0b, 0x8b, 0x19, 0xac, 0x17, 0x5c, 0x17, 0x2a, 0xb2, 0x2b, 0x7e, + 0x4c, 0x98, 0xe8, 0xc1, 0x44, 0x58, 0xb7, 0x7c, 0xee, 0x4b, 0xb3, 0x2b, 0x58, 0x8e, 0xdd, 0x1b, + 0x80, 0x1e, 0x05, 0x8c, 0x3f, 0x93, 0x0f, 0x07, 0xec, 0xac, 0x96, 0x79, 0x0f, 0x2e, 0x0c, 0x70, + 0xeb, 0x6b, 0xe1, 0x17, 0x43, 0x4d, 0xf3, 0xd5, 0xd1, 0x8c, 0x2b, 0xdf, 0x27, 0x3c, 0x05, 0x1c, + 0xea, 0x9d, 0xe7, 0xa1, 0x2c, 0xed, 0x52, 0xdf, 0x76, 0x7d, 0xa8, 0x28, 0x52, 0x0b, 0xff, 0x1c, + 0xce, 0xa7, 0x82, 0xbe, 0x20, 0x54, 0xb6, 0x33, 0x96, 0xf4, 0xcb, 0x4f, 0xc6, 0x7d, 0xa5, 0x36, + 0xc8, 0x8e, 0x87, 0xf1, 0x2e, 0x81, 0x0b, 0x92, 0xe7, 0x41, 0xc0, 0x78, 0x4c, 0x4f, 0x52, 0xab, + 0x97, 0x01, 0xb6, 0x9a, 0x3c, 0x38, 0x26, 0x4f, 0xa2, 0x50, 0x5d, 0xa3, 0x45, 0x6c, 0xcc, 0xa4, + 0x57, 0x64, 0xae, 0xdf, 0x39, 0x5e, 0x86, 0x52, 0xc3, 0xa7, 0xe1, 0x49, 0xe3, 0x65, 0xc0, 0x75, + 0x03, 0xdf, 0x9f, 0x70, 0xff, 0x64, 0xc1, 0x92, 0xf9, 0x9d, 0xc6, 0xb1, 0x48, 0x17, 0x77, 0x21, + 0xcf, 0xd3, 0x3a, 0x66, 0x21, 0xcb, 0x88, 0x11, 0x88, 0x28, 0x75, 0xb0, 0x04, 0x19, 0x9e, 0x56, + 0x07, 0xe7, 0xea, 0xe9, 0xf0, 0x21, 0x4f, 0xff, 0xbf, 0x08, 0x68, 0x74, 0x39, 0xa3, 0x23, 0x36, + 0x1b, 0xc4, 0xdc, 0x50, 0x83, 0xf8, 0x7c, 0xb8, 0x41, 0x54, 0x57, 0xf3, 0xed, 0x49, 0x34, 0x99, + 0xa0, 0x4d, 0x1c, 0xe8, 0x63, 0xf2, 0x53, 0xf4, 0x31, 0x68, 0x2d, 0xbd, 0x71, 0xd4, 0x5d, 0x87, + 0xd2, 0x9c, 0x42, 0x93, 0xa6, 0xa7, 0xeb, 0x0a, 0x7d, 0x0b, 0xdd, 0x9b, 0xee, 0xb5, 0x24, 0x3f, + 0xfc, 0x52, 0x52, 0x83, 0xf2, 0x76, 0x9a, 0x28, 0xa7, 0x78, 0x2a, 0x31, 0x41, 0x68, 0x5d, 0x17, + 0x36, 0x2a, 0x35, 0x5f, 0x1e, 0x35, 0x31, 0x7d, 0x16, 0x89, 0xa9, 0xae, 0x6c, 0x0e, 0x33, 0x4a, + 0xcb, 0x92, 0x74, 0xd0, 0xe6, 0x44, 0xbe, 0x9f, 0xb0, 0xbe, 0x44, 0x9f, 0x42, 0x01, 0x13, 0xd6, + 0x0d, 0xb9, 0x7c, 0x7f, 0x29, 0x6f, 0x5c, 0x19, 0x23, 0x5d, 0x31, 0xc9, 0xb3, 0xaa, 0x01, 0xe8, + 0x37, 0x30, 0xa7, 0x46, 0xcc, 0x2e, 0x8f, 0x7b, 0x36, 0xc8, 0xd0, 0x4c, 0x63, 0x74, 0x43, 0xa1, + 0x29, 0x71, 0x1c, 0x3f, 0x23, 0x11, 0xd1, 0xef, 0x82, 0xa2, 0x35, 0x9e, 0xc5, 0xc6, 0x0c, 0xda, + 0x80, 0x59, 0x4e, 0xfd, 0x26, 0xb1, 0xe7, 0x27, 0x70, 0xa1, 0x62, 0x15, 0x89, 0x2d, 0x09, 0xa2, + 0x88, 0xb4, 0xec, 0x05, 0x55, 0x29, 0x29, 0x0a, 0xfd, 0x18, 0x16, 0xa2, 0x6e, 0x47, 0x36, 0x0b, + 0xad, 0x3d, 0x4e, 0x12, 0x66, 0x9f, 0x97, 0xdf, 0x1b, 0x9a, 0x45, 0x57, 0x61, 0x3e, 0xea, 0x76, + 0xf6, 0xc5, 0x0d, 0xaf, 0xd8, 0x16, 0x25, 0xdb, 0xe0, 0x24, 0xba, 0x01, 0x4b, 0x02, 0x97, 0xee, + 0xb6, 0xe2, 0x5c, 0x92, 0x9c, 0xa3, 0x0b, 0xef, 0xa1, 0x67, 0x7e, 0x1f, 0x1d, 0x81, 0xf3, 0x1c, + 0x2a, 0xe6, 0x3e, 0x64, 0x60, 0x6f, 0x0f, 0x76, 0xdc, 0x13, 0xc4, 0x85, 0xd1, 0x70, 0x3c, 0x87, + 0x1f, 0x3e, 0x4d, 0x5a, 0x3e, 0x27, 0x59, 0x99, 0x77, 0x34, 0x03, 0x5d, 0x82, 0xc2, 0xae, 0xda, + 0x28, 0xf5, 0x5e, 0xaa, 0x29, 0x31, 0x5f, 0x27, 0xc2, 0x79, 0x3a, 0xdd, 0x6a, 0xca, 0xbd, 0x0c, + 0x4e, 0x96, 0x78, 0xe5, 0x0c, 0xf7, 0xaf, 0x39, 0x80, 0x7e, 0x30, 0xa0, 0x0f, 0x01, 0x3a, 0xa4, + 0x15, 0xf8, 0xbf, 0xe3, 0xfd, 0x86, 0xb2, 0x24, 0x67, 0x64, 0x57, 0xd9, 0x2f, 0xfd, 0x73, 0xef, + 0x5c, 0xfa, 0x23, 0xc8, 0xb3, 0xe0, 0x6b, 0xa2, 0xcb, 0x14, 0x39, 0x46, 0x4f, 0xa0, 0xec, 0x47, + 0x51, 0xcc, 0x65, 0x18, 0xa7, 0xcd, 0xf6, 0xcd, 0xd3, 0xc2, 0xd7, 0xdb, 0xea, 0xf3, 0xab, 0x53, + 0x62, 0x4a, 0x70, 0xee, 0xc1, 0xe2, 0x30, 0xc3, 0x54, 0xcd, 0xe0, 0x37, 0x16, 0x9c, 0x1f, 0xda, + 0x3a, 0xf4, 0x49, 0x2f, 0x0b, 0x58, 0x13, 0x1c, 0xaf, 0x34, 0x01, 0xdc, 0x87, 0xca, 0x16, 0xe7, + 0x22, 0xeb, 0x29, 0xdb, 0x54, 0xbb, 0x77, 0x3a, 0x76, 0x00, 0xe1, 0x7e, 0x6b, 0x41, 0x31, 0x0d, + 0xe6, 0xcc, 0x9e, 0xff, 0xee, 0x60, 0xcf, 0x7f, 0x6d, 0xfc, 0xe5, 0x30, 0xda, 0xea, 0xa3, 0x4b, + 0x90, 0x0b, 0xf4, 0x53, 0x6d, 0xad, 0xf0, 0xe6, 0xf5, 0x4a, 0x6e, 0xa7, 0x8e, 0x73, 0x41, 0xeb, + 0xed, 0x9f, 0x00, 0xae, 0xff, 0x0a, 0x3e, 0xc8, 0xbc, 0xb0, 0x51, 0x19, 0xe6, 0xf6, 0xf6, 0xb7, + 0xf0, 0x7e, 0xa3, 0xbe, 0x78, 0x0e, 0x55, 0xa0, 0xb8, 0xfd, 0xe4, 0xf1, 0xee, 0xa3, 0xc6, 0x7e, + 0x63, 0xd1, 0x12, 0x4b, 0xf5, 0x86, 0x18, 0xd7, 0x17, 0x73, 0x1b, 0xdf, 0x14, 0x60, 0x6e, 0x5b, + 0xfd, 0xf3, 0x84, 0xf6, 0xa1, 0xd4, 0xfb, 0x4b, 0x02, 0xb9, 0x19, 0x5e, 0x1b, 0xfa, 0x6f, 0xc3, + 0xf9, 0xe8, 0x54, 0x1e, 0x9d, 0xd0, 0x1f, 0xc0, 0xac, 0xfc, 0x73, 0x06, 0x65, 0xb4, 0xdd, 0xe6, + 0xbf, 0x36, 0xce, 0xe9, 0x7f, 0x76, 0xac, 0x5b, 0x42, 0x92, 0x7c, 0xb3, 0xc8, 0x92, 0x64, 0x3e, + 0x8c, 0x3a, 0x2b, 0x67, 0x3c, 0x76, 0xa0, 0xc7, 0x50, 0xd0, 0x8d, 0x5c, 0x16, 0xab, 0xf9, 0x32, + 0xe1, 0xac, 0x8e, 0x67, 0x50, 0xc2, 0xd6, 0x2d, 0xf4, 0xb8, 0xf7, 0xd6, 0x9d, 0xa5, 0x9a, 0x59, + 0x05, 0x3b, 0x67, 0xac, 0xaf, 0x59, 0xeb, 0x16, 0xfa, 0x12, 0xca, 0x46, 0x9d, 0x8b, 0x32, 0xaa, + 0xac, 0xd1, 0xa2, 0xd9, 0xb9, 0x76, 0x06, 0x97, 0xb6, 0xbc, 0x01, 0x79, 0x79, 0xc0, 0x32, 0x9c, + 0x6d, 0x94, 0xc1, 0x59, 0x6a, 0x0e, 0x94, 0xc5, 0x07, 0xaa, 0x70, 0x27, 0x91, 0x19, 0x7d, 0xe8, + 0xda, 0x59, 0xf7, 0xed, 0xd8, 0xb0, 0x19, 0x09, 0xe2, 0x75, 0x0b, 0xc5, 0x80, 0x46, 0x93, 0x2a, + 0xfa, 0x69, 0x46, 0x94, 0x8c, 0xcb, 0xec, 0xce, 0x8d, 0xc9, 0x98, 0x95, 0x51, 0xb5, 0xca, 0xab, + 0x37, 0xcb, 0xd6, 0x3f, 0xdf, 0x2c, 0x5b, 0xff, 0x7e, 0xb3, 0x6c, 0x1d, 0x14, 0x64, 0x25, 0xf5, + 0xb3, 0xef, 0x03, 0x00, 0x00, 0xff, 0xff, 0x41, 0xb0, 0xbc, 0xf4, 0x99, 0x1d, 0x00, 0x00, } // Reference imports to suppress errors if they are not otherwise used. @@ -2892,6 +2914,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 +3028,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 +3047,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 } @@ -4392,6 +4428,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 +4607,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 +4663,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) } @@ -5237,6 +5286,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 +6088,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 +6116,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 +6147,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 +6243,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 +6686,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:]) @@ -10867,6 +10954,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 327c9eeaf420..78a595266cdc 100644 --- a/api/services/control/control.proto +++ b/api/services/control/control.proto @@ -35,7 +35,7 @@ message PruneRequest { } message DiskUsageRequest { - repeated string filter = 1; + repeated string filter = 1; } message DiskUsageResponse { @@ -60,8 +60,8 @@ message UsageRecord { message SolveRequest { string Ref = 1; pb.Definition Definition = 2; - string Exporter = 3; - map ExporterAttrs = 4; + string ExporterDeprecated = 3; + map ExporterAttrsDeprecated = 4; string Session = 5; string Frontend = 6; map FrontendAttrs = 7; @@ -70,6 +70,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 { @@ -231,7 +232,12 @@ message BuildResultInfo { repeated Descriptor Attestations = 2; } +// 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 c2d8cce253fb..f80e99b76579 100644 --- a/client/client_test.go +++ b/client/client_test.go @@ -152,6 +152,7 @@ func TestIntegration(t *testing.T) { testTarExporterWithSocketCopy, testTarExporterSymlink, testMultipleRegistryCacheImportExport, + testMultipleExporters, testSourceMap, testSourceMapFromRef, testLazyImagePush, @@ -2551,6 +2552,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 integration.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) { integration.CheckFeatureCompat(t, sb, integration.FeatureOCIExporter) requiresLinux(t) @@ -6739,7 +6812,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 22ff2031d442..55e1b59b66db 100644 --- a/client/solve.go +++ b/client/solve.go @@ -4,6 +4,7 @@ import ( "context" "encoding/base64" "encoding/json" + "fmt" "io" "os" "path/filepath" @@ -55,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 { @@ -125,12 +126,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{} @@ -156,51 +168,59 @@ 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 + var exporterConfig struct { + outputDirs []string + outputs map[string]filesync.FileOutputFunc } - 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(ex.Output)) - } - if supportDir { - if ex.OutputDir == "" { - return nil, errors.Errorf("output directory is required for %s exporter", ex.Type) - } + 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 support 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 + if exporterConfig.outputs == nil { + exporterConfig.outputs = make(map[string]filesync.FileOutputFunc) + } + exporterConfig.outputs[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: + exporterConfig.outputDirs = append(exporterConfig.outputDirs, ex.OutputDir) } - contentStores["export"] = cs - storesToUpdate = append(storesToUpdate, ex.OutputDir) - default: - s.Allow(filesync.NewFSSyncTargetDir(ex.OutputDir)) } } @@ -208,6 +228,13 @@ func (c *Client) solve(ctx context.Context, def *llb.Definition, runGateway runG s.Allow(sessioncontent.NewAttachable(contentStores)) } + if len(exporterConfig.outputDirs) > 0 { + s.Allow(filesync.NewFSSyncTargetDir(exporterConfig.outputDirs)) + } + if len(exporterConfig.outputs) > 0 { + s.Allow(filesync.NewFSSyncTarget(exporterConfig.outputs)) + } + eg.Go(func() error { sd := c.sessionDialer if sd == nil { @@ -255,11 +282,31 @@ func (c *Client) solve(ctx context.Context, def *llb.Definition, runGateway runG frontendInputs[key] = def.ToPB() } + exports := make([]*controlapi.Exporter, 0, len(opt.Exports)) + var localExporter bool + for _, exp := range exporters { + if exp.Type != ExporterLocal { + exports = append(exports, &controlapi.Exporter{ + ID: exp.id, + Type: exp.Type, + Attrs: exp.Attrs, + }) + } else { + localExporter = true + } + } + if localExporter { + // Add a single instance of the local exporter + // since it's replicated entirely on the client + exports = append(exports, &controlapi.Exporter{ + Type: ExporterLocal, + }) + } + resp, err := c.ControlClient().Solve(ctx, &controlapi.SolveRequest{ Ref: ref, Definition: pbd, - Exporter: ex.Type, - ExporterAttrs: ex.Attrs, + Exporters: exports, Session: s.ID(), Frontend: opt.Frontend, FrontendAttrs: frontendAttrs, diff --git a/control/control.go b/control/control.go index 8a09b8ace34d..6a00da211058 100644 --- a/control/control.go +++ b/control/control.go @@ -311,6 +311,15 @@ func translateLegacySolveRequest(req *controlapi.SolveRequest) { req.Cache.Imports = append(req.Cache.Imports, im) } req.Cache.ImportRefsDeprecated = nil + // translate single exporter to a slice (v0.11.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) { @@ -323,7 +332,7 @@ func (c *Controller) Solve(ctx context.Context, req *controlapi.SolveRequest) (* time.AfterFunc(time.Second, c.throttledGC) }() - var expi exporter.ExporterInstance + var expis []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() @@ -331,25 +340,28 @@ 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) + for _, ex := range req.Exporters { + exp, err := w.Exporter(ex.Type, ex.ID, c.opt.SessionManager) if err != nil { return nil, err } - expi, err = exp.Resolve(ctx, req.ExporterAttrs) + expi, err := exp.Resolve(ctx, ex.Attrs) if err != nil { return nil, err } + expis = append(expis, expi) } if c, err := findDuplicateCacheOptions(req.Cache.Exports); err != nil { @@ -441,7 +453,7 @@ 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, diff --git a/exporter/containerimage/export.go b/exporter/containerimage/export.go index 5cfd9cbb9def..6efaf773c8f3 100644 --- a/exporter/containerimage/export.go +++ b/exporter/containerimage/export.go @@ -187,20 +187,28 @@ 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) { - if src.Metadata == nil { - src.Metadata = make(map[string][]byte) +func (e *imageExporterInstance) Export(ctx context.Context, src *exporter.Source, sessionID string) (map[string]string, exporter.DescriptorReference, error) { + return e.ExportImage(ctx, src, exptypes.InlineCache{}, sessionID) +} + +func (e *imageExporterInstance) ExportImage(ctx context.Context, inp *exporter.Source, inlineCache exptypes.InlineCache, sessionID string) (_ map[string]string, descref exporter.DescriptorReference, err error) { + meta := make(map[string][]byte) + for k, v := range inp.Metadata { + meta[k] = v } for k, v := range e.meta { - src.Metadata[k] = v + meta[k] = v } + src := *inp + src.Metadata = meta opts := e.opts - as, _, err := ParseAnnotations(src.Metadata) + as, _, err := ParseAnnotations(meta) if err != nil { return nil, nil, err } opts.Annotations = opts.Annotations.Merge(as) + opts.InlineCache = inlineCache ctx, done, err := leaseutil.WithLease(ctx, e.opt.LeaseManager, leaseutil.MakeTemporary) if err != nil { @@ -212,7 +220,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, &opts) if err != nil { return nil, nil, err } @@ -224,7 +232,7 @@ func (e *imageExporterInstance) Export(ctx context.Context, src *exporter.Source resp := make(map[string]string) - if n, ok := src.Metadata["image.name"]; e.opts.ImageName == "*" && ok { + if n, ok := meta["image.name"]; e.opts.ImageName == "*" && ok { e.opts.ImageName = string(n) } @@ -274,7 +282,7 @@ func (e *imageExporterInstance) Export(ctx context.Context, src *exporter.Source tagDone(nil) if e.unpack { - if err := e.unpackImage(ctx, img, src, session.NewGroup(sessionID)); err != nil { + if err := e.unpackImage(ctx, img, &src, session.NewGroup(sessionID)); err != nil { return nil, nil, err } } @@ -310,7 +318,7 @@ func (e *imageExporterInstance) Export(ctx context.Context, src *exporter.Source } } if e.push { - err := e.pushImage(ctx, src, sessionID, targetName, desc.Digest) + err := e.pushImage(ctx, &src, sessionID, targetName, desc.Digest) if err != nil { return nil, nil, errors.Wrapf(err, "failed to push %v", targetName) } diff --git a/exporter/containerimage/exptypes/types.go b/exporter/containerimage/exptypes/types.go index c4d5721ea65d..35cb537fb3a2 100644 --- a/exporter/containerimage/exptypes/types.go +++ b/exporter/containerimage/exptypes/types.go @@ -29,3 +29,9 @@ type Platform struct { ID string Platform ocispecs.Platform } + +type InlineCache struct { + Cache []byte + // Platforms maps platform -> inline cache + Platforms map[string][]byte +} diff --git a/exporter/containerimage/opts.go b/exporter/containerimage/opts.go index 791f268afda5..536393e4aaf8 100644 --- a/exporter/containerimage/opts.go +++ b/exporter/containerimage/opts.go @@ -19,6 +19,7 @@ type ImageCommitOpts struct { OCITypes bool Annotations AnnotationsGroup Epoch *time.Time + InlineCache exptypes.InlineCache ForceInlineAttestations bool // force inline attestations to be attached } diff --git a/exporter/containerimage/writer.go b/exporter/containerimage/writer.go index 15d6edb82b12..ba4223f08f64 100644 --- a/exporter/containerimage/writer.go +++ b/exporter/containerimage/writer.go @@ -133,8 +133,7 @@ func (ic *ImageWriter) Commit(ctx context.Context, inp *exporter.Source, session } config := exptypes.ParseKey(inp.Metadata, exptypes.ExporterImageConfigKey, p) - inlineCache := exptypes.ParseKey(inp.Metadata, exptypes.ExporterInlineCache, p) - mfstDesc, configDesc, err := ic.commitDistributionManifest(ctx, opts, ref, config, &remotes[0], annotations, inlineCache, opts.Epoch, session.NewGroup(sessionID)) + mfstDesc, configDesc, err := ic.commitDistributionManifest(ctx, opts, ref, config, &remotes[0], annotations, opts.InlineCache.Cache, opts.Epoch, session.NewGroup(sessionID)) if err != nil { return nil, err } @@ -191,7 +190,8 @@ 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) + // FIXME(dima) + //inlineCache := exptypes.ParseKey(inp.Metadata, exptypes.ExporterInlineCache, p) remote := &remotes[remotesMap[p.ID]] if remote == nil { @@ -200,7 +200,7 @@ 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)) + desc, _, err := ic.commitDistributionManifest(ctx, opts, r, config, remote, opts.Annotations.Platform(&p.Platform), opts.InlineCache.Platforms[p.ID], opts.Epoch, session.NewGroup(sessionID)) if err != nil { return nil, err } diff --git a/exporter/oci/export.go b/exporter/oci/export.go index 81ac7857deb8..8920b8f5e932 100644 --- a/exporter/oci/export.go +++ b/exporter/oci/export.go @@ -47,6 +47,7 @@ type Opt struct { ImageWriter *containerimage.ImageWriter Variant ExporterVariant LeaseManager leases.Manager + ID string // to support concurrent instances } type imageExporter struct { @@ -112,25 +113,38 @@ 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) { - if e.opt.Variant == VariantDocker && len(src.Refs) > 0 { +func (e *imageExporterInstance) Export(ctx context.Context, src *exporter.Source, sessionID string) (map[string]string, exporter.DescriptorReference, error) { + return e.ExportImage(ctx, src, exptypes.InlineCache{}, sessionID) +} + +func (e *imageExporterInstance) ExportImage(ctx context.Context, inp *exporter.Source, inlineCache exptypes.InlineCache, sessionID string) (_ map[string]string, descref exporter.DescriptorReference, err error) { + if e.opt.Variant == VariantDocker && len(inp.Refs) > 0 { return nil, nil, errors.Errorf("docker exporter does not currently support exporting manifest lists") } - if src.Metadata == nil { - src.Metadata = make(map[string][]byte) + meta := make(map[string][]byte) + for k, v := range inp.Metadata { + meta[k] = v } for k, v := range e.meta { - src.Metadata[k] = v + meta[k] = v } + src := *inp + src.Metadata = meta opts := e.opts - as, _, err := containerimage.ParseAnnotations(src.Metadata) + as, _, err := containerimage.ParseAnnotations(meta) if err != nil { return nil, nil, err } opts.Annotations = opts.Annotations.Merge(as) + if err != nil { + return nil, nil, err + } + opts.Annotations = as.Merge(opts.Annotations) + opts.InlineCache = inlineCache + ctx, done, err := leaseutil.WithLease(ctx, e.opt.LeaseManager, leaseutil.MakeTemporary) if err != nil { return nil, nil, err @@ -141,7 +155,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, &opts) if err != nil { return nil, nil, err } @@ -176,7 +190,7 @@ func (e *imageExporterInstance) Export(ctx context.Context, src *exporter.Source } resp[exptypes.ExporterImageDescriptorKey] = base64.StdEncoding.EncodeToString(dtdesc) - if n, ok := src.Metadata["image.name"]; e.opts.ImageName == "*" && ok { + if n, ok := meta["image.name"]; e.opts.ImageName == "*" && ok { e.opts.ImageName = string(n) } @@ -239,7 +253,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.opt.ID, caller) if err != nil { return nil, nil, err } diff --git a/exporter/tar/export.go b/exporter/tar/export.go index 7259f6b24a9a..53fc5b7c87a9 100644 --- a/exporter/tar/export.go +++ b/exporter/tar/export.go @@ -21,6 +21,7 @@ import ( type Opt struct { SessionManager *session.Manager + ID string // to support concurrent instances } type localExporter struct { @@ -151,10 +152,11 @@ 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.opt.ID, caller) if err != nil { return nil, nil, err } + report := progress.OneOff(ctx, "sending tarball") if err := fsutil.WriteTar(ctx, fs, w); err != nil { w.Close() diff --git a/session/filesync/diffcopy.go b/session/filesync/diffcopy.go index 27bc5d541496..e47b91c2f35c 100644 --- a/session/filesync/diffcopy.go +++ b/session/filesync/diffcopy.go @@ -5,14 +5,19 @@ import ( "context" io "io" "os" + strings "strings" "time" "github.com/moby/buildkit/util/bklog" "github.com/pkg/errors" "github.com/tonistiigi/fsutil" + copy "github.com/tonistiigi/fsutil/copy" fstypes "github.com/tonistiigi/fsutil/types" "google.golang.org/grpc" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/metadata" + "google.golang.org/grpc/status" ) type Stream interface { @@ -25,8 +30,8 @@ func sendDiffCopy(stream Stream, fs fsutil.FS, progress progressCb) error { return errors.WithStack(fsutil.Send(stream.Context(), stream, fs, progress)) } -func newStreamWriter(stream grpc.ClientStream) io.WriteCloser { - wc := &streamWriterCloser{ClientStream: stream} +func newStreamWriter(stream grpc.ClientStream, id string) io.WriteCloser { + wc := &streamWriterCloser{ClientStream: stream, id: id} return &bufferedWriteCloser{Writer: bufio.NewWriter(wc), Closer: wc} } @@ -44,10 +49,11 @@ func (bwc *bufferedWriteCloser) Close() error { type streamWriterCloser struct { grpc.ClientStream + id string } func (wc *streamWriterCloser) Write(dt []byte) (int, error) { - if err := wc.ClientStream.SendMsg(&BytesMessage{Data: dt}); err != nil { + if err := wc.ClientStream.SendMsg(&BytesMessage{ID: wc.id, Data: dt}); err != nil { // SendMsg return EOF on remote errors if errors.Is(err, io.EOF) { if err := errors.WithStack(wc.ClientStream.RecvMsg(struct{}{})); err != nil { @@ -98,11 +104,16 @@ func recvDiffCopy(ds grpc.ClientStream, dest string, cu CacheUpdater, progress p })) } -func syncTargetDiffCopy(ds grpc.ServerStream, dest string) error { - if err := os.MkdirAll(dest, 0700); err != nil { - return errors.Wrapf(err, "failed to create synctarget dest dir %s", dest) +func syncTargetDiffCopy(ds grpc.ServerStream, dests []string) error { + if len(dests) == 0 { + return errors.New("empty list of directories to sync") } - return errors.WithStack(fsutil.Receive(ds.Context(), ds, dest, fsutil.ReceiveOpt{ + for _, dest := range dests { + if err := os.MkdirAll(dest, 0700); err != nil { + return errors.Wrapf(err, "failed to create synctarget dest dir %s", dest) + } + } + err := fsutil.Receive(ds.Context(), ds, dests[0], fsutil.ReceiveOpt{ Merge: true, Filter: func() func(string, *fstypes.Stat) bool { uid := os.Getuid() @@ -113,10 +124,29 @@ func syncTargetDiffCopy(ds grpc.ServerStream, dest string) error { return true } }(), - })) + }) + for _, dest := range dests[1:] { + if err := syncDir(context.TODO(), dests[0], dest); err != nil { + return errors.WithStack(err) + } + } + return errors.WithStack(err) } -func writeTargetFile(ds grpc.ServerStream, wc io.WriteCloser) error { +func syncDir(ctx context.Context, origDir, newDir string) error { + handler := func(dst, src, xattrKey string, err error) error { + bklog.G(ctx).Warn(err) + return nil + } + opts := []copy.Opt{ + copy.WithXAttrErrorHandler(handler), + copy.AllowWildcards, + } + return copy.Copy(ctx, origDir, "*", newDir, "/", opts...) +} + +func writeTargetFile(ds grpc.ServerStream, fs map[string]FileOutputFunc, opts metadata.MD) (err error) { + var wc io.WriteCloser for { bm := BytesMessage{} if err := ds.RecvMsg(&bm); err != nil { @@ -125,6 +155,36 @@ func writeTargetFile(ds grpc.ServerStream, wc io.WriteCloser) error { } return errors.WithStack(err) } + if wc == nil { + md := map[string]string{} + for k, v := range opts { + if strings.HasPrefix(k, keyExporterMetaPrefix) { + md[strings.TrimPrefix(k, keyExporterMetaPrefix)] = strings.Join(v, ",") + } + } + + if exp, ok := fs[bm.ID]; ok { + wc, err = exp(md) + } else { + // Legacy case - default to the first export + for _, exp := range fs { + wc, err = exp(md) + break + } + } + if err != nil { + return errors.WithStack(err) + } + if wc == nil { + return status.Errorf(codes.AlreadyExists, "target already exists") + } + defer func() { + err1 := wc.Close() + if err != nil { + err = err1 + } + }() + } if _, err := wc.Write(bm.Data); err != nil { return errors.WithStack(err) } diff --git a/session/filesync/filesync.go b/session/filesync/filesync.go index a51b490857f1..68fddb2e0ab9 100644 --- a/session/filesync/filesync.go +++ b/session/filesync/filesync.go @@ -34,6 +34,8 @@ 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 @@ -229,24 +231,36 @@ func FSSync(ctx context.Context, c session.Caller, opt FSSendRequestOpt) error { } // NewFSSyncTargetDir allows writing into a directory -func NewFSSyncTargetDir(outdir string) session.Attachable { - p := &fsSyncTarget{ - outdir: outdir, +func NewFSSyncTargetDir(outdirs []string) session.Attachable { + p := &fsSyncTargetDir{ + outdirs: outdirs, } return p } +type fsSyncTargetDir struct { + outdirs []string +} + +func (sp *fsSyncTargetDir) Register(server *grpc.Server) { + RegisterFSSendServer(server, sp) +} + +func (sp *fsSyncTargetDir) DiffCopy(stream FSSend_DiffCopyServer) (err error) { + return syncTargetDiffCopy(stream, sp.outdirs) +} + // NewFSSyncTarget allows writing into an io.WriteCloser -func NewFSSyncTarget(f func(map[string]string) (io.WriteCloser, error)) session.Attachable { +func NewFSSyncTarget(fs map[string]FileOutputFunc) session.Attachable { p := &fsSyncTarget{ - f: f, + fs: fs, } return p } type fsSyncTarget struct { - outdir string - f func(map[string]string) (io.WriteCloser, error) + // fs maps exporter ID -> output func + fs map[string]FileOutputFunc } func (sp *fsSyncTarget) Register(server *grpc.Server) { @@ -254,37 +268,31 @@ func (sp *fsSyncTarget) Register(server *grpc.Server) { } func (sp *fsSyncTarget) DiffCopy(stream FileSend_DiffCopyServer) (err error) { - if sp.outdir != "" { - return syncTargetDiffCopy(stream, sp.outdir) - } - - if sp.f == nil { + if len(sp.fs) == 0 { return errors.New("empty outfile and outdir") } opts, _ := metadata.FromIncomingContext(stream.Context()) // if no metadata continue with empty object - md := map[string]string{} - for k, v := range opts { - if strings.HasPrefix(k, keyExporterMetaPrefix) { - md[strings.TrimPrefix(k, keyExporterMetaPrefix)] = strings.Join(v, ",") - } + + return writeTargetFile(stream, sp.fs, opts) +} + +func CopyToCaller(ctx context.Context, fs fsutil.FS, c session.Caller, progress func(int, bool)) error { + method := session.MethodURL(_FSSend_serviceDesc.ServiceName, "diffcopy") + if !c.Supports(method) { + return copyToCallerLegacy(ctx, fs, c, progress) } - wc, err := sp.f(md) + + client := NewFSSendClient(c.Conn()) + + cc, err := client.DiffCopy(ctx) if err != nil { - return err - } - if wc == nil { - return status.Errorf(codes.AlreadyExists, "target already exists") + return errors.WithStack(err) } - defer func() { - err1 := wc.Close() - if err != nil { - err = err1 - } - }() - return writeTargetFile(stream, wc) + + return sendDiffCopy(cc, fs, progress) } -func CopyToCaller(ctx context.Context, fs fsutil.FS, c session.Caller, progress func(int, bool)) error { +func copyToCallerLegacy(ctx context.Context, fs fsutil.FS, 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) @@ -300,7 +308,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) @@ -320,7 +328,7 @@ func CopyFileWriter(ctx context.Context, md map[string]string, c session.Caller) return nil, errors.WithStack(err) } - return newStreamWriter(cc), nil + return newStreamWriter(cc, id), nil } type InvalidSessionError struct { diff --git a/session/filesync/filesync.pb.go b/session/filesync/filesync.pb.go index 6110307abb0e..141e538fff37 100644 --- a/session/filesync/filesync.pb.go +++ b/session/filesync/filesync.pb.go @@ -7,6 +7,7 @@ import ( bytes "bytes" context "context" fmt "fmt" + _ "github.com/gogo/protobuf/gogoproto" proto "github.com/gogo/protobuf/proto" types "github.com/tonistiigi/fsutil/types" grpc "google.golang.org/grpc" @@ -33,6 +34,8 @@ const _ = proto.GoGoProtoPackageIsVersion3 // please upgrade the proto package // BytesMessage contains a chunk of byte data type BytesMessage struct { Data []byte `protobuf:"bytes,1,opt,name=data,proto3" json:"data,omitempty"` + // ID identifies the exporter generating the data + ID string `protobuf:"bytes,2,opt,name=id,proto3" json:"id,omitempty"` } func (m *BytesMessage) Reset() { *m = BytesMessage{} } @@ -74,6 +77,13 @@ func (m *BytesMessage) GetData() []byte { return nil } +func (m *BytesMessage) GetID() string { + if m != nil { + return m.ID + } + return "" +} + func init() { proto.RegisterType((*BytesMessage)(nil), "moby.filesync.v1.BytesMessage") } @@ -81,25 +91,28 @@ func init() { func init() { proto.RegisterFile("filesync.proto", fileDescriptor_d1042549f1f24495) } var fileDescriptor_d1042549f1f24495 = []byte{ - // 281 bytes of a gzipped FileDescriptorProto + // 335 bytes of a gzipped FileDescriptorProto 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4b, 0xcb, 0xcc, 0x49, 0x2d, 0xae, 0xcc, 0x4b, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0xc8, 0xcd, 0x4f, 0xaa, 0xd4, 0x83, 0x0b, 0x96, 0x19, 0x4a, 0xe9, 0xa6, 0x67, 0x96, 0x64, 0x94, 0x26, 0xe9, 0x25, 0xe7, - 0xe7, 0xea, 0x97, 0xe4, 0xe7, 0x65, 0x16, 0x97, 0x64, 0x66, 0xa6, 0x67, 0xea, 0xa7, 0x15, 0x97, - 0x96, 0x64, 0xe6, 0xe8, 0x97, 0x54, 0x16, 0xa4, 0x16, 0xeb, 0x97, 0x67, 0x16, 0xa5, 0x42, 0x0c, - 0x50, 0x52, 0xe2, 0xe2, 0x71, 0xaa, 0x2c, 0x49, 0x2d, 0xf6, 0x4d, 0x2d, 0x2e, 0x4e, 0x4c, 0x4f, - 0x15, 0x12, 0xe2, 0x62, 0x49, 0x49, 0x2c, 0x49, 0x94, 0x60, 0x54, 0x60, 0xd4, 0xe0, 0x09, 0x02, - 0xb3, 0x8d, 0x9a, 0x19, 0xb9, 0x38, 0xdc, 0x32, 0x73, 0x52, 0x83, 0x2b, 0xf3, 0x92, 0x85, 0xac, - 0xb8, 0x38, 0x5c, 0x32, 0xd3, 0xd2, 0x9c, 0xf3, 0x0b, 0x2a, 0x85, 0x44, 0xf4, 0x20, 0xc6, 0xea, - 0x81, 0x8d, 0xd5, 0x0b, 0x48, 0x4c, 0xce, 0x4e, 0x2d, 0x91, 0xc2, 0x2a, 0xaa, 0xc1, 0x68, 0xc0, - 0x28, 0x64, 0xcd, 0xc5, 0x19, 0x92, 0x58, 0x14, 0x5c, 0x52, 0x94, 0x9a, 0x98, 0x4b, 0xaa, 0x66, - 0xa3, 0x28, 0xa8, 0x23, 0x52, 0xf3, 0x52, 0x84, 0xfc, 0x90, 0x1c, 0x21, 0xa7, 0x87, 0x1e, 0x06, - 0x7a, 0xc8, 0x3e, 0x92, 0x22, 0x20, 0x0f, 0x32, 0xdb, 0xc9, 0xee, 0xc2, 0x43, 0x39, 0x86, 0x1b, - 0x0f, 0xe5, 0x18, 0x3e, 0x3c, 0x94, 0x63, 0x6c, 0x78, 0x24, 0xc7, 0xb8, 0xe2, 0x91, 0x1c, 0xe3, - 0x89, 0x47, 0x72, 0x8c, 0x17, 0x1e, 0xc9, 0x31, 0x3e, 0x78, 0x24, 0xc7, 0xf8, 0xe2, 0x91, 0x1c, - 0xc3, 0x87, 0x47, 0x72, 0x8c, 0x13, 0x1e, 0xcb, 0x31, 0x5c, 0x78, 0x2c, 0xc7, 0x70, 0xe3, 0xb1, - 0x1c, 0x43, 0x14, 0x07, 0xcc, 0xcc, 0x24, 0x36, 0x70, 0x60, 0x1a, 0x03, 0x02, 0x00, 0x00, 0xff, - 0xff, 0xe6, 0x17, 0x63, 0x59, 0x9f, 0x01, 0x00, 0x00, + 0xe7, 0xea, 0xa7, 0xe7, 0xa7, 0xe7, 0xeb, 0x83, 0x15, 0x26, 0x95, 0xa6, 0x81, 0x79, 0x60, 0x0e, + 0x98, 0x05, 0x31, 0x00, 0x45, 0x79, 0x49, 0x7e, 0x5e, 0x66, 0x71, 0x49, 0x66, 0x66, 0x7a, 0xa6, + 0x7e, 0x5a, 0x71, 0x69, 0x49, 0x66, 0x8e, 0x7e, 0x49, 0x65, 0x41, 0x6a, 0xb1, 0x7e, 0x79, 0x66, + 0x51, 0x2a, 0x44, 0xb9, 0x92, 0x15, 0x17, 0x8f, 0x53, 0x65, 0x49, 0x6a, 0xb1, 0x6f, 0x6a, 0x71, + 0x71, 0x62, 0x7a, 0xaa, 0x90, 0x10, 0x17, 0x4b, 0x4a, 0x62, 0x49, 0xa2, 0x04, 0xa3, 0x02, 0xa3, + 0x06, 0x4f, 0x10, 0x98, 0x2d, 0x24, 0xc6, 0xc5, 0x94, 0x99, 0x22, 0xc1, 0xa4, 0xc0, 0xa8, 0xc1, + 0xe9, 0xc4, 0xf6, 0xe8, 0x9e, 0x3c, 0x93, 0xa7, 0x4b, 0x10, 0x53, 0x66, 0x8a, 0x51, 0x33, 0x23, + 0x17, 0x87, 0x5b, 0x66, 0x4e, 0x6a, 0x70, 0x65, 0x5e, 0xb2, 0x90, 0x15, 0x17, 0x87, 0x4b, 0x66, + 0x5a, 0x9a, 0x73, 0x7e, 0x41, 0xa5, 0x90, 0x88, 0x1e, 0xc4, 0x3a, 0x3d, 0xb0, 0x75, 0x7a, 0x01, + 0x89, 0xc9, 0xd9, 0xa9, 0x25, 0x52, 0x58, 0x45, 0x35, 0x18, 0x0d, 0x18, 0x85, 0xac, 0xb9, 0x38, + 0x43, 0x12, 0x8b, 0x82, 0x4b, 0x8a, 0x52, 0x13, 0x73, 0x49, 0xd5, 0x6c, 0xe4, 0xc2, 0xc5, 0xe6, + 0x16, 0x1c, 0x9c, 0x9a, 0x97, 0x42, 0x89, 0x13, 0x8c, 0xa2, 0xa0, 0x5e, 0x01, 0x99, 0xe3, 0x87, + 0x64, 0x8e, 0x9c, 0x1e, 0x7a, 0x84, 0xe8, 0x21, 0x87, 0x97, 0x14, 0x01, 0x79, 0x90, 0xd9, 0x4e, + 0x76, 0x17, 0x1e, 0xca, 0x31, 0xdc, 0x78, 0x28, 0xc7, 0xf0, 0xe1, 0xa1, 0x1c, 0x63, 0xc3, 0x23, + 0x39, 0xc6, 0x15, 0x8f, 0xe4, 0x18, 0x4f, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, + 0x23, 0x39, 0xc6, 0x17, 0x8f, 0xe4, 0x18, 0x3e, 0x3c, 0x92, 0x63, 0x9c, 0xf0, 0x58, 0x8e, 0xe1, + 0xc2, 0x63, 0x39, 0x86, 0x1b, 0x8f, 0xe5, 0x18, 0xa2, 0x38, 0x60, 0x66, 0x26, 0xb1, 0x81, 0xa3, + 0xca, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0x22, 0x04, 0x54, 0xac, 0x2c, 0x02, 0x00, 0x00, } func (this *BytesMessage) Equal(that interface{}) bool { @@ -124,15 +137,19 @@ func (this *BytesMessage) Equal(that interface{}) bool { if !bytes.Equal(this.Data, that1.Data) { return false } + if this.ID != that1.ID { + return false + } return true } func (this *BytesMessage) GoString() string { if this == nil { return "nil" } - s := make([]string, 0, 5) + s := make([]string, 0, 6) s = append(s, "&filesync.BytesMessage{") s = append(s, "Data: "+fmt.Sprintf("%#v", this.Data)+",\n") + s = append(s, "ID: "+fmt.Sprintf("%#v", this.ID)+",\n") s = append(s, "}") return strings.Join(s, "") } @@ -325,6 +342,110 @@ var _FileSync_serviceDesc = grpc.ServiceDesc{ Metadata: "filesync.proto", } +// FSSendClient is the client API for FSSend service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. +type FSSendClient interface { + DiffCopy(ctx context.Context, opts ...grpc.CallOption) (FSSend_DiffCopyClient, error) +} + +type fSSendClient struct { + cc *grpc.ClientConn +} + +func NewFSSendClient(cc *grpc.ClientConn) FSSendClient { + return &fSSendClient{cc} +} + +func (c *fSSendClient) DiffCopy(ctx context.Context, opts ...grpc.CallOption) (FSSend_DiffCopyClient, error) { + stream, err := c.cc.NewStream(ctx, &_FSSend_serviceDesc.Streams[0], "/moby.filesync.v1.FSSend/DiffCopy", opts...) + if err != nil { + return nil, err + } + x := &fSSendDiffCopyClient{stream} + return x, nil +} + +type FSSend_DiffCopyClient interface { + Send(*types.Packet) error + Recv() (*types.Packet, error) + grpc.ClientStream +} + +type fSSendDiffCopyClient struct { + grpc.ClientStream +} + +func (x *fSSendDiffCopyClient) Send(m *types.Packet) error { + return x.ClientStream.SendMsg(m) +} + +func (x *fSSendDiffCopyClient) Recv() (*types.Packet, error) { + m := new(types.Packet) + if err := x.ClientStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +// FSSendServer is the server API for FSSend service. +type FSSendServer interface { + DiffCopy(FSSend_DiffCopyServer) error +} + +// UnimplementedFSSendServer can be embedded to have forward compatible implementations. +type UnimplementedFSSendServer struct { +} + +func (*UnimplementedFSSendServer) DiffCopy(srv FSSend_DiffCopyServer) error { + return status.Errorf(codes.Unimplemented, "method DiffCopy not implemented") +} + +func RegisterFSSendServer(s *grpc.Server, srv FSSendServer) { + s.RegisterService(&_FSSend_serviceDesc, srv) +} + +func _FSSend_DiffCopy_Handler(srv interface{}, stream grpc.ServerStream) error { + return srv.(FSSendServer).DiffCopy(&fSSendDiffCopyServer{stream}) +} + +type FSSend_DiffCopyServer interface { + Send(*types.Packet) error + Recv() (*types.Packet, error) + grpc.ServerStream +} + +type fSSendDiffCopyServer struct { + grpc.ServerStream +} + +func (x *fSSendDiffCopyServer) Send(m *types.Packet) error { + return x.ServerStream.SendMsg(m) +} + +func (x *fSSendDiffCopyServer) Recv() (*types.Packet, error) { + m := new(types.Packet) + if err := x.ServerStream.RecvMsg(m); err != nil { + return nil, err + } + return m, nil +} + +var _FSSend_serviceDesc = grpc.ServiceDesc{ + ServiceName: "moby.filesync.v1.FSSend", + HandlerType: (*FSSendServer)(nil), + Methods: []grpc.MethodDesc{}, + Streams: []grpc.StreamDesc{ + { + StreamName: "DiffCopy", + Handler: _FSSend_DiffCopy_Handler, + ServerStreams: true, + ClientStreams: true, + }, + }, + Metadata: "filesync.proto", +} + // FileSendClient is the client API for FileSend service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. @@ -449,6 +570,13 @@ func (m *BytesMessage) MarshalToSizedBuffer(dAtA []byte) (int, error) { _ = i var l int _ = l + if len(m.ID) > 0 { + i -= len(m.ID) + copy(dAtA[i:], m.ID) + i = encodeVarintFilesync(dAtA, i, uint64(len(m.ID))) + i-- + dAtA[i] = 0x12 + } if len(m.Data) > 0 { i -= len(m.Data) copy(dAtA[i:], m.Data) @@ -480,6 +608,10 @@ func (m *BytesMessage) Size() (n int) { if l > 0 { n += 1 + l + sovFilesync(uint64(l)) } + l = len(m.ID) + if l > 0 { + n += 1 + l + sovFilesync(uint64(l)) + } return n } @@ -495,6 +627,7 @@ func (this *BytesMessage) String() string { } s := strings.Join([]string{`&BytesMessage{`, `Data:` + fmt.Sprintf("%v", this.Data) + `,`, + `ID:` + fmt.Sprintf("%v", this.ID) + `,`, `}`, }, "") return s @@ -570,6 +703,38 @@ func (m *BytesMessage) Unmarshal(dAtA []byte) error { m.Data = []byte{} } iNdEx = postIndex + case 2: + 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 ErrIntOverflowFilesync + } + 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 ErrInvalidLengthFilesync + } + postIndex := iNdEx + intStringLen + if postIndex < 0 { + return ErrInvalidLengthFilesync + } + if postIndex > l { + return io.ErrUnexpectedEOF + } + m.ID = string(dAtA[iNdEx:postIndex]) + iNdEx = postIndex default: iNdEx = preIndex skippy, err := skipFilesync(dAtA[iNdEx:]) diff --git a/session/filesync/filesync.proto b/session/filesync/filesync.proto index 9e39179285ee..9c1fd3d1956c 100644 --- a/session/filesync/filesync.proto +++ b/session/filesync/filesync.proto @@ -4,6 +4,7 @@ package moby.filesync.v1; option go_package = "filesync"; +import "github.com/gogo/protobuf/gogoproto/gogo.proto"; import "github.com/tonistiigi/fsutil/types/wire.proto"; service FileSync{ @@ -11,12 +12,17 @@ service FileSync{ rpc TarStream(stream fsutil.types.Packet) returns (stream fsutil.types.Packet); } +service FSSend{ + rpc DiffCopy(stream fsutil.types.Packet) returns (stream fsutil.types.Packet); +} + service FileSend{ rpc DiffCopy(stream BytesMessage) returns (stream BytesMessage); } - // BytesMessage contains a chunk of byte data message BytesMessage{ bytes data = 1; + // ID identifies the exporter generating the data + string id = 2 [(gogoproto.customname) = "ID"]; } diff --git a/solver/llbsolver/solver.go b/solver/llbsolver/solver.go index 9295e08c6372..d78003f6af27 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 } @@ -551,23 +551,10 @@ 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.runExporters(ctx, exp.Exporters, inlineCacheExporter, j, cached, inp) + if err != nil { + return nil, err } cacheExporterResponse, err := runCacheExporters(ctx, cacheExporters, j, cached, inp) @@ -647,42 +634,92 @@ 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 *exptypes.InlineCache, err error) { + result = &exptypes.InlineCache{} if inlineExporter == nil { return nil, nil } 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)) + dtic, err := inlineCache(ctx, inlineExporter, res, e.Config().Compression(), session.NewGroup(j.SessionID)) if err != nil { return err } if dtic != nil { - meta[exptypes.ExporterInlineCache] = dtic + result.Cache = dtic } } for k, res := range cached.Refs { - dtic, err := inlineCache(ctx, inlineExporter.Exporter, res, e.Config().Compression(), session.NewGroup(j.SessionID)) + dtic, err := inlineCache(ctx, inlineExporter, 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 + result.Platforms[k] = dtic } } return nil }); err != nil { return nil, err } - return meta, nil + return result, nil } -func splitCacheExporters(exporters []RemoteCacheExporter) (rest []RemoteCacheExporter, inline *RemoteCacheExporter) { - rest = make([]RemoteCacheExporter, 0, len(exporters)) +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, descref exporter.DescriptorReference, err error) { + eg, ctx := errgroup.WithContext(ctx) + sessionID := job.SessionID + resps := make([]map[string]string, len(exporters)) for i, exp := range exporters { - if _, ok := asInlineCache(exp.Exporter); ok { - inline = &exporters[i] + func(exp exporter.ExporterInstance, i int) { + eg.Go(func() error { + if imageExporter, ok := asImageExporter(exp); ok { + inlineCache, err := runInlineCacheExporter(ctx, exp, inlineCacheExporter, job, cached) + if err != nil { + return err + } + return inBuilderContext(ctx, job, exp.Name(), job.SessionID+"-export", func(ctx context.Context, _ session.Group) (err error) { + resps[i], err = imageExporter.ExportImage(ctx, inp, *inlineCache, sessionID) + if err != nil { + return err + } + return nil + }) + } + return inBuilderContext(ctx, job, exp.Name(), job.SessionID+"-export", func(ctx context.Context, _ session.Group) error { + var dref exporter.DescriptorReference + resps[i], dref, err = exp.Export(ctx, inp, sessionID) + if err != nil { + return err + } + if dref != nil { + descref = dref + } + return nil + }) + }) + }(exp, i) + } + if err := eg.Wait(); err != nil { + return nil, nil, err + } + + for _, resp := range resps { + for k, v := range resp { + if exporterResponse == nil { + exporterResponse = make(map[string]string) + } + exporterResponse[k] = v + } + } + return exporterResponse, descref, nil +} + +func splitCacheExporters(exporters []RemoteCacheExporter) (rest []RemoteCacheExporter, inline inlineCacheExporter) { + rest = make([]RemoteCacheExporter, 0, len(exporters)) + for _, exp := range exporters { + if ic, ok := asInlineCache(exp.Exporter); ok { + inline = ic + fmt.Printf("Found inline cache exporter (%T)\n", ic) continue } rest = append(rest, exp) @@ -819,7 +856,18 @@ func getProvenance(ref solver.ResultProxy, br *provenanceBridge, id string, reqs return pr, nil } +type imageExporterInstance interface { + exporter.ExporterInstance + ExportImage(ctx context.Context, src *exporter.Source, cache exptypes.InlineCache, sessionID string) (map[string]string, error) +} + +func asImageExporter(e exporter.ExporterInstance) (imageExporterInstance, bool) { + ie, ok := e.(imageExporterInstance) + return ie, ok +} + type inlineCacheExporter interface { + solver.CacheExporterTarget ExportForLayers(context.Context, []digest.Digest) ([]byte, error) } @@ -828,11 +876,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 +895,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, diff --git a/solver/result/result.go b/solver/result/result.go index cfcfe9dcbd3e..0a445d66c71f 100644 --- a/solver/result/result.go +++ b/solver/result/result.go @@ -7,7 +7,6 @@ import ( ) type Result[T comparable] struct { - mu sync.Mutex Ref T Refs map[string]T Metadata map[string][]byte @@ -15,30 +14,24 @@ type Result[T comparable] struct { } func (r *Result[T]) AddMeta(k string, v []byte) { - r.mu.Lock() if r.Metadata == nil { r.Metadata = map[string][]byte{} } r.Metadata[k] = v - r.mu.Unlock() } func (r *Result[T]) AddRef(k string, ref T) { - r.mu.Lock() if r.Refs == nil { r.Refs = map[string]T{} } r.Refs[k] = ref - r.mu.Unlock() } func (r *Result[T]) AddAttestation(k string, v Attestation[T]) { - r.mu.Lock() if r.Attestations == nil { r.Attestations = map[string][]Attestation[T]{} } r.Attestations[k] = append(r.Attestations[k], v) - r.mu.Unlock() } func (r *Result[T]) SetRef(ref T) { @@ -46,9 +39,6 @@ func (r *Result[T]) SetRef(ref T) { } func (r *Result[T]) SingleRef() (T, error) { - r.mu.Lock() - defer r.mu.Unlock() - var zero T if r.Refs != nil && r.Ref == zero { var t T @@ -58,9 +48,6 @@ func (r *Result[T]) SingleRef() (T, error) { } func (r *Result[T]) FindRef(key string) (T, bool) { - r.mu.Lock() - defer r.mu.Unlock() - if r.Refs != nil { if ref, ok := r.Refs[key]; ok { return ref, true diff --git a/worker/base/worker.go b/worker/base/worker.go index 8d402ff2fb1b..34bd617adb05 100644 --- a/worker/base/worker.go +++ b/worker/base/worker.go @@ -382,7 +382,7 @@ func (w *Worker) Prune(ctx context.Context, ch chan client.UsageInfo, opt ...cli return w.CacheMgr.Prune(ctx, ch, opt...) } -func (w *Worker) Exporter(name string, sm *session.Manager) (exporter.Exporter, error) { +func (w *Worker) Exporter(name, id string, sm *session.Manager) (exporter.Exporter, error) { switch name { case client.ExporterImage: return imageexporter.New(imageexporter.Opt{ @@ -399,6 +399,7 @@ func (w *Worker) Exporter(name string, sm *session.Manager) (exporter.Exporter, case client.ExporterTar: return tarexporter.New(tarexporter.Opt{ SessionManager: sm, + ID: id, }) case client.ExporterOCI: return ociexporter.New(ociexporter.Opt{ @@ -406,6 +407,7 @@ func (w *Worker) Exporter(name string, sm *session.Manager) (exporter.Exporter, ImageWriter: w.imageWriter, Variant: ociexporter.VariantOCI, LeaseManager: w.LeaseManager(), + ID: id, }) case client.ExporterDocker: return ociexporter.New(ociexporter.Opt{ @@ -413,6 +415,7 @@ func (w *Worker) Exporter(name string, sm *session.Manager) (exporter.Exporter, ImageWriter: w.imageWriter, Variant: ociexporter.VariantDocker, LeaseManager: w.LeaseManager(), + ID: id, }) default: return nil, errors.Errorf("exporter %q could not be found", name) diff --git a/worker/worker.go b/worker/worker.go index d62047e9fb5f..94ff537dd618 100644 --- a/worker/worker.go +++ b/worker/worker.go @@ -32,7 +32,7 @@ type Worker interface { ResolveOp(v solver.Vertex, s frontend.FrontendLLBBridge, sm *session.Manager) (solver.Op, error) ResolveImageConfig(ctx context.Context, ref string, opt llb.ResolveImageConfigOpt, sm *session.Manager, g session.Group) (string, digest.Digest, []byte, error) DiskUsage(ctx context.Context, opt client.DiskUsageInfo) ([]*client.UsageInfo, error) - Exporter(name string, sm *session.Manager) (exporter.Exporter, error) + Exporter(name, id string, sm *session.Manager) (exporter.Exporter, error) Prune(ctx context.Context, ch chan client.UsageInfo, opt ...client.PruneInfo) error FromRemote(ctx context.Context, remote *solver.Remote) (cache.ImmutableRef, error) PruneCacheMounts(ctx context.Context, ids []string) error From 7a9405e999d755b17ba94f3fe1ad1425decba2cf Mon Sep 17 00:00:00 2001 From: a-palchikov Date: Thu, 12 Jan 2023 15:05:44 +0100 Subject: [PATCH 2/7] Update the signature of the expected image exporter interface to match the implementations Signed-off-by: a-palchikov --- solver/llbsolver/solver.go | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/solver/llbsolver/solver.go b/solver/llbsolver/solver.go index d78003f6af27..aaf99fe8dbc2 100644 --- a/solver/llbsolver/solver.go +++ b/solver/llbsolver/solver.go @@ -637,7 +637,7 @@ func runCacheExporters(ctx context.Context, exporters []RemoteCacheExporter, j * func runInlineCacheExporter(ctx context.Context, e exporter.ExporterInstance, inlineExporter inlineCacheExporter, j *solver.Job, cached *result.Result[solver.CachedResult]) (result *exptypes.InlineCache, err error) { result = &exptypes.InlineCache{} if inlineExporter == nil { - return nil, nil + return result, nil } 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 { @@ -678,10 +678,14 @@ func (s *Solver) runExporters(ctx context.Context, exporters []exporter.Exporter return err } return inBuilderContext(ctx, job, exp.Name(), job.SessionID+"-export", func(ctx context.Context, _ session.Group) (err error) { - resps[i], err = imageExporter.ExportImage(ctx, inp, *inlineCache, sessionID) + var dref exporter.DescriptorReference + resps[i], dref, err = imageExporter.ExportImage(ctx, inp, *inlineCache, sessionID) if err != nil { return err } + if dref != nil { + descref = dref + } return nil }) } @@ -719,7 +723,6 @@ func splitCacheExporters(exporters []RemoteCacheExporter) (rest []RemoteCacheExp for _, exp := range exporters { if ic, ok := asInlineCache(exp.Exporter); ok { inline = ic - fmt.Printf("Found inline cache exporter (%T)\n", ic) continue } rest = append(rest, exp) @@ -858,7 +861,7 @@ func getProvenance(ref solver.ResultProxy, br *provenanceBridge, id string, reqs type imageExporterInstance interface { exporter.ExporterInstance - ExportImage(ctx context.Context, src *exporter.Source, cache exptypes.InlineCache, sessionID string) (map[string]string, error) + ExportImage(ctx context.Context, src *exporter.Source, cache exptypes.InlineCache, sessionID string) (map[string]string, exporter.DescriptorReference, error) } func asImageExporter(e exporter.ExporterInstance) (imageExporterInstance, bool) { From 64704b3c4c98a6bbde83df6e6c9a55f0e4996ac8 Mon Sep 17 00:00:00 2001 From: a-palchikov Date: Thu, 2 Mar 2023 12:14:27 +0100 Subject: [PATCH 3/7] Pass the configured options on to the local exporter. Still the question remains as to what to do to possibly different options per local exporter since it's currently replicated on the client but the attestation files are generated on the server. Signed-off-by: a-palchikov --- client/solve.go | 46 +++++++++++++++++++------------ control/control.go | 6 ++-- exporter/containerimage/writer.go | 2 -- 3 files changed, 30 insertions(+), 24 deletions(-) diff --git a/client/solve.go b/client/solve.go index 55e1b59b66db..7645f001d594 100644 --- a/client/solve.go +++ b/client/solve.go @@ -283,7 +283,7 @@ func (c *Client) solve(ctx context.Context, def *llb.Definition, runGateway runG } exports := make([]*controlapi.Exporter, 0, len(opt.Exports)) - var localExporter bool + var localExporter *controlapi.Exporter for _, exp := range exporters { if exp.Type != ExporterLocal { exports = append(exports, &controlapi.Exporter{ @@ -291,30 +291,40 @@ func (c *Client) solve(ctx context.Context, def *llb.Definition, runGateway runG Type: exp.Type, Attrs: exp.Attrs, }) - } else { - localExporter = true + } else if localExporter == nil { + // TODO(dima): different options per local exporter? + localExporter = &controlapi.Exporter{ + Type: ExporterLocal, + Attrs: exp.Attrs, + } } } - if localExporter { + if localExporter != nil { // Add a single instance of the local exporter // since it's replicated entirely on the client - exports = append(exports, &controlapi.Exporter{ - Type: ExporterLocal, - }) + exports = append(exports, localExporter) + } + + // For older server, use the first exporter + var ex ExportEntry + if len(opt.Exports) != 0 { + ex = opt.Exports[0] } resp, err := c.ControlClient().Solve(ctx, &controlapi.SolveRequest{ - Ref: ref, - Definition: pbd, - Exporters: exports, - 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, + ExporterDeprecated: ex.Type, + ExporterAttrsDeprecated: ex.Attrs, + Exporters: exports, + 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/control/control.go b/control/control.go index 6a00da211058..749dc4fe237f 100644 --- a/control/control.go +++ b/control/control.go @@ -311,7 +311,7 @@ func translateLegacySolveRequest(req *controlapi.SolveRequest) { req.Cache.Imports = append(req.Cache.Imports, im) } req.Cache.ImportRefsDeprecated = nil - // translate single exporter to a slice (v0.11.0) + // 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, @@ -332,7 +332,6 @@ func (c *Controller) Solve(ctx context.Context, req *controlapi.SolveRequest) (* time.AfterFunc(time.Second, c.throttledGC) }() - var expis []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() @@ -352,6 +351,7 @@ func (c *Controller) Solve(ctx context.Context, req *controlapi.SolveRequest) (* } } + var expis []exporter.ExporterInstance for _, ex := range req.Exporters { exp, err := w.Exporter(ex.Type, ex.ID, c.opt.SessionManager) if err != nil { @@ -455,8 +455,6 @@ func (c *Controller) Solve(ctx context.Context, req *controlapi.SolveRequest) (* }, llbsolver.ExporterRequest{ 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/exporter/containerimage/writer.go b/exporter/containerimage/writer.go index ba4223f08f64..6e469623066a 100644 --- a/exporter/containerimage/writer.go +++ b/exporter/containerimage/writer.go @@ -190,8 +190,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) - // FIXME(dima) - //inlineCache := exptypes.ParseKey(inp.Metadata, exptypes.ExporterInlineCache, p) remote := &remotes[remotesMap[p.ID]] if remote == nil { From 75b12378a20131efd1aeb5b97736c1d59029f7fb Mon Sep 17 00:00:00 2001 From: a-palchikov Date: Thu, 2 Mar 2023 22:35:58 +0100 Subject: [PATCH 4/7] Fix a concurrent map write. Fix nil map write Signed-off-by: a-palchikov --- frontend/dockerui/build.go | 36 ++++++++++++++++++++++++++++++------ solver/llbsolver/solver.go | 4 +++- 2 files changed, 33 insertions(+), 7 deletions(-) diff --git a/frontend/dockerui/build.go b/frontend/dockerui/build.go index 8fc9bbbff11e..11c3d9ab89c1 100644 --- a/frontend/dockerui/build.go +++ b/frontend/dockerui/build.go @@ -4,6 +4,7 @@ import ( "context" "encoding/json" "fmt" + "sync" "github.com/containerd/containerd/platforms" "github.com/moby/buildkit/exporter/containerimage/exptypes" @@ -17,7 +18,7 @@ import ( type BuildFunc func(ctx context.Context, platform *ocispecs.Platform, idx int) (client.Reference, *image.Image, error) func (bc *Client) Build(ctx context.Context, fn BuildFunc) (*ResultBuilder, error) { - res := client.NewResult() + res := &mutableResult{res: client.NewResult()} targets := make([]*ocispecs.Platform, 0, len(bc.TargetPlatforms)) for _, p := range bc.TargetPlatforms { @@ -65,11 +66,11 @@ func (bc *Client) Build(ctx context.Context, fn BuildFunc) (*ResultBuilder, erro k := platforms.Format(p) if bc.MultiPlatformRequested { - res.AddRef(k, ref) - res.AddMeta(fmt.Sprintf("%s/%s", exptypes.ExporterImageConfigKey, k), config) + res.addRef(k, ref) + res.addMeta(fmt.Sprintf("%s/%s", exptypes.ExporterImageConfigKey, k), config) } else { - res.SetRef(ref) - res.AddMeta(exptypes.ExporterImageConfigKey, config) + res.setRef(ref) + res.addMeta(exptypes.ExporterImageConfigKey, config) } expPlatforms.Platforms[i] = exptypes.Platform{ ID: k, @@ -82,7 +83,7 @@ func (bc *Client) Build(ctx context.Context, fn BuildFunc) (*ResultBuilder, erro return nil, err } return &ResultBuilder{ - Result: res, + Result: res.res, expPlatforms: expPlatforms, }, nil } @@ -112,3 +113,26 @@ func (rb *ResultBuilder) EachPlatform(ctx context.Context, fn func(ctx context.C } return eg.Wait() } + +func (r *mutableResult) addMeta(k string, v []byte) { + r.mu.Lock() + r.res.AddMeta(k, v) + r.mu.Unlock() +} + +func (r *mutableResult) addRef(k string, ref client.Reference) { + r.mu.Lock() + r.res.AddRef(k, ref) + r.mu.Unlock() +} + +func (r *mutableResult) setRef(ref client.Reference) { + r.mu.Lock() + r.res.SetRef(ref) + r.mu.Unlock() +} + +type mutableResult struct { + mu sync.Mutex + res *client.Result +} diff --git a/solver/llbsolver/solver.go b/solver/llbsolver/solver.go index aaf99fe8dbc2..06dc3fd59185 100644 --- a/solver/llbsolver/solver.go +++ b/solver/llbsolver/solver.go @@ -635,7 +635,9 @@ func runCacheExporters(ctx context.Context, exporters []RemoteCacheExporter, j * } func runInlineCacheExporter(ctx context.Context, e exporter.ExporterInstance, inlineExporter inlineCacheExporter, j *solver.Job, cached *result.Result[solver.CachedResult]) (result *exptypes.InlineCache, err error) { - result = &exptypes.InlineCache{} + result = &exptypes.InlineCache{ + Platforms: make(map[string][]byte), + } if inlineExporter == nil { return result, nil } From 7b845fd265fe460c8230e9502c8c9eb4235d4e1f Mon Sep 17 00:00:00 2001 From: a-palchikov Date: Mon, 6 Mar 2023 12:35:17 +0100 Subject: [PATCH 5/7] Address review comments Signed-off-by: a-palchikov --- client/solve.go | 32 ++++++++++++-------------------- session/filesync/filesync.go | 1 - solver/result/result.go | 2 -- 3 files changed, 12 insertions(+), 23 deletions(-) diff --git a/client/solve.go b/client/solve.go index 7645f001d594..25f270f508f1 100644 --- a/client/solve.go +++ b/client/solve.go @@ -186,7 +186,7 @@ func (c *Client) solve(ctx context.Context, def *llb.Definition, runGateway runG supportFile = ex.Output != nil } if supportFile && supportDir { - return nil, errors.Errorf("both file and directory output is not support by %s exporter", ex.Type) + 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) @@ -305,26 +305,18 @@ func (c *Client) solve(ctx context.Context, def *llb.Definition, runGateway runG exports = append(exports, localExporter) } - // For older server, use the first exporter - var ex ExportEntry - if len(opt.Exports) != 0 { - ex = opt.Exports[0] - } - resp, err := c.ControlClient().Solve(ctx, &controlapi.SolveRequest{ - Ref: ref, - Definition: pbd, - ExporterDeprecated: ex.Type, - ExporterAttrsDeprecated: ex.Attrs, - Exporters: exports, - 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, + 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/session/filesync/filesync.go b/session/filesync/filesync.go index 68fddb2e0ab9..b5c6606ce646 100644 --- a/session/filesync/filesync.go +++ b/session/filesync/filesync.go @@ -7,7 +7,6 @@ import ( "net/url" "os" "strconv" - "strings" "unicode" "github.com/moby/buildkit/session" diff --git a/solver/result/result.go b/solver/result/result.go index 0a445d66c71f..d77febc0c3a6 100644 --- a/solver/result/result.go +++ b/solver/result/result.go @@ -1,8 +1,6 @@ package result import ( - "sync" - "github.com/pkg/errors" ) From 9741d62dbd526c2052637c3a3e49c0536e913cbe Mon Sep 17 00:00:00 2001 From: a-palchikov Date: Tue, 1 Aug 2023 12:04:40 +0200 Subject: [PATCH 6/7] Address review comments: * assign the input source in exporters to avoid re-using the stale reference * remove the FSSend gRPC service - instead rely on gRPC metadata to communicate the functional protocol Signed-off-by: a-palchikov --- client/solve.go | 7 +- exporter/containerimage/export.go | 15 +-- exporter/oci/export.go | 20 ++-- session/filesync/filesync.go | 69 +++++--------- session/filesync/filesync.pb.go | 148 +++++------------------------- session/filesync/filesync.proto | 4 - 6 files changed, 64 insertions(+), 199 deletions(-) diff --git a/client/solve.go b/client/solve.go index 25f270f508f1..eb17edea1a8f 100644 --- a/client/solve.go +++ b/client/solve.go @@ -228,11 +228,8 @@ func (c *Client) solve(ctx context.Context, def *llb.Definition, runGateway runG s.Allow(sessioncontent.NewAttachable(contentStores)) } - if len(exporterConfig.outputDirs) > 0 { - s.Allow(filesync.NewFSSyncTargetDir(exporterConfig.outputDirs)) - } - if len(exporterConfig.outputs) > 0 { - s.Allow(filesync.NewFSSyncTarget(exporterConfig.outputs)) + if len(exporterConfig.outputDirs) > 0 || len(exporterConfig.outputs) > 0 { + s.Allow(filesync.NewFSSyncTarget(exporterConfig.outputs, exporterConfig.outputDirs)) } eg.Go(func() error { diff --git a/exporter/containerimage/export.go b/exporter/containerimage/export.go index 6efaf773c8f3..a200709582c9 100644 --- a/exporter/containerimage/export.go +++ b/exporter/containerimage/export.go @@ -191,16 +191,17 @@ func (e *imageExporterInstance) Export(ctx context.Context, src *exporter.Source return e.ExportImage(ctx, src, exptypes.InlineCache{}, sessionID) } -func (e *imageExporterInstance) ExportImage(ctx context.Context, inp *exporter.Source, inlineCache exptypes.InlineCache, sessionID string) (_ map[string]string, descref exporter.DescriptorReference, err error) { +func (e *imageExporterInstance) ExportImage(ctx context.Context, src *exporter.Source, inlineCache exptypes.InlineCache, sessionID string) (_ map[string]string, descref exporter.DescriptorReference, err error) { meta := make(map[string][]byte) - for k, v := range inp.Metadata { + for k, v := range src.Metadata { meta[k] = v } for k, v := range e.meta { meta[k] = v } - src := *inp - src.Metadata = meta + inp := *src + inp.Metadata = meta + src = &inp opts := e.opts as, _, err := ParseAnnotations(meta) @@ -220,7 +221,7 @@ func (e *imageExporterInstance) ExportImage(ctx context.Context, inp *exporter.S } }() - desc, err := e.opt.ImageWriter.Commit(ctx, &src, sessionID, &opts) + desc, err := e.opt.ImageWriter.Commit(ctx, src, sessionID, &opts) if err != nil { return nil, nil, err } @@ -282,7 +283,7 @@ func (e *imageExporterInstance) ExportImage(ctx context.Context, inp *exporter.S tagDone(nil) if e.unpack { - if err := e.unpackImage(ctx, img, &src, session.NewGroup(sessionID)); err != nil { + if err := e.unpackImage(ctx, img, src, session.NewGroup(sessionID)); err != nil { return nil, nil, err } } @@ -318,7 +319,7 @@ func (e *imageExporterInstance) ExportImage(ctx context.Context, inp *exporter.S } } if e.push { - err := e.pushImage(ctx, &src, sessionID, targetName, desc.Digest) + err := e.pushImage(ctx, src, sessionID, targetName, desc.Digest) if err != nil { return nil, nil, errors.Wrapf(err, "failed to push %v", targetName) } diff --git a/exporter/oci/export.go b/exporter/oci/export.go index 8920b8f5e932..3e67a5d60042 100644 --- a/exporter/oci/export.go +++ b/exporter/oci/export.go @@ -117,20 +117,21 @@ func (e *imageExporterInstance) Export(ctx context.Context, src *exporter.Source return e.ExportImage(ctx, src, exptypes.InlineCache{}, sessionID) } -func (e *imageExporterInstance) ExportImage(ctx context.Context, inp *exporter.Source, inlineCache exptypes.InlineCache, sessionID string) (_ map[string]string, descref exporter.DescriptorReference, err error) { - if e.opt.Variant == VariantDocker && len(inp.Refs) > 0 { +func (e *imageExporterInstance) ExportImage(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") } meta := make(map[string][]byte) - for k, v := range inp.Metadata { + for k, v := range src.Metadata { meta[k] = v } for k, v := range e.meta { meta[k] = v } - src := *inp - src.Metadata = meta + inp := *src + inp.Metadata = meta + src = &inp opts := e.opts as, _, err := containerimage.ParseAnnotations(meta) @@ -138,11 +139,6 @@ func (e *imageExporterInstance) ExportImage(ctx context.Context, inp *exporter.S return nil, nil, err } opts.Annotations = opts.Annotations.Merge(as) - - if err != nil { - return nil, nil, err - } - opts.Annotations = as.Merge(opts.Annotations) opts.InlineCache = inlineCache ctx, done, err := leaseutil.WithLease(ctx, e.opt.LeaseManager, leaseutil.MakeTemporary) @@ -155,7 +151,7 @@ func (e *imageExporterInstance) ExportImage(ctx context.Context, inp *exporter.S } }() - desc, err := e.opt.ImageWriter.Commit(ctx, &src, sessionID, &opts) + desc, err := e.opt.ImageWriter.Commit(ctx, src, sessionID, &opts) if err != nil { return nil, nil, err } @@ -190,7 +186,7 @@ func (e *imageExporterInstance) ExportImage(ctx context.Context, inp *exporter.S } resp[exptypes.ExporterImageDescriptorKey] = base64.StdEncoding.EncodeToString(dtdesc) - if n, ok := meta["image.name"]; e.opts.ImageName == "*" && ok { + if n, ok := src.Metadata["image.name"]; e.opts.ImageName == "*" && ok { e.opts.ImageName = string(n) } diff --git a/session/filesync/filesync.go b/session/filesync/filesync.go index b5c6606ce646..bbd3272d271a 100644 --- a/session/filesync/filesync.go +++ b/session/filesync/filesync.go @@ -24,6 +24,7 @@ const ( keyExcludePatterns = "exclude-patterns" keyFollowPaths = "followpaths" keyDirName = "dir-name" + keyDirSync = "dir-sync" keyExporterMetaPrefix = "exporter-md-" ) @@ -67,6 +68,7 @@ func (sp *fsSyncProvider) Register(server *grpc.Server) { func (sp *fsSyncProvider) DiffCopy(stream FileSync_DiffCopyServer) error { return sp.handle("diffcopy", stream) } + func (sp *fsSyncProvider) TarStream(stream FileSync_TarStreamServer) error { return sp.handle("tarstream", stream) } @@ -229,37 +231,20 @@ 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(outdirs []string) session.Attachable { - p := &fsSyncTargetDir{ - outdirs: outdirs, - } - return p -} - -type fsSyncTargetDir struct { - outdirs []string -} - -func (sp *fsSyncTargetDir) Register(server *grpc.Server) { - RegisterFSSendServer(server, sp) -} - -func (sp *fsSyncTargetDir) DiffCopy(stream FSSend_DiffCopyServer) (err error) { - return syncTargetDiffCopy(stream, sp.outdirs) -} - // NewFSSyncTarget allows writing into an io.WriteCloser -func NewFSSyncTarget(fs map[string]FileOutputFunc) session.Attachable { +// or stream a file system to the client +func NewFSSyncTarget(fs map[string]FileOutputFunc, outdirs []string) session.Attachable { p := &fsSyncTarget{ - fs: fs, + fs: fs, + outdirs: outdirs, } return p } type fsSyncTarget struct { // fs maps exporter ID -> output func - fs map[string]FileOutputFunc + fs map[string]FileOutputFunc + outdirs []string } func (sp *fsSyncTarget) Register(server *grpc.Server) { @@ -267,38 +252,31 @@ func (sp *fsSyncTarget) Register(server *grpc.Server) { } func (sp *fsSyncTarget) DiffCopy(stream FileSend_DiffCopyServer) (err error) { - if len(sp.fs) == 0 { - return errors.New("empty outfile and outdir") + if len(sp.fs) == 0 && len(sp.outdirs) == 0 { + return errors.New("empty outfile and outdirs") } opts, _ := metadata.FromIncomingContext(stream.Context()) // if no metadata continue with empty object + if _, ok := opts[keyDirSync]; ok { + return syncTargetDiffCopy(stream, sp.outdirs) + } + return writeTargetFile(stream, sp.fs, opts) } func CopyToCaller(ctx context.Context, fs fsutil.FS, c session.Caller, progress func(int, bool)) error { - method := session.MethodURL(_FSSend_serviceDesc.ServiceName, "diffcopy") + method := session.MethodURL(_FileSend_serviceDesc.ServiceName, "diffcopy") if !c.Supports(method) { - return copyToCallerLegacy(ctx, fs, c, progress) + return errors.Errorf("method %s not supported by the client", method) } - client := NewFSSendClient(c.Conn()) - - cc, err := client.DiffCopy(ctx) - if err != nil { - return errors.WithStack(err) + opts := map[string][]string{ + keyDirSync: {"true"}, } - return sendDiffCopy(cc, fs, progress) -} - -func copyToCallerLegacy(ctx context.Context, fs fsutil.FS, 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) - } + ctx = metadata.NewOutgoingContext(ctx, opts) client := NewFileSendClient(c.Conn()) - cc, err := client.DiffCopy(ctx) if err != nil { return errors.WithStack(err) @@ -358,13 +336,13 @@ func decodeOpts(opts map[string][]string) map[string][]string { md := make(map[string][]string, len(opts)) for k, v := range opts { out := make([]string, len(v)) - var isDecoded bool + var isEncoded bool if v, ok := opts[k+"-encoded"]; ok && len(v) > 0 { if b, _ := strconv.ParseBool(v[0]); b { - isDecoded = true + isEncoded = true } } - if isDecoded { + if isEncoded { for i, s := range v { out[i], _ = url.QueryUnescape(s) } @@ -380,13 +358,14 @@ func decodeOpts(opts map[string][]string) map[string][]string { // is backwards compatible and avoids encoding ASCII characters. func encodeStringForHeader(inputs []string) ([]string, bool) { var encode bool +L: for _, input := range inputs { for _, runeVal := range input { // Only encode non-ASCII characters, and characters that have special // meaning during decoding. if runeVal > unicode.MaxASCII { encode = true - break + break L } } } diff --git a/session/filesync/filesync.pb.go b/session/filesync/filesync.pb.go index 141e538fff37..c851b44bce3d 100644 --- a/session/filesync/filesync.pb.go +++ b/session/filesync/filesync.pb.go @@ -91,28 +91,28 @@ func init() { func init() { proto.RegisterFile("filesync.proto", fileDescriptor_d1042549f1f24495) } var fileDescriptor_d1042549f1f24495 = []byte{ - // 335 bytes of a gzipped FileDescriptorProto - 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0xe2, 0xe2, 0x4b, 0xcb, 0xcc, 0x49, - 0x2d, 0xae, 0xcc, 0x4b, 0xd6, 0x2b, 0x28, 0xca, 0x2f, 0xc9, 0x17, 0x12, 0xc8, 0xcd, 0x4f, 0xaa, - 0xd4, 0x83, 0x0b, 0x96, 0x19, 0x4a, 0xe9, 0xa6, 0x67, 0x96, 0x64, 0x94, 0x26, 0xe9, 0x25, 0xe7, - 0xe7, 0xea, 0xa7, 0xe7, 0xa7, 0xe7, 0xeb, 0x83, 0x15, 0x26, 0x95, 0xa6, 0x81, 0x79, 0x60, 0x0e, - 0x98, 0x05, 0x31, 0x00, 0x45, 0x79, 0x49, 0x7e, 0x5e, 0x66, 0x71, 0x49, 0x66, 0x66, 0x7a, 0xa6, - 0x7e, 0x5a, 0x71, 0x69, 0x49, 0x66, 0x8e, 0x7e, 0x49, 0x65, 0x41, 0x6a, 0xb1, 0x7e, 0x79, 0x66, - 0x51, 0x2a, 0x44, 0xb9, 0x92, 0x15, 0x17, 0x8f, 0x53, 0x65, 0x49, 0x6a, 0xb1, 0x6f, 0x6a, 0x71, - 0x71, 0x62, 0x7a, 0xaa, 0x90, 0x10, 0x17, 0x4b, 0x4a, 0x62, 0x49, 0xa2, 0x04, 0xa3, 0x02, 0xa3, - 0x06, 0x4f, 0x10, 0x98, 0x2d, 0x24, 0xc6, 0xc5, 0x94, 0x99, 0x22, 0xc1, 0xa4, 0xc0, 0xa8, 0xc1, - 0xe9, 0xc4, 0xf6, 0xe8, 0x9e, 0x3c, 0x93, 0xa7, 0x4b, 0x10, 0x53, 0x66, 0x8a, 0x51, 0x33, 0x23, - 0x17, 0x87, 0x5b, 0x66, 0x4e, 0x6a, 0x70, 0x65, 0x5e, 0xb2, 0x90, 0x15, 0x17, 0x87, 0x4b, 0x66, - 0x5a, 0x9a, 0x73, 0x7e, 0x41, 0xa5, 0x90, 0x88, 0x1e, 0xc4, 0x3a, 0x3d, 0xb0, 0x75, 0x7a, 0x01, - 0x89, 0xc9, 0xd9, 0xa9, 0x25, 0x52, 0x58, 0x45, 0x35, 0x18, 0x0d, 0x18, 0x85, 0xac, 0xb9, 0x38, - 0x43, 0x12, 0x8b, 0x82, 0x4b, 0x8a, 0x52, 0x13, 0x73, 0x49, 0xd5, 0x6c, 0xe4, 0xc2, 0xc5, 0xe6, - 0x16, 0x1c, 0x9c, 0x9a, 0x97, 0x42, 0x89, 0x13, 0x8c, 0xa2, 0xa0, 0x5e, 0x01, 0x99, 0xe3, 0x87, - 0x64, 0x8e, 0x9c, 0x1e, 0x7a, 0x84, 0xe8, 0x21, 0x87, 0x97, 0x14, 0x01, 0x79, 0x90, 0xd9, 0x4e, - 0x76, 0x17, 0x1e, 0xca, 0x31, 0xdc, 0x78, 0x28, 0xc7, 0xf0, 0xe1, 0xa1, 0x1c, 0x63, 0xc3, 0x23, - 0x39, 0xc6, 0x15, 0x8f, 0xe4, 0x18, 0x4f, 0x3c, 0x92, 0x63, 0xbc, 0xf0, 0x48, 0x8e, 0xf1, 0xc1, - 0x23, 0x39, 0xc6, 0x17, 0x8f, 0xe4, 0x18, 0x3e, 0x3c, 0x92, 0x63, 0x9c, 0xf0, 0x58, 0x8e, 0xe1, - 0xc2, 0x63, 0x39, 0x86, 0x1b, 0x8f, 0xe5, 0x18, 0xa2, 0x38, 0x60, 0x66, 0x26, 0xb1, 0x81, 0xa3, - 0xca, 0x18, 0x10, 0x00, 0x00, 0xff, 0xff, 0x22, 0x04, 0x54, 0xac, 0x2c, 0x02, 0x00, 0x00, + // 322 bytes of a gzipped FileDescriptorProto + 0x1f, 0x8b, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xff, 0x94, 0x50, 0x3d, 0x4b, 0x03, 0x41, + 0x14, 0xbc, 0x77, 0x48, 0x48, 0x96, 0x20, 0xb2, 0x88, 0x84, 0x14, 0xcf, 0x60, 0x95, 0xc6, 0x3d, + 0x8d, 0x5d, 0x04, 0x8b, 0x18, 0x04, 0x0b, 0x45, 0x12, 0xab, 0x74, 0xf7, 0xb1, 0x77, 0x2e, 0x26, + 0xb7, 0xe1, 0x76, 0xa3, 0x5c, 0x27, 0xf8, 0x07, 0xfc, 0x19, 0xfe, 0x14, 0xcb, 0x94, 0xa9, 0xc4, + 0x6c, 0x1a, 0xcb, 0xfc, 0x04, 0x71, 0x4f, 0x25, 0x8a, 0x20, 0x76, 0xef, 0xcd, 0xce, 0xce, 0xcc, + 0x1b, 0xb2, 0x1e, 0x8b, 0x21, 0x57, 0x79, 0x1a, 0xb2, 0x71, 0x26, 0xb5, 0xa4, 0x1b, 0x23, 0x19, + 0xe4, 0xec, 0x0b, 0xbc, 0xd9, 0xaf, 0xef, 0x26, 0x42, 0x5f, 0x4d, 0x02, 0x16, 0xca, 0x91, 0x97, + 0xc8, 0x44, 0x7a, 0x96, 0x18, 0x4c, 0x62, 0xbb, 0xd9, 0xc5, 0x4e, 0x85, 0xc0, 0x37, 0xba, 0x96, + 0xa9, 0x50, 0x5a, 0x88, 0x44, 0x78, 0xb1, 0x9a, 0x68, 0x31, 0xf4, 0x74, 0x3e, 0xe6, 0xca, 0xbb, + 0x15, 0x19, 0x2f, 0xe8, 0x3b, 0x6d, 0x52, 0xed, 0xe4, 0x9a, 0xab, 0x33, 0xae, 0x94, 0x9f, 0x70, + 0x4a, 0xc9, 0x5a, 0xe4, 0x6b, 0xbf, 0x06, 0x0d, 0x68, 0x56, 0x7b, 0x76, 0xa6, 0x5b, 0xc4, 0x15, + 0x51, 0xcd, 0x6d, 0x40, 0xb3, 0xd2, 0x29, 0x99, 0xe7, 0x6d, 0xf7, 0xb4, 0xdb, 0x73, 0x45, 0xd4, + 0xba, 0x07, 0x52, 0x3e, 0x11, 0x43, 0xde, 0xcf, 0xd3, 0x90, 0xb6, 0x49, 0xb9, 0x2b, 0xe2, 0xf8, + 0x58, 0x8e, 0x73, 0xba, 0xc9, 0x0a, 0x3b, 0x66, 0xed, 0xd8, 0x85, 0x1f, 0x5e, 0x73, 0x5d, 0xff, + 0x15, 0x6d, 0xc2, 0x1e, 0xd0, 0x43, 0x52, 0xb9, 0xf4, 0xb3, 0xbe, 0xce, 0xb8, 0x3f, 0xfa, 0xef, + 0xe7, 0xd6, 0xe0, 0x23, 0x04, 0x4f, 0x23, 0x7a, 0xbe, 0x12, 0x02, 0xd9, 0xcf, 0x2a, 0xd9, 0xea, + 0xa5, 0xf5, 0x3f, 0xde, 0xdf, 0xb5, 0x3b, 0x47, 0xd3, 0x39, 0x3a, 0xb3, 0x39, 0x3a, 0xcb, 0x39, + 0xc2, 0x9d, 0x41, 0x78, 0x34, 0x08, 0x4f, 0x06, 0x61, 0x6a, 0x10, 0x5e, 0x0c, 0xc2, 0xab, 0x41, + 0x67, 0x69, 0x10, 0x1e, 0x16, 0xe8, 0x4c, 0x17, 0xe8, 0xcc, 0x16, 0xe8, 0x0c, 0xca, 0x9f, 0x9a, + 0x41, 0xc9, 0x96, 0x7c, 0xf0, 0x16, 0x00, 0x00, 0xff, 0xff, 0x04, 0xde, 0xb5, 0xc8, 0xe6, 0x01, + 0x00, 0x00, } func (this *BytesMessage) Equal(that interface{}) bool { @@ -342,110 +342,6 @@ var _FileSync_serviceDesc = grpc.ServiceDesc{ Metadata: "filesync.proto", } -// FSSendClient is the client API for FSSend service. -// -// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. -type FSSendClient interface { - DiffCopy(ctx context.Context, opts ...grpc.CallOption) (FSSend_DiffCopyClient, error) -} - -type fSSendClient struct { - cc *grpc.ClientConn -} - -func NewFSSendClient(cc *grpc.ClientConn) FSSendClient { - return &fSSendClient{cc} -} - -func (c *fSSendClient) DiffCopy(ctx context.Context, opts ...grpc.CallOption) (FSSend_DiffCopyClient, error) { - stream, err := c.cc.NewStream(ctx, &_FSSend_serviceDesc.Streams[0], "/moby.filesync.v1.FSSend/DiffCopy", opts...) - if err != nil { - return nil, err - } - x := &fSSendDiffCopyClient{stream} - return x, nil -} - -type FSSend_DiffCopyClient interface { - Send(*types.Packet) error - Recv() (*types.Packet, error) - grpc.ClientStream -} - -type fSSendDiffCopyClient struct { - grpc.ClientStream -} - -func (x *fSSendDiffCopyClient) Send(m *types.Packet) error { - return x.ClientStream.SendMsg(m) -} - -func (x *fSSendDiffCopyClient) Recv() (*types.Packet, error) { - m := new(types.Packet) - if err := x.ClientStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} - -// FSSendServer is the server API for FSSend service. -type FSSendServer interface { - DiffCopy(FSSend_DiffCopyServer) error -} - -// UnimplementedFSSendServer can be embedded to have forward compatible implementations. -type UnimplementedFSSendServer struct { -} - -func (*UnimplementedFSSendServer) DiffCopy(srv FSSend_DiffCopyServer) error { - return status.Errorf(codes.Unimplemented, "method DiffCopy not implemented") -} - -func RegisterFSSendServer(s *grpc.Server, srv FSSendServer) { - s.RegisterService(&_FSSend_serviceDesc, srv) -} - -func _FSSend_DiffCopy_Handler(srv interface{}, stream grpc.ServerStream) error { - return srv.(FSSendServer).DiffCopy(&fSSendDiffCopyServer{stream}) -} - -type FSSend_DiffCopyServer interface { - Send(*types.Packet) error - Recv() (*types.Packet, error) - grpc.ServerStream -} - -type fSSendDiffCopyServer struct { - grpc.ServerStream -} - -func (x *fSSendDiffCopyServer) Send(m *types.Packet) error { - return x.ServerStream.SendMsg(m) -} - -func (x *fSSendDiffCopyServer) Recv() (*types.Packet, error) { - m := new(types.Packet) - if err := x.ServerStream.RecvMsg(m); err != nil { - return nil, err - } - return m, nil -} - -var _FSSend_serviceDesc = grpc.ServiceDesc{ - ServiceName: "moby.filesync.v1.FSSend", - HandlerType: (*FSSendServer)(nil), - Methods: []grpc.MethodDesc{}, - Streams: []grpc.StreamDesc{ - { - StreamName: "DiffCopy", - Handler: _FSSend_DiffCopy_Handler, - ServerStreams: true, - ClientStreams: true, - }, - }, - Metadata: "filesync.proto", -} - // FileSendClient is the client API for FileSend service. // // For semantics around ctx use and closing/ending streaming RPCs, please refer to https://godoc.org/google.golang.org/grpc#ClientConn.NewStream. diff --git a/session/filesync/filesync.proto b/session/filesync/filesync.proto index 9c1fd3d1956c..ffea3c130d89 100644 --- a/session/filesync/filesync.proto +++ b/session/filesync/filesync.proto @@ -12,10 +12,6 @@ service FileSync{ rpc TarStream(stream fsutil.types.Packet) returns (stream fsutil.types.Packet); } -service FSSend{ - rpc DiffCopy(stream fsutil.types.Packet) returns (stream fsutil.types.Packet); -} - service FileSend{ rpc DiffCopy(stream BytesMessage) returns (stream BytesMessage); } From d1192f45538280b74088763cd0ae230b815bf8ec Mon Sep 17 00:00:00 2001 From: a-palchikov Date: Wed, 2 Aug 2023 12:18:06 +0200 Subject: [PATCH 7/7] Add a new MutableResult type to solver/result package that implements thread-safe Result. Address review comments: * Re-use the input variable in exporters to avoid accidental re-use of stale values Signed-off-by: a-palchikov --- exporter/containerimage/export.go | 14 +++---- exporter/exporter.go | 2 +- exporter/local/export.go | 4 +- exporter/oci/export.go | 10 ++--- exporter/tar/export.go | 4 +- frontend/dockerui/build.go | 36 +++--------------- frontend/gateway/client/client.go | 5 +++ solver/llbsolver/solver.go | 37 +++++++++---------- solver/result/result.go | 61 +++++++++++++++++++++++++++++++ 9 files changed, 104 insertions(+), 69 deletions(-) diff --git a/exporter/containerimage/export.go b/exporter/containerimage/export.go index a200709582c9..5c7dd3e7690d 100644 --- a/exporter/containerimage/export.go +++ b/exporter/containerimage/export.go @@ -187,11 +187,11 @@ 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, exporter.DescriptorReference, error) { +func (e *imageExporterInstance) Export(ctx context.Context, src exporter.Source, sessionID string) (map[string]string, exporter.DescriptorReference, error) { return e.ExportImage(ctx, src, exptypes.InlineCache{}, sessionID) } -func (e *imageExporterInstance) ExportImage(ctx context.Context, src *exporter.Source, inlineCache exptypes.InlineCache, sessionID string) (_ map[string]string, descref exporter.DescriptorReference, err error) { +func (e *imageExporterInstance) ExportImage(ctx context.Context, src exporter.Source, inlineCache exptypes.InlineCache, sessionID string) (_ map[string]string, descref exporter.DescriptorReference, err error) { meta := make(map[string][]byte) for k, v := range src.Metadata { meta[k] = v @@ -199,9 +199,7 @@ func (e *imageExporterInstance) ExportImage(ctx context.Context, src *exporter.S for k, v := range e.meta { meta[k] = v } - inp := *src - inp.Metadata = meta - src = &inp + src.Metadata = meta opts := e.opts as, _, err := ParseAnnotations(meta) @@ -221,7 +219,7 @@ func (e *imageExporterInstance) ExportImage(ctx context.Context, src *exporter.S } }() - desc, err := e.opt.ImageWriter.Commit(ctx, src, sessionID, &opts) + desc, err := e.opt.ImageWriter.Commit(ctx, &src, sessionID, &opts) if err != nil { return nil, nil, err } @@ -283,7 +281,7 @@ func (e *imageExporterInstance) ExportImage(ctx context.Context, src *exporter.S tagDone(nil) if e.unpack { - if err := e.unpackImage(ctx, img, src, session.NewGroup(sessionID)); err != nil { + if err := e.unpackImage(ctx, img, &src, session.NewGroup(sessionID)); err != nil { return nil, nil, err } } @@ -319,7 +317,7 @@ func (e *imageExporterInstance) ExportImage(ctx context.Context, src *exporter.S } } if e.push { - err := e.pushImage(ctx, src, sessionID, targetName, desc.Digest) + err := e.pushImage(ctx, &src, sessionID, targetName, desc.Digest) if err != nil { return nil, nil, errors.Wrapf(err, "failed to push %v", targetName) } diff --git a/exporter/exporter.go b/exporter/exporter.go index 0e7d8d14f280..fb9ddd36d36f 100644 --- a/exporter/exporter.go +++ b/exporter/exporter.go @@ -20,7 +20,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, sessionID string) (map[string]string, DescriptorReference, error) } type DescriptorReference interface { diff --git a/exporter/local/export.go b/exporter/local/export.go index 771b7aaf2252..4f5788022691 100644 --- a/exporter/local/export.go +++ b/exporter/local/export.go @@ -60,12 +60,12 @@ 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, sessionID string) (map[string]string, exporter.DescriptorReference, error) { timeoutCtx, cancel := context.WithTimeout(ctx, 5*time.Second) defer cancel() if e.opts.Epoch == nil { - if tm, ok, err := epoch.ParseSource(inp); err != nil { + if tm, ok, err := epoch.ParseSource(&inp); err != nil { return nil, nil, err } else if ok { e.opts.Epoch = tm diff --git a/exporter/oci/export.go b/exporter/oci/export.go index 3e67a5d60042..afe55ba3eed5 100644 --- a/exporter/oci/export.go +++ b/exporter/oci/export.go @@ -113,11 +113,11 @@ 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, exporter.DescriptorReference, error) { +func (e *imageExporterInstance) Export(ctx context.Context, src exporter.Source, sessionID string) (map[string]string, exporter.DescriptorReference, error) { return e.ExportImage(ctx, src, exptypes.InlineCache{}, sessionID) } -func (e *imageExporterInstance) ExportImage(ctx context.Context, src *exporter.Source, inlineCache exptypes.InlineCache, sessionID string) (_ map[string]string, descref exporter.DescriptorReference, err error) { +func (e *imageExporterInstance) ExportImage(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") } @@ -129,9 +129,7 @@ func (e *imageExporterInstance) ExportImage(ctx context.Context, src *exporter.S for k, v := range e.meta { meta[k] = v } - inp := *src - inp.Metadata = meta - src = &inp + src.Metadata = meta opts := e.opts as, _, err := containerimage.ParseAnnotations(meta) @@ -151,7 +149,7 @@ func (e *imageExporterInstance) ExportImage(ctx context.Context, src *exporter.S } }() - desc, err := e.opt.ImageWriter.Commit(ctx, src, sessionID, &opts) + desc, err := e.opt.ImageWriter.Commit(ctx, &src, sessionID, &opts) if err != nil { return nil, nil, err } diff --git a/exporter/tar/export.go b/exporter/tar/export.go index 53fc5b7c87a9..57a4b4e3434e 100644 --- a/exporter/tar/export.go +++ b/exporter/tar/export.go @@ -58,7 +58,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, sessionID string) (map[string]string, exporter.DescriptorReference, error) { var defers []func() error defer func() { @@ -68,7 +68,7 @@ func (e *localExporterInstance) Export(ctx context.Context, inp *exporter.Source }() if e.opts.Epoch == nil { - if tm, ok, err := epoch.ParseSource(inp); err != nil { + if tm, ok, err := epoch.ParseSource(&inp); err != nil { return nil, nil, err } else if ok { e.opts.Epoch = tm diff --git a/frontend/dockerui/build.go b/frontend/dockerui/build.go index 11c3d9ab89c1..d894b5595001 100644 --- a/frontend/dockerui/build.go +++ b/frontend/dockerui/build.go @@ -4,7 +4,6 @@ import ( "context" "encoding/json" "fmt" - "sync" "github.com/containerd/containerd/platforms" "github.com/moby/buildkit/exporter/containerimage/exptypes" @@ -18,7 +17,7 @@ import ( type BuildFunc func(ctx context.Context, platform *ocispecs.Platform, idx int) (client.Reference, *image.Image, error) func (bc *Client) Build(ctx context.Context, fn BuildFunc) (*ResultBuilder, error) { - res := &mutableResult{res: client.NewResult()} + res := client.NewMutableResult() targets := make([]*ocispecs.Platform, 0, len(bc.TargetPlatforms)) for _, p := range bc.TargetPlatforms { @@ -66,11 +65,11 @@ func (bc *Client) Build(ctx context.Context, fn BuildFunc) (*ResultBuilder, erro k := platforms.Format(p) if bc.MultiPlatformRequested { - res.addRef(k, ref) - res.addMeta(fmt.Sprintf("%s/%s", exptypes.ExporterImageConfigKey, k), config) + res.AddRef(k, ref) + res.AddMeta(fmt.Sprintf("%s/%s", exptypes.ExporterImageConfigKey, k), config) } else { - res.setRef(ref) - res.addMeta(exptypes.ExporterImageConfigKey, config) + res.SetRef(ref) + res.AddMeta(exptypes.ExporterImageConfigKey, config) } expPlatforms.Platforms[i] = exptypes.Platform{ ID: k, @@ -83,7 +82,7 @@ func (bc *Client) Build(ctx context.Context, fn BuildFunc) (*ResultBuilder, erro return nil, err } return &ResultBuilder{ - Result: res.res, + Result: res.Result(), expPlatforms: expPlatforms, }, nil } @@ -113,26 +112,3 @@ func (rb *ResultBuilder) EachPlatform(ctx context.Context, fn func(ctx context.C } return eg.Wait() } - -func (r *mutableResult) addMeta(k string, v []byte) { - r.mu.Lock() - r.res.AddMeta(k, v) - r.mu.Unlock() -} - -func (r *mutableResult) addRef(k string, ref client.Reference) { - r.mu.Lock() - r.res.AddRef(k, ref) - r.mu.Unlock() -} - -func (r *mutableResult) setRef(ref client.Reference) { - r.mu.Lock() - r.res.SetRef(ref) - r.mu.Unlock() -} - -type mutableResult struct { - mu sync.Mutex - res *client.Result -} diff --git a/frontend/gateway/client/client.go b/frontend/gateway/client/client.go index 23585de9078e..2565e98e6cd2 100644 --- a/frontend/gateway/client/client.go +++ b/frontend/gateway/client/client.go @@ -16,6 +16,7 @@ import ( ) type Result = result.Result[Reference] +type MutableResult = result.MutableResult[Reference] type Attestation = result.Attestation[Reference] @@ -25,6 +26,10 @@ func NewResult() *Result { return &Result{} } +func NewMutableResult() *MutableResult { + return &MutableResult{} +} + type Client interface { Solve(ctx context.Context, req SolveRequest) (*Result, error) ResolveImageConfig(ctx context.Context, ref string, opt llb.ResolveImageConfigOpt) (string, digest.Digest, []byte, error) diff --git a/solver/llbsolver/solver.go b/solver/llbsolver/solver.go index 06dc3fd59185..adbecca73b87 100644 --- a/solver/llbsolver/solver.go +++ b/solver/llbsolver/solver.go @@ -552,12 +552,12 @@ 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.runExporters(ctx, exp.Exporters, inlineCacheExporter, j, cached, inp) + exporterResponse, descref, err = s.runExporters(ctx, exp.Exporters, inlineCacheExporter, j, *cached, *inp) if err != nil { return nil, err } - cacheExporterResponse, err := runCacheExporters(ctx, cacheExporters, j, cached, inp) + cacheExporterResponse, err := runCacheExporters(ctx, cacheExporters, j, *cached, *inp) if err != nil { return nil, err } @@ -582,7 +582,7 @@ func (s *Solver) Solve(ctx context.Context, id string, sessionID string, req fro }, nil } -func runCacheExporters(ctx context.Context, exporters []RemoteCacheExporter, j *solver.Job, cached *result.Result[solver.CachedResult], inp *result.Result[cache.ImmutableRef]) (map[string]string, error) { +func runCacheExporters(ctx context.Context, exporters []RemoteCacheExporter, j *solver.Job, cached result.Result[solver.CachedResult], inp result.Result[cache.ImmutableRef]) (map[string]string, error) { eg, ctx := errgroup.WithContext(ctx) g := session.NewGroup(j.SessionID) var cacheExporterResponse map[string]string @@ -593,7 +593,7 @@ func runCacheExporters(ctx context.Context, exporters []RemoteCacheExporter, j * 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 { + if err := result.EachRef(&cached, &inp, func(res solver.CachedResult, ref cache.ImmutableRef) error { ctx = withDescHandlerCacheOpts(ctx, ref) // Configure compression @@ -634,7 +634,7 @@ func runCacheExporters(ctx context.Context, exporters []RemoteCacheExporter, j * return cacheExporterResponse, nil } -func runInlineCacheExporter(ctx context.Context, e exporter.ExporterInstance, inlineExporter inlineCacheExporter, j *solver.Job, cached *result.Result[solver.CachedResult]) (result *exptypes.InlineCache, err error) { +func runInlineCacheExporter(ctx context.Context, e exporter.ExporterInstance, inlineExporter inlineCacheExporter, j *solver.Job, cached result.Result[solver.CachedResult]) (result *exptypes.InlineCache, err error) { result = &exptypes.InlineCache{ Platforms: make(map[string][]byte), } @@ -642,32 +642,29 @@ func runInlineCacheExporter(ctx context.Context, e exporter.ExporterInstance, in return result, nil } 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 { + err := cached.EachPlatformRef(func(platformID string, res solver.CachedResult) error { dtic, err := inlineCache(ctx, inlineExporter, res, e.Config().Compression(), session.NewGroup(j.SessionID)) if err != nil { return err } - if dtic != nil { - result.Cache = dtic - } - } - for k, res := range cached.Refs { - dtic, err := inlineCache(ctx, inlineExporter, res, e.Config().Compression(), session.NewGroup(j.SessionID)) - if err != nil { - return err + if dtic == nil { + return nil } - if dtic != nil { - result.Platforms[k] = dtic + if platformID == "" { + result.Cache = dtic + } else { + result.Platforms[platformID] = dtic } - } - return nil + return nil + }) + return err }); err != nil { return nil, err } return result, nil } -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, descref 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, descref exporter.DescriptorReference, err error) { eg, ctx := errgroup.WithContext(ctx) sessionID := job.SessionID resps := make([]map[string]string, len(exporters)) @@ -863,7 +860,7 @@ func getProvenance(ref solver.ResultProxy, br *provenanceBridge, id string, reqs type imageExporterInstance interface { exporter.ExporterInstance - ExportImage(ctx context.Context, src *exporter.Source, cache exptypes.InlineCache, sessionID string) (map[string]string, exporter.DescriptorReference, error) + ExportImage(ctx context.Context, src exporter.Source, cache exptypes.InlineCache, sessionID string) (map[string]string, exporter.DescriptorReference, error) } func asImageExporter(e exporter.ExporterInstance) (imageExporterInstance, bool) { diff --git a/solver/result/result.go b/solver/result/result.go index d77febc0c3a6..9d77647a91bc 100644 --- a/solver/result/result.go +++ b/solver/result/result.go @@ -1,6 +1,8 @@ package result import ( + "sync" + "github.com/pkg/errors" ) @@ -85,6 +87,33 @@ func (r *Result[T]) EachRef(fn func(T) error) (err error) { return err } +// EachPlatformRef iterates through all underlying refs and invokes +// the given handler with the platform ID corresponding to the ref. +// For the single ref case, the platform ID is an empty string. +func (r *Result[T]) EachPlatformRef(fn func(string, T) error) (err error) { + var zero T + if r.Ref != zero { + err = fn("", r.Ref) + } + for p, r := range r.Refs { + if r != zero { + if err1 := fn(p, r); err1 != nil && err == nil { + err = err1 + } + } + } + for p, as := range r.Attestations { + for _, a := range as { + if a.Ref != zero { + if err1 := fn(p, a.Ref); err1 != nil && err == nil { + err = err1 + } + } + } + } + return err +} + // EachRef iterates over references in both a and b. // a and b are assumed to be of the same size and map their references // to the same set of keys @@ -170,3 +199,35 @@ func ConvertResult[U comparable, V comparable](r *Result[U], fn func(U) (V, erro return r2, nil } + +// Result returns the underlying Result value +func (r *MutableResult[T]) Result() *Result[T] { + return &r.res +} + +// AddMeta adds the metadata specified with the given key/value pair +func (r *MutableResult[T]) AddMeta(k string, v []byte) { + r.mu.Lock() + r.res.AddMeta(k, v) + r.mu.Unlock() +} + +// AddRef adds the specified reference for the platform given with k +func (r *MutableResult[T]) AddRef(k string, ref T) { + r.mu.Lock() + r.res.AddRef(k, ref) + r.mu.Unlock() +} + +// SetRef sets the specified reference as the single reference +func (r *MutableResult[T]) SetRef(ref T) { + r.mu.Lock() + r.res.SetRef(ref) + r.mu.Unlock() +} + +// MutableResult is a thread-safe version of Result +type MutableResult[T comparable] struct { + mu sync.Mutex + res Result[T] +}