@@ -47,50 +47,63 @@ type SolveOpt struct {
47
47
CacheImports []CacheOptionsEntry
48
48
Session []session.Attachable
49
49
AllowedEntitlements []string
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 .
52
52
SharedSession * session.Session // TODO: refactor to better session syncing
53
53
SessionPreInitialized bool // TODO: refactor to better session syncing
54
54
Internal bool
55
55
SourcePolicy * spb.Policy
56
56
Ref string
57
57
58
- // internal exporter state
58
+ // internal solver state
59
59
s solverState
60
60
}
61
61
62
62
type solverState struct {
63
- // storesToUpdate maps exporter ID -> oci store
64
- storesToUpdate map [string ]ociStore
65
- cacheOpt * cacheOptions
63
+ exporterOpt * exporterOptions
64
+ cacheOpt * cacheOptions
66
65
// Only one of runGateway or def can be set.
67
66
// runGateway optionally defines the gateway callback
68
67
runGateway runGatewayCB
69
68
// def optionally defines the LLB definition for the client
70
69
def * llb.Definition
71
70
}
72
71
72
+ type exporterOptions struct {
73
+ // storesToUpdate maps exporter ID -> oci store
74
+ storesToUpdate map [string ]ociStore
75
+ }
76
+
73
77
type cacheOptions struct {
74
78
options controlapi.CacheOptions
75
79
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
77
81
frontendAttrs map [string ]string
78
82
}
79
83
80
84
type ociStore struct {
81
85
path string
86
+ tag string
82
87
}
83
88
84
89
type ExportEntry struct {
85
90
Type string
86
91
Attrs map [string ]string
87
92
Output filesync.FileOutputFunc // for ExporterOCI and ExporterDocker
88
93
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
89
98
}
90
99
91
100
type CacheOptionsEntry struct {
92
101
Type string
93
102
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
94
107
}
95
108
96
109
// Solve calls Solve on the controller.
@@ -115,9 +128,32 @@ func (c *Client) Solve(ctx context.Context, def *llb.Definition, opt SolveOpt, s
115
128
116
129
type runGatewayCB func (ref string , s * session.Session , opts map [string ]string ) error
117
130
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.
119
151
// 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
+
121
157
mounts , err := prepareMounts (opt )
122
158
if err != nil {
123
159
return err
@@ -145,8 +181,9 @@ func ParseExporterOpts(opt *SolveOpt, s *session.Session) error {
145
181
contentStores [key2 ] = store
146
182
}
147
183
184
+ opt .s .exporterOpt = & exporterOptions {}
148
185
var syncTargets []filesync.FSSyncTarget
149
- for exID , ex := range opt .Exports {
186
+ for _ , ex := range opt .Exports {
150
187
var supportFile bool
151
188
var supportDir bool
152
189
switch ex .Type {
@@ -171,7 +208,7 @@ func ParseExporterOpts(opt *SolveOpt, s *session.Session) error {
171
208
if ex .Output == nil {
172
209
return errors .Errorf ("output file writer is required for %s exporter" , ex .Type )
173
210
}
174
- syncTargets = append (syncTargets , filesync .WithFSSync (exID , ex .Output ))
211
+ syncTargets = append (syncTargets , filesync .WithFSSync (ex . id , ex .Output ))
175
212
}
176
213
if supportDir {
177
214
if ex .OutputDir == "" {
@@ -187,12 +224,12 @@ func ParseExporterOpts(opt *SolveOpt, s *session.Session) error {
187
224
return err
188
225
}
189
226
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 )
192
229
}
193
- opt .s .storesToUpdate [strconv . Itoa ( exID ) ] = ociStore {path : ex .OutputDir }
230
+ opt .s .exporterOpt . storesToUpdate [ex . id ] = ociStore {path : ex .OutputDir }
194
231
default :
195
- syncTargets = append (syncTargets , filesync .WithFSSyncDir (exID , ex .OutputDir ))
232
+ syncTargets = append (syncTargets , filesync .WithFSSyncDir (ex . id , ex .OutputDir ))
196
233
}
197
234
}
198
235
}
@@ -236,13 +273,15 @@ func (c *Client) solve(ctx context.Context, opt SolveOpt, statusChan chan *Solve
236
273
}
237
274
}
238
275
239
- err := ParseCacheOptions (ctx , & opt )
276
+ opt .initExporterIDs ()
277
+
278
+ err := opt .parseCacheOptions (ctx )
240
279
if err != nil {
241
280
return nil , err
242
281
}
243
282
244
283
if ! opt .SessionPreInitialized {
245
- if err := ParseExporterOpts ( & opt , s ); err != nil {
284
+ if err := opt . parseExporterOptions ( s ); err != nil {
246
285
return nil , err
247
286
}
248
287
@@ -299,8 +338,7 @@ func (c *Client) solve(ctx context.Context, opt SolveOpt, statusChan chan *Solve
299
338
exports = append (exports , & controlapi.Exporter {
300
339
Type : exp .Type ,
301
340
Attrs : exp .Attrs ,
302
- // Keep this in sync with SetupExporters id assignment
303
- ID : strconv .Itoa (i ),
341
+ ID : exp .id ,
304
342
})
305
343
}
306
344
@@ -330,6 +368,7 @@ func (c *Client) solve(ctx context.Context, opt SolveOpt, statusChan chan *Solve
330
368
for _ , resp := range resp .ExporterResponses {
331
369
res .ExporterResponses = append (res .ExporterResponses , ExporterResponse {
332
370
ID : resp .Metadata .ID ,
371
+ Type : resp .Metadata .Type ,
333
372
Data : resp .Data ,
334
373
})
335
374
}
@@ -389,26 +428,27 @@ func (c *Client) solve(ctx context.Context, opt SolveOpt, statusChan chan *Solve
389
428
if err := eg .Wait (); err != nil {
390
429
return nil , err
391
430
}
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 {
397
436
return nil , err
398
437
}
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
404
444
}
405
445
}
406
446
407
- if len (opt .s .storesToUpdate ) == 0 {
447
+ if len (opt .s .exporterOpt . storesToUpdate ) == 0 {
408
448
return res , nil
409
449
}
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 )
412
452
if err != nil {
413
453
return nil , err
414
454
}
@@ -433,25 +473,43 @@ func (c *Client) solve(ctx context.Context, opt SolveOpt, statusChan chan *Solve
433
473
return res , nil
434
474
}
435
475
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 ) {
437
491
if resp := resp .exporter (exporterID ); resp != nil {
438
492
if manifestDescDt := resp .Data [exptypes .ExporterImageDescriptorKey ]; manifestDescDt != "" {
439
- return unmarshalManifestDescriptor (manifestDescDt )
493
+ return unmarshalEncodedManifestDescriptor (manifestDescDt )
440
494
}
441
495
}
442
496
if manifestDescDt := resp .ExporterResponse [exptypes .ExporterImageDescriptorKey ]; manifestDescDt != "" {
443
- return unmarshalManifestDescriptor (manifestDescDt )
497
+ return unmarshalEncodedManifestDescriptor (manifestDescDt )
444
498
}
445
499
return nil , nil
446
500
}
447
501
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 )
450
504
if err != nil {
451
505
return nil , err
452
506
}
507
+ return unmarshalManifestDescriptor (string (manifestDescDt ))
508
+ }
509
+
510
+ func unmarshalManifestDescriptor (manifestDescJSON string ) (* ocispecs.Descriptor , error ) {
453
511
var desc ocispecs.Descriptor
454
- if err = json .Unmarshal ([]byte (manifestDescDt ), & desc ); err != nil {
512
+ if err : = json .Unmarshal ([]byte (manifestDescJSON ), & desc ); err != nil {
455
513
return nil , err
456
514
}
457
515
return & desc , nil
@@ -502,13 +560,16 @@ func prepareSyncedFiles(def *llb.Definition, localMounts map[string]fsutil.FS) (
502
560
return result , nil
503
561
}
504
562
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
+ }
506
567
var (
507
568
cacheExports []* controlapi.CacheOptionsEntry
508
569
cacheImports []* controlapi.CacheOptionsEntry
509
570
)
510
571
contentStores := make (map [string ]content.Store )
511
- storesToUpdate := make (map [string ]string )
572
+ storesToUpdate := make (map [string ]ociStore )
512
573
frontendAttrs := make (map [string ]string )
513
574
for _ , ex := range opt .CacheExports {
514
575
if ex .Type == "local" {
@@ -529,8 +590,7 @@ func ParseCacheOptions(ctx context.Context, opt *SolveOpt) error {
529
590
if t , ok := ex .Attrs ["tag" ]; ok {
530
591
tag = t
531
592
}
532
- // TODO(AkihiroSuda): support custom index JSON path and tag
533
- storesToUpdate [csDir ] = tag
593
+ storesToUpdate [ex .id ] = ociStore {path : csDir , tag : tag }
534
594
}
535
595
if ex .Type == "registry" {
536
596
regRef := ex .Attrs ["ref" ]
@@ -541,6 +601,7 @@ func ParseCacheOptions(ctx context.Context, opt *SolveOpt) error {
541
601
cacheExports = append (cacheExports , & controlapi.CacheOptionsEntry {
542
602
Type : ex .Type ,
543
603
Attrs : ex .Attrs ,
604
+ ID : ex .id ,
544
605
})
545
606
}
546
607
for _ , im := range opt .CacheImports {
0 commit comments