Skip to content

Commit a21f962

Browse files
authoredFeb 27, 2024··
render: support rendering FBC from bundle directories (#748)
* render: support rendering FBC from bundle and packagemanifest directories In order to generate expected olm.bundles, this commit also adds a new --image-ref-template flag to the `render` subcommand, which callers can use to generate image references from source data based on package name, bundle name, and bundle version. Signed-off-by: Joe Lanford <[email protected]> * make --image-ref-template alpha (and include simple framework for alpha flags and usage) Signed-off-by: Joe Lanford <[email protected]> --------- Signed-off-by: Joe Lanford <[email protected]>
1 parent 805788e commit a21f962

File tree

11 files changed

+328
-68
lines changed

11 files changed

+328
-68
lines changed
 

‎alpha/action/render.go

+124-35
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
"sort"
1313
"strings"
1414
"sync"
15+
"text/template"
1516

1617
"github.com/h2non/filetype"
1718
"github.com/h2non/filetype/matchers"
@@ -39,6 +40,7 @@ const (
3940
RefSqliteFile
4041
RefDCImage
4142
RefDCDir
43+
RefBundleDir
4244

4345
RefAll = 0
4446
)
@@ -50,10 +52,11 @@ func (r RefType) Allowed(refType RefType) bool {
5052
var ErrNotAllowed = errors.New("not allowed")
5153

5254
type Render struct {
53-
Refs []string
54-
Registry image.Registry
55-
AllowedRefMask RefType
56-
Migrate bool
55+
Refs []string
56+
Registry image.Registry
57+
AllowedRefMask RefType
58+
Migrate bool
59+
ImageRefTemplate *template.Template
5760

5861
skipSqliteDeprecationLog bool
5962
}
@@ -125,25 +128,44 @@ func (r Render) createRegistry() (*containerdregistry.Registry, error) {
125128
}
126129

127130
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
139-
}
140-
if !r.AllowedRefMask.Allowed(RefSqliteFile) {
141-
return nil, fmt.Errorf("cannot render sqlite file: %w", ErrNotAllowed)
131+
stat, err := os.Stat(ref)
132+
if err != nil {
133+
return r.imageToDeclcfg(ctx, ref)
134+
}
135+
if stat.IsDir() {
136+
dirEntries, err := os.ReadDir(ref)
137+
if err != nil {
138+
return nil, err
139+
}
140+
if isBundle(dirEntries) {
141+
// Looks like a bundle directory
142+
if !r.AllowedRefMask.Allowed(RefBundleDir) {
143+
return nil, fmt.Errorf("cannot render bundle directory %q: %w", ref, ErrNotAllowed)
142144
}
143-
return sqliteToDeclcfg(ctx, ref)
145+
return r.renderBundleDirectory(ref)
144146
}
147+
148+
// Otherwise, assume it is a declarative config root directory.
149+
if !r.AllowedRefMask.Allowed(RefDCDir) {
150+
return nil, fmt.Errorf("cannot render declarative config directory: %w", ErrNotAllowed)
151+
}
152+
return declcfg.LoadFS(ctx, os.DirFS(ref))
153+
}
154+
// The only supported file type is an sqlite DB file,
155+
// since declarative configs will be in a directory.
156+
if err := checkDBFile(ref); err != nil {
157+
return nil, err
158+
}
159+
if !r.AllowedRefMask.Allowed(RefSqliteFile) {
160+
return nil, fmt.Errorf("cannot render sqlite file: %w", ErrNotAllowed)
161+
}
162+
163+
db, err := sqlite.Open(ref)
164+
if err != nil {
165+
return nil, err
145166
}
146-
return r.imageToDeclcfg(ctx, ref)
167+
defer db.Close()
168+
return sqliteToDeclcfg(ctx, db)
147169
}
148170

149171
func (r Render) imageToDeclcfg(ctx context.Context, imageRef string) (*declcfg.DeclarativeConfig, error) {
@@ -169,7 +191,12 @@ func (r Render) imageToDeclcfg(ctx context.Context, imageRef string) (*declcfg.D
169191
if !r.AllowedRefMask.Allowed(RefSqliteImage) {
170192
return nil, fmt.Errorf("cannot render sqlite image: %w", ErrNotAllowed)
171193
}
172-
cfg, err = sqliteToDeclcfg(ctx, filepath.Join(tmpDir, dbFile))
194+
db, err := sqlite.Open(filepath.Join(tmpDir, dbFile))
195+
if err != nil {
196+
return nil, err
197+
}
198+
defer db.Close()
199+
cfg, err = sqliteToDeclcfg(ctx, db)
173200
if err != nil {
174201
return nil, err
175202
}
@@ -190,10 +217,11 @@ func (r Render) imageToDeclcfg(ctx context.Context, imageRef string) (*declcfg.D
190217
return nil, err
191218
}
192219

193-
cfg, err = bundleToDeclcfg(img.Bundle)
220+
bundle, err := bundleToDeclcfg(img.Bundle)
194221
if err != nil {
195222
return nil, err
196223
}
224+
cfg = &declcfg.DeclarativeConfig{Bundles: []declcfg.Bundle{*bundle}}
197225
} else {
198226
labelKeys := sets.StringKeySet(labels)
199227
labelVals := []string{}
@@ -221,17 +249,11 @@ func checkDBFile(ref string) error {
221249
return nil
222250
}
223251

224-
func sqliteToDeclcfg(ctx context.Context, dbFile string) (*declcfg.DeclarativeConfig, error) {
252+
func sqliteToDeclcfg(ctx context.Context, db *sql.DB) (*declcfg.DeclarativeConfig, error) {
225253
logDeprecationMessage.Do(func() {
226254
sqlite.LogSqliteDeprecation()
227255
})
228256

229-
db, err := sqlite.Open(dbFile)
230-
if err != nil {
231-
return nil, err
232-
}
233-
defer db.Close()
234-
235257
migrator, err := sqlite.NewSQLLiteMigrator(db)
236258
if err != nil {
237259
return nil, err
@@ -303,7 +325,7 @@ func populateDBRelatedImages(ctx context.Context, cfg *declcfg.DeclarativeConfig
303325
return nil
304326
}
305327

306-
func bundleToDeclcfg(bundle *registry.Bundle) (*declcfg.DeclarativeConfig, error) {
328+
func bundleToDeclcfg(bundle *registry.Bundle) (*declcfg.Bundle, error) {
307329
objs, props, err := registry.ObjectsAndPropertiesFromBundle(bundle)
308330
if err != nil {
309331
return nil, fmt.Errorf("get properties for bundle %q: %v", bundle.Name, err)
@@ -323,7 +345,7 @@ func bundleToDeclcfg(bundle *registry.Bundle) (*declcfg.DeclarativeConfig, error
323345
}
324346
}
325347

326-
dBundle := declcfg.Bundle{
348+
return &declcfg.Bundle{
327349
Schema: "olm.bundle",
328350
Name: bundle.Name,
329351
Package: bundle.Package,
@@ -332,9 +354,7 @@ func bundleToDeclcfg(bundle *registry.Bundle) (*declcfg.DeclarativeConfig, error
332354
RelatedImages: relatedImages,
333355
Objects: objs,
334356
CsvJSON: string(csvJson),
335-
}
336-
337-
return &declcfg.DeclarativeConfig{Bundles: []declcfg.Bundle{dBundle}}, nil
357+
}, nil
338358
}
339359

340360
func getRelatedImages(b *registry.Bundle) ([]declcfg.RelatedImage, error) {
@@ -363,7 +383,7 @@ func getRelatedImages(b *registry.Bundle) ([]declcfg.RelatedImage, error) {
363383
allImages = allImages.Insert(ri.Image)
364384
}
365385

366-
if !allImages.Has(b.BundleImage) {
386+
if b.BundleImage != "" && !allImages.Has(b.BundleImage) {
367387
relatedImages = append(relatedImages, declcfg.RelatedImage{
368388
Image: b.BundleImage,
369389
})
@@ -454,3 +474,72 @@ func combineConfigs(cfgs []declcfg.DeclarativeConfig) *declcfg.DeclarativeConfig
454474
}
455475
return out
456476
}
477+
478+
func isBundle(entries []os.DirEntry) bool {
479+
foundManifests := false
480+
foundMetadata := false
481+
for _, e := range entries {
482+
if e.IsDir() {
483+
switch e.Name() {
484+
case "manifests":
485+
foundManifests = true
486+
case "metadata":
487+
foundMetadata = true
488+
}
489+
}
490+
if foundMetadata && foundManifests {
491+
return true
492+
}
493+
}
494+
return false
495+
}
496+
497+
type imageReferenceTemplateData struct {
498+
Package string
499+
Name string
500+
Version string
501+
}
502+
503+
func (r *Render) renderBundleDirectory(ref string) (*declcfg.DeclarativeConfig, error) {
504+
img, err := registry.NewImageInput(image.SimpleReference(""), ref)
505+
if err != nil {
506+
return nil, err
507+
}
508+
if err := r.templateBundleImageRef(img.Bundle); err != nil {
509+
return nil, fmt.Errorf("failed templating image reference from bundle for %q: %v", ref, err)
510+
}
511+
fbcBundle, err := bundleToDeclcfg(img.Bundle)
512+
if err != nil {
513+
return nil, err
514+
}
515+
return &declcfg.DeclarativeConfig{Bundles: []declcfg.Bundle{*fbcBundle}}, nil
516+
}
517+
518+
func (r *Render) templateBundleImageRef(bundle *registry.Bundle) error {
519+
if r.ImageRefTemplate == nil {
520+
return nil
521+
}
522+
523+
var pkgProp property.Package
524+
for _, p := range bundle.Properties {
525+
if p.Type != property.TypePackage {
526+
continue
527+
}
528+
if err := json.Unmarshal(p.Value, &pkgProp); err != nil {
529+
return err
530+
}
531+
break
532+
}
533+
534+
var buf strings.Builder
535+
tmplInput := imageReferenceTemplateData{
536+
Package: bundle.Package,
537+
Name: bundle.Name,
538+
Version: pkgProp.Version,
539+
}
540+
if err := r.ImageRefTemplate.Execute(&buf, tmplInput); err != nil {
541+
return err
542+
}
543+
bundle.BundleImage = buf.String()
544+
return nil
545+
}

0 commit comments

Comments
 (0)
Please sign in to comment.