@@ -47,50 +47,63 @@ type SolveOpt struct {
4747 CacheImports []CacheOptionsEntry
4848 Session []session.Attachable
4949 AllowedEntitlements []entitlements.Entitlement
50- // When the session is custom-initialized, ParseExporterOpts need to be used to correctly
51- // set up the session for export.
50+ // When the session is custom-initialized, Init can be used to
51+ // set up the session for export automatically .
5252 SharedSession * session.Session // TODO: refactor to better session syncing
5353 SessionPreInitialized bool // TODO: refactor to better session syncing
5454 Internal bool
5555 SourcePolicy * spb.Policy
5656 Ref string
5757
58- // internal exporter state
58+ // internal solver state
5959 s solverState
6060}
6161
6262type solverState struct {
63- // storesToUpdate maps exporter ID -> oci store
64- storesToUpdate map [string ]ociStore
65- cacheOpt * cacheOptions
63+ exporterOpt * exporterOptions
64+ cacheOpt * cacheOptions
6665 // Only one of runGateway or def can be set.
6766 // runGateway optionally defines the gateway callback
6867 runGateway runGatewayCB
6968 // def optionally defines the LLB definition for the client
7069 def * llb.Definition
7170}
7271
72+ type exporterOptions struct {
73+ // storesToUpdate maps exporter ID -> oci store
74+ storesToUpdate map [string ]ociStore
75+ }
76+
7377type cacheOptions struct {
7478 options controlapi.CacheOptions
7579 contentStores map [string ]content.Store // key: ID of content store ("local:" + csDir)
76- storesToUpdate map [string ]string // key: path to content store, value: tag
80+ storesToUpdate map [string ]ociStore // key: exporter ID
7781 frontendAttrs map [string ]string
7882}
7983
8084type ociStore struct {
8185 path string
86+ tag string
8287}
8388
8489type ExportEntry struct {
8590 Type string
8691 Attrs map [string ]string
8792 Output filesync.FileOutputFunc // for ExporterOCI and ExporterDocker
8893 OutputDir string // for ExporterLocal
94+
95+ // id identifies the exporter in the configuration.
96+ // Will be assigned automatically and should not be set by the user.
97+ id string
8998}
9099
91100type CacheOptionsEntry struct {
92101 Type string
93102 Attrs map [string ]string
103+
104+ // id identifies the exporter in the configuration.
105+ // Will be assigned automatically and should not be set by the user.
106+ id string
94107}
95108
96109// Solve calls Solve on the controller.
@@ -115,9 +128,32 @@ func (c *Client) Solve(ctx context.Context, def *llb.Definition, opt SolveOpt, s
115128
116129type runGatewayCB func (ref string , s * session.Session , opts map [string ]string ) error
117130
118- // ParseExporterOpts configures the specified session with the underlying exporter configuration.
131+ // Init initializes the SolveOpt.
132+ // It parses and initializes the cache exports/imports and output exporters.
133+ func (opt * SolveOpt ) Init (ctx context.Context , s * session.Session ) error {
134+ opt .initExporterIDs ()
135+ if err := opt .parseCacheOptions (ctx ); err != nil {
136+ return err
137+ }
138+ return opt .parseExporterOptions (s )
139+ }
140+
141+ func (opt * SolveOpt ) initExporterIDs () {
142+ for i := range opt .Exports {
143+ opt .Exports [i ].id = strconv .Itoa (i )
144+ }
145+ for i := range opt .CacheExports {
146+ opt .CacheExports [i ].id = strconv .Itoa (i )
147+ }
148+ }
149+
150+ // parseExporterOptions configures the specified session with the underlying exporter configuration.
119151// It needs to be invoked *after* ParseCacheOpts
120- func ParseExporterOpts (opt * SolveOpt , s * session.Session ) error {
152+ func (opt * SolveOpt ) parseExporterOptions (s * session.Session ) error {
153+ if opt .s .exporterOpt != nil {
154+ return nil
155+ }
156+
121157 mounts , err := prepareMounts (opt )
122158 if err != nil {
123159 return err
@@ -145,8 +181,9 @@ func ParseExporterOpts(opt *SolveOpt, s *session.Session) error {
145181 contentStores [key2 ] = store
146182 }
147183
184+ opt .s .exporterOpt = & exporterOptions {}
148185 var syncTargets []filesync.FSSyncTarget
149- for exID , ex := range opt .Exports {
186+ for _ , ex := range opt .Exports {
150187 var supportFile bool
151188 var supportDir bool
152189 switch ex .Type {
@@ -171,7 +208,7 @@ func ParseExporterOpts(opt *SolveOpt, s *session.Session) error {
171208 if ex .Output == nil {
172209 return errors .Errorf ("output file writer is required for %s exporter" , ex .Type )
173210 }
174- syncTargets = append (syncTargets , filesync .WithFSSync (exID , ex .Output ))
211+ syncTargets = append (syncTargets , filesync .WithFSSync (ex . id , ex .Output ))
175212 }
176213 if supportDir {
177214 if ex .OutputDir == "" {
@@ -187,12 +224,12 @@ func ParseExporterOpts(opt *SolveOpt, s *session.Session) error {
187224 return err
188225 }
189226 contentStores ["export" ] = cs
190- if opt .s .storesToUpdate == nil {
191- opt .s .storesToUpdate = make (map [string ]ociStore )
227+ if opt .s .exporterOpt . storesToUpdate == nil {
228+ opt .s .exporterOpt . storesToUpdate = make (map [string ]ociStore )
192229 }
193- opt .s .storesToUpdate [strconv . Itoa ( exID ) ] = ociStore {path : ex .OutputDir }
230+ opt .s .exporterOpt . storesToUpdate [ex . id ] = ociStore {path : ex .OutputDir }
194231 default :
195- syncTargets = append (syncTargets , filesync .WithFSSyncDir (exID , ex .OutputDir ))
232+ syncTargets = append (syncTargets , filesync .WithFSSyncDir (ex . id , ex .OutputDir ))
196233 }
197234 }
198235 }
@@ -236,13 +273,15 @@ func (c *Client) solve(ctx context.Context, opt SolveOpt, statusChan chan *Solve
236273 }
237274 }
238275
239- err := ParseCacheOptions (ctx , & opt )
276+ opt .initExporterIDs ()
277+
278+ err := opt .parseCacheOptions (ctx )
240279 if err != nil {
241280 return nil , err
242281 }
243282
244283 if ! opt .SessionPreInitialized {
245- if err := ParseExporterOpts ( & opt , s ); err != nil {
284+ if err := opt . parseExporterOptions ( s ); err != nil {
246285 return nil , err
247286 }
248287
@@ -299,8 +338,7 @@ func (c *Client) solve(ctx context.Context, opt SolveOpt, statusChan chan *Solve
299338 exports = append (exports , & controlapi.Exporter {
300339 Type : exp .Type ,
301340 Attrs : exp .Attrs ,
302- // Keep this in sync with SetupExporters id assignment
303- ID : strconv .Itoa (i ),
341+ ID : exp .id ,
304342 })
305343 }
306344
@@ -330,6 +368,7 @@ func (c *Client) solve(ctx context.Context, opt SolveOpt, statusChan chan *Solve
330368 for _ , resp := range resp .ExporterResponses {
331369 res .ExporterResponses = append (res .ExporterResponses , ExporterResponse {
332370 ID : resp .Metadata .ID ,
371+ Type : resp .Metadata .Type ,
333372 Data : resp .Data ,
334373 })
335374 }
@@ -389,26 +428,27 @@ func (c *Client) solve(ctx context.Context, opt SolveOpt, statusChan chan *Solve
389428 if err := eg .Wait (); err != nil {
390429 return nil , err
391430 }
392- // Update index.json of exported cache content store
393- // FIXME(AkihiroSuda): dedupe const definition of cache/remotecache.ExporterResponseManifestDesc = "cache.manifest"
394- if manifestDescJSON := res . ExporterResponse [ "cache.manifest" ]; manifestDescJSON != "" {
395- var manifestDesc ocispecs. Descriptor
396- if err = json . Unmarshal ([] byte ( manifestDescJSON ), & manifestDesc ); err != nil {
431+
432+ for id , store := range opt . s . cacheOpt . storesToUpdate {
433+ // Update index.json of exported cache content store
434+ manifestDesc , err := getCacheManifestDescriptor ( id , res )
435+ if err != nil {
397436 return nil , err
398437 }
399- for storePath , tag := range opt .s .cacheOpt .storesToUpdate {
400- idx := ociindex .NewStoreIndex (storePath )
401- if err := idx .Put (manifestDesc , ociindex .Tag (tag )); err != nil {
402- return nil , err
403- }
438+ if manifestDesc == nil {
439+ continue
440+ }
441+ idx := ociindex .NewStoreIndex (store .path )
442+ if err := idx .Put (* manifestDesc , ociindex .Tag (store .tag )); err != nil {
443+ return nil , err
404444 }
405445 }
406446
407- if len (opt .s .storesToUpdate ) == 0 {
447+ if len (opt .s .exporterOpt . storesToUpdate ) == 0 {
408448 return res , nil
409449 }
410- for id , store := range opt .s .storesToUpdate {
411- manifestDesc , err := getManifestDescriptor (id , res )
450+ for id , store := range opt .s .exporterOpt . storesToUpdate {
451+ manifestDesc , err := getImageManifestDescriptor (id , res )
412452 if err != nil {
413453 return nil , err
414454 }
@@ -433,25 +473,43 @@ func (c *Client) solve(ctx context.Context, opt SolveOpt, statusChan chan *Solve
433473 return res , nil
434474}
435475
436- func getManifestDescriptor (exporterID string , resp * SolveResponse ) (* ocispecs.Descriptor , error ) {
476+ func getCacheManifestDescriptor (exporterID string , resp * SolveResponse ) (* ocispecs.Descriptor , error ) {
477+ const exporterResponseManifestDesc = "cache.manifest"
478+ if resp := resp .cacheExporter (exporterID ); resp != nil {
479+ // FIXME(AkihiroSuda): dedupe const definition of cache/remotecache.ExporterResponseManifestDesc = "cache.manifest"
480+ if manifestDescDt := resp .Data [exporterResponseManifestDesc ]; manifestDescDt != "" {
481+ return unmarshalManifestDescriptor (manifestDescDt )
482+ }
483+ }
484+ if manifestDescDt := resp .ExporterResponse [exporterResponseManifestDesc ]; manifestDescDt != "" {
485+ return unmarshalManifestDescriptor (manifestDescDt )
486+ }
487+ return nil , nil
488+ }
489+
490+ func getImageManifestDescriptor (exporterID string , resp * SolveResponse ) (* ocispecs.Descriptor , error ) {
437491 if resp := resp .exporter (exporterID ); resp != nil {
438492 if manifestDescDt := resp .Data [exptypes .ExporterImageDescriptorKey ]; manifestDescDt != "" {
439- return unmarshalManifestDescriptor (manifestDescDt )
493+ return unmarshalEncodedManifestDescriptor (manifestDescDt )
440494 }
441495 }
442496 if manifestDescDt := resp .ExporterResponse [exptypes .ExporterImageDescriptorKey ]; manifestDescDt != "" {
443- return unmarshalManifestDescriptor (manifestDescDt )
497+ return unmarshalEncodedManifestDescriptor (manifestDescDt )
444498 }
445499 return nil , nil
446500}
447501
448- func unmarshalManifestDescriptor ( manifestDesc string ) (* ocispecs.Descriptor , error ) {
449- manifestDescDt , err := base64 .StdEncoding .DecodeString (manifestDesc )
502+ func unmarshalEncodedManifestDescriptor ( base64Payload string ) (* ocispecs.Descriptor , error ) {
503+ manifestDescDt , err := base64 .StdEncoding .DecodeString (base64Payload )
450504 if err != nil {
451505 return nil , err
452506 }
507+ return unmarshalManifestDescriptor (string (manifestDescDt ))
508+ }
509+
510+ func unmarshalManifestDescriptor (manifestDescJSON string ) (* ocispecs.Descriptor , error ) {
453511 var desc ocispecs.Descriptor
454- if err = json .Unmarshal ([]byte (manifestDescDt ), & desc ); err != nil {
512+ if err : = json .Unmarshal ([]byte (manifestDescJSON ), & desc ); err != nil {
455513 return nil , err
456514 }
457515 return & desc , nil
@@ -502,13 +560,16 @@ func prepareSyncedFiles(def *llb.Definition, localMounts map[string]fsutil.FS) (
502560 return result , nil
503561}
504562
505- func ParseCacheOptions (ctx context.Context , opt * SolveOpt ) error {
563+ func (opt * SolveOpt ) parseCacheOptions (ctx context.Context ) error {
564+ if opt .s .cacheOpt != nil {
565+ return nil
566+ }
506567 var (
507568 cacheExports []* controlapi.CacheOptionsEntry
508569 cacheImports []* controlapi.CacheOptionsEntry
509570 )
510571 contentStores := make (map [string ]content.Store )
511- storesToUpdate := make (map [string ]string )
572+ storesToUpdate := make (map [string ]ociStore )
512573 frontendAttrs := make (map [string ]string )
513574 for _ , ex := range opt .CacheExports {
514575 if ex .Type == "local" {
@@ -529,8 +590,7 @@ func ParseCacheOptions(ctx context.Context, opt *SolveOpt) error {
529590 if t , ok := ex .Attrs ["tag" ]; ok {
530591 tag = t
531592 }
532- // TODO(AkihiroSuda): support custom index JSON path and tag
533- storesToUpdate [csDir ] = tag
593+ storesToUpdate [ex .id ] = ociStore {path : csDir , tag : tag }
534594 }
535595 if ex .Type == "registry" {
536596 regRef := ex .Attrs ["ref" ]
@@ -541,6 +601,7 @@ func ParseCacheOptions(ctx context.Context, opt *SolveOpt) error {
541601 cacheExports = append (cacheExports , & controlapi.CacheOptionsEntry {
542602 Type : ex .Type ,
543603 Attrs : ex .Attrs ,
604+ ID : ex .id ,
544605 })
545606 }
546607 for _ , im := range opt .CacheImports {
0 commit comments