@@ -12,6 +12,7 @@ import (
12
12
"sort"
13
13
"strings"
14
14
"sync"
15
+ "text/template"
15
16
16
17
"github.com/h2non/filetype"
17
18
"github.com/h2non/filetype/matchers"
@@ -40,6 +41,9 @@ const (
40
41
RefDCImage
41
42
RefDCDir
42
43
44
+ RefBundleDir
45
+ RefPackageManifestDir
46
+
43
47
RefAll = 0
44
48
)
45
49
@@ -50,10 +54,11 @@ func (r RefType) Allowed(refType RefType) bool {
50
54
var ErrNotAllowed = errors .New ("not allowed" )
51
55
52
56
type Render struct {
53
- Refs []string
54
- Registry image.Registry
55
- AllowedRefMask RefType
56
- Migrate bool
57
+ Refs []string
58
+ Registry image.Registry
59
+ AllowedRefMask RefType
60
+ Migrate bool
61
+ ImageRefTemplate * template.Template
57
62
58
63
skipSqliteDeprecationLog bool
59
64
}
@@ -125,25 +130,50 @@ func (r Render) createRegistry() (*containerdregistry.Registry, error) {
125
130
}
126
131
127
132
func (r Render ) renderReference (ctx context.Context , ref string ) (* declcfg.DeclarativeConfig , error ) {
128
- if stat , serr := os .Stat (ref ); serr == nil {
129
- if stat .IsDir () {
130
- if ! r .AllowedRefMask .Allowed (RefDCDir ) {
131
- return nil , fmt .Errorf ("cannot render declarative config directory: %w" , ErrNotAllowed )
132
- }
133
- return declcfg .LoadFS (ctx , os .DirFS (ref ))
134
- } else {
135
- // The only supported file type is an sqlite DB file,
136
- // since declarative configs will be in a directory.
137
- if err := checkDBFile (ref ); err != nil {
138
- return nil , err
133
+ stat , err := os .Stat (ref )
134
+ if err != nil {
135
+ return r .imageToDeclcfg (ctx , ref )
136
+ }
137
+ if stat .IsDir () {
138
+ dirEntries , err := os .ReadDir (ref )
139
+ if err != nil {
140
+ return nil , err
141
+ }
142
+ if isBundle (dirEntries ) {
143
+ // Looks like a bundle directory
144
+ if ! r .AllowedRefMask .Allowed (RefBundleDir ) {
145
+ return nil , fmt .Errorf ("cannot render bundle directory %q: %w" , ref , ErrNotAllowed )
139
146
}
140
- if ! r .AllowedRefMask .Allowed (RefSqliteFile ) {
141
- return nil , fmt .Errorf ("cannot render sqlite file: %w" , ErrNotAllowed )
147
+ return r .renderBundleDirectory (ref )
148
+ } else if isPackageManifest (dirEntries ) {
149
+ // Looks like a package manifest directory
150
+ if ! r .AllowedRefMask .Allowed (RefPackageManifestDir ) {
151
+ return nil , fmt .Errorf ("cannot render package manifest directory: %w" , ErrNotAllowed )
142
152
}
143
- return sqliteToDeclcfg (ctx , ref )
153
+ return r .renderPackageManifest (ctx , ref )
154
+ }
155
+
156
+ // Otherwise, assume it is a declarative config root directory.
157
+ if ! r .AllowedRefMask .Allowed (RefDCDir ) {
158
+ return nil , fmt .Errorf ("cannot render declarative config directory: %w" , ErrNotAllowed )
144
159
}
160
+ return declcfg .LoadFS (ctx , os .DirFS (ref ))
161
+ }
162
+ // The only supported file type is an sqlite DB file,
163
+ // since declarative configs will be in a directory.
164
+ if err := checkDBFile (ref ); err != nil {
165
+ return nil , err
166
+ }
167
+ if ! r .AllowedRefMask .Allowed (RefSqliteFile ) {
168
+ return nil , fmt .Errorf ("cannot render sqlite file: %w" , ErrNotAllowed )
145
169
}
146
- return r .imageToDeclcfg (ctx , ref )
170
+
171
+ db , err := sqlite .Open (ref )
172
+ if err != nil {
173
+ return nil , err
174
+ }
175
+ defer db .Close ()
176
+ return sqliteToDeclcfg (ctx , db )
147
177
}
148
178
149
179
func (r Render ) imageToDeclcfg (ctx context.Context , imageRef string ) (* declcfg.DeclarativeConfig , error ) {
@@ -169,7 +199,12 @@ func (r Render) imageToDeclcfg(ctx context.Context, imageRef string) (*declcfg.D
169
199
if ! r .AllowedRefMask .Allowed (RefSqliteImage ) {
170
200
return nil , fmt .Errorf ("cannot render sqlite image: %w" , ErrNotAllowed )
171
201
}
172
- cfg , err = sqliteToDeclcfg (ctx , filepath .Join (tmpDir , dbFile ))
202
+ db , err := sqlite .Open (filepath .Join (tmpDir , dbFile ))
203
+ if err != nil {
204
+ return nil , err
205
+ }
206
+ defer db .Close ()
207
+ cfg , err = sqliteToDeclcfg (ctx , db )
173
208
if err != nil {
174
209
return nil , err
175
210
}
@@ -190,10 +225,11 @@ func (r Render) imageToDeclcfg(ctx context.Context, imageRef string) (*declcfg.D
190
225
return nil , err
191
226
}
192
227
193
- cfg , err = bundleToDeclcfg (img .Bundle )
228
+ bundle , err : = bundleToDeclcfg (img .Bundle )
194
229
if err != nil {
195
230
return nil , err
196
231
}
232
+ cfg = & declcfg.DeclarativeConfig {Bundles : []declcfg.Bundle {* bundle }}
197
233
} else {
198
234
labelKeys := sets .StringKeySet (labels )
199
235
labelVals := []string {}
@@ -221,17 +257,11 @@ func checkDBFile(ref string) error {
221
257
return nil
222
258
}
223
259
224
- func sqliteToDeclcfg (ctx context.Context , dbFile string ) (* declcfg.DeclarativeConfig , error ) {
260
+ func sqliteToDeclcfg (ctx context.Context , db * sql. DB ) (* declcfg.DeclarativeConfig , error ) {
225
261
logDeprecationMessage .Do (func () {
226
262
sqlite .LogSqliteDeprecation ()
227
263
})
228
264
229
- db , err := sqlite .Open (dbFile )
230
- if err != nil {
231
- return nil , err
232
- }
233
- defer db .Close ()
234
-
235
265
migrator , err := sqlite .NewSQLLiteMigrator (db )
236
266
if err != nil {
237
267
return nil , err
@@ -303,7 +333,7 @@ func populateDBRelatedImages(ctx context.Context, cfg *declcfg.DeclarativeConfig
303
333
return nil
304
334
}
305
335
306
- func bundleToDeclcfg (bundle * registry.Bundle ) (* declcfg.DeclarativeConfig , error ) {
336
+ func bundleToDeclcfg (bundle * registry.Bundle ) (* declcfg.Bundle , error ) {
307
337
objs , props , err := registry .ObjectsAndPropertiesFromBundle (bundle )
308
338
if err != nil {
309
339
return nil , fmt .Errorf ("get properties for bundle %q: %v" , bundle .Name , err )
@@ -323,7 +353,7 @@ func bundleToDeclcfg(bundle *registry.Bundle) (*declcfg.DeclarativeConfig, error
323
353
}
324
354
}
325
355
326
- dBundle := declcfg.Bundle {
356
+ return & declcfg.Bundle {
327
357
Schema : "olm.bundle" ,
328
358
Name : bundle .Name ,
329
359
Package : bundle .Package ,
@@ -332,9 +362,7 @@ func bundleToDeclcfg(bundle *registry.Bundle) (*declcfg.DeclarativeConfig, error
332
362
RelatedImages : relatedImages ,
333
363
Objects : objs ,
334
364
CsvJSON : string (csvJson ),
335
- }
336
-
337
- return & declcfg.DeclarativeConfig {Bundles : []declcfg.Bundle {dBundle }}, nil
365
+ }, nil
338
366
}
339
367
340
368
func getRelatedImages (b * registry.Bundle ) ([]declcfg.RelatedImage , error ) {
@@ -363,7 +391,7 @@ func getRelatedImages(b *registry.Bundle) ([]declcfg.RelatedImage, error) {
363
391
allImages = allImages .Insert (ri .Image )
364
392
}
365
393
366
- if ! allImages .Has (b .BundleImage ) {
394
+ if b . BundleImage != "" && ! allImages .Has (b .BundleImage ) {
367
395
relatedImages = append (relatedImages , declcfg.RelatedImage {
368
396
Image : b .BundleImage ,
369
397
})
@@ -454,3 +482,150 @@ func combineConfigs(cfgs []declcfg.DeclarativeConfig) *declcfg.DeclarativeConfig
454
482
}
455
483
return out
456
484
}
485
+
486
+ func isBundle (entries []os.DirEntry ) bool {
487
+ foundManifests := false
488
+ foundMetadata := false
489
+ for _ , e := range entries {
490
+ if e .IsDir () {
491
+ switch e .Name () {
492
+ case "manifests" :
493
+ foundManifests = true
494
+ case "metadata" :
495
+ foundMetadata = true
496
+ }
497
+ }
498
+ if foundMetadata && foundManifests {
499
+ return true
500
+ }
501
+ }
502
+ return false
503
+ }
504
+
505
+ func isPackageManifest (entries []os.DirEntry ) bool {
506
+ for _ , e := range entries {
507
+ if strings .HasSuffix (e .Name (), ".package.yaml" ) || strings .HasSuffix (e .Name (), ".package.yml" ) {
508
+ return true
509
+ }
510
+ }
511
+ return false
512
+ }
513
+
514
+ type imageReferenceTemplateData struct {
515
+ Package string
516
+ Name string
517
+ Version string
518
+ }
519
+
520
+ func (r * Render ) renderPackageManifest (ctx context.Context , ref string ) (* declcfg.DeclarativeConfig , error ) {
521
+ db , err := sql .Open ("sqlite3" , fmt .Sprintf ("file:%s?mode=memory&cache=shared&_foreign_keys=on" , ref ))
522
+ if err != nil {
523
+ return nil , err
524
+ }
525
+ defer db .Close ()
526
+
527
+ dbLoader , err := sqlite .NewSQLLiteLoader (db )
528
+ if err != nil {
529
+ return nil , err
530
+ }
531
+ if err := dbLoader .Migrate (ctx ); err != nil {
532
+ return nil , err
533
+ }
534
+
535
+ loader := sqlite .NewSQLLoaderForDirectory (dbLoader , ref )
536
+ if err := loader .Populate (); err != nil {
537
+ return nil , fmt .Errorf ("error loading manifests from directory: %s" , err )
538
+ }
539
+
540
+ if err := r .templateDBImageRefs (ctx , db ); err != nil {
541
+ return nil , fmt .Errorf ("error templating image references: %v" , err )
542
+ }
543
+
544
+ return sqliteToDeclcfg (ctx , db )
545
+ }
546
+
547
+ func (r * Render ) templateDBImageRefs (ctx context.Context , db * sql.DB ) error {
548
+ if r .ImageRefTemplate == nil {
549
+ return nil
550
+ }
551
+ tx , err := db .BeginTx (ctx , nil )
552
+ rows , err := tx .QueryContext (ctx , "SELECT DISTINCT en.package_name, op.name, op.version FROM operatorbundle op JOIN channel_entry en ON op.name = en.operatorbundle_name;" )
553
+ if err != nil {
554
+ return err
555
+ }
556
+ defer rows .Close ()
557
+
558
+ for rows .Next () {
559
+ var (
560
+ pkgName sql.NullString
561
+ name sql.NullString
562
+ version sql.NullString
563
+ )
564
+ if err := rows .Scan (& pkgName , & name , & version ); err != nil {
565
+ return err
566
+ }
567
+ if ! pkgName .Valid || ! name .Valid || ! version .Valid {
568
+ continue
569
+ }
570
+ var buf strings.Builder
571
+ tmplInput := imageReferenceTemplateData {
572
+ Package : pkgName .String ,
573
+ Name : name .String ,
574
+ Version : version .String ,
575
+ }
576
+ if err := r .ImageRefTemplate .Execute (& buf , tmplInput ); err != nil {
577
+ return err
578
+ }
579
+ if _ , err := tx .ExecContext (ctx , "UPDATE operatorbundle SET bundlePath = ? WHERE name = ?;" , buf .String (), name .String ); err != nil {
580
+ if rollbackErr := tx .Rollback (); rollbackErr != nil {
581
+ return fmt .Errorf ("failed to rollback transaction: %v; original error: %v" , rollbackErr , err )
582
+ }
583
+ return err
584
+ }
585
+ }
586
+ return tx .Commit ()
587
+ }
588
+
589
+ func (r * Render ) renderBundleDirectory (ref string ) (* declcfg.DeclarativeConfig , error ) {
590
+ img , err := registry .NewImageInput (image .SimpleReference ("" ), ref )
591
+ if err != nil {
592
+ return nil , err
593
+ }
594
+ if err := r .templateBundleImageRef (img .Bundle ); err != nil {
595
+ return nil , fmt .Errorf ("failed templating image reference from bundle for %q: %v" , ref , err )
596
+ }
597
+ fbcBundle , err := bundleToDeclcfg (img .Bundle )
598
+ if err != nil {
599
+ return nil , err
600
+ }
601
+ return & declcfg.DeclarativeConfig {Bundles : []declcfg.Bundle {* fbcBundle }}, nil
602
+ }
603
+
604
+ func (r * Render ) templateBundleImageRef (bundle * registry.Bundle ) error {
605
+ if r .ImageRefTemplate == nil {
606
+ return nil
607
+ }
608
+
609
+ var pkgProp property.Package
610
+ for _ , p := range bundle .Properties {
611
+ if p .Type != property .TypePackage {
612
+ continue
613
+ }
614
+ if err := json .Unmarshal (p .Value , & pkgProp ); err != nil {
615
+ return err
616
+ }
617
+ break
618
+ }
619
+
620
+ var buf strings.Builder
621
+ tmplInput := imageReferenceTemplateData {
622
+ Package : bundle .Package ,
623
+ Name : bundle .Name ,
624
+ Version : pkgProp .Version ,
625
+ }
626
+ if err := r .ImageRefTemplate .Execute (& buf , tmplInput ); err != nil {
627
+ return err
628
+ }
629
+ bundle .BundleImage = buf .String ()
630
+ return nil
631
+ }
0 commit comments