Skip to content

Commit

Permalink
render: support rendering FBC from bundle directories (#748)
Browse files Browse the repository at this point in the history
* 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 <joe.lanford@gmail.com>

* make --image-ref-template alpha (and include simple framework for alpha flags and usage)

Signed-off-by: Joe Lanford <joe.lanford@gmail.com>

---------

Signed-off-by: Joe Lanford <joe.lanford@gmail.com>
  • Loading branch information
joelanford committed Feb 27, 2024
1 parent 805788e commit a21f962
Show file tree
Hide file tree
Showing 11 changed files with 328 additions and 68 deletions.
159 changes: 124 additions & 35 deletions alpha/action/render.go
Expand Up @@ -12,6 +12,7 @@ import (
"sort"
"strings"
"sync"
"text/template"

"github.com/h2non/filetype"
"github.com/h2non/filetype/matchers"
Expand Down Expand Up @@ -39,6 +40,7 @@ const (
RefSqliteFile
RefDCImage
RefDCDir
RefBundleDir

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

type Render struct {
Refs []string
Registry image.Registry
AllowedRefMask RefType
Migrate bool
Refs []string
Registry image.Registry
AllowedRefMask RefType
Migrate bool
ImageRefTemplate *template.Template

skipSqliteDeprecationLog bool
}
Expand Down Expand Up @@ -125,25 +128,44 @@ func (r Render) createRegistry() (*containerdregistry.Registry, error) {
}

func (r Render) renderReference(ctx context.Context, ref string) (*declcfg.DeclarativeConfig, error) {
if stat, serr := os.Stat(ref); serr == nil {
if stat.IsDir() {
if !r.AllowedRefMask.Allowed(RefDCDir) {
return nil, fmt.Errorf("cannot render declarative config directory: %w", ErrNotAllowed)
}
return declcfg.LoadFS(ctx, os.DirFS(ref))
} else {
// The only supported file type is an sqlite DB file,
// since declarative configs will be in a directory.
if err := checkDBFile(ref); err != nil {
return nil, err
}
if !r.AllowedRefMask.Allowed(RefSqliteFile) {
return nil, fmt.Errorf("cannot render sqlite file: %w", ErrNotAllowed)
stat, err := os.Stat(ref)
if err != nil {
return r.imageToDeclcfg(ctx, ref)
}
if stat.IsDir() {
dirEntries, err := os.ReadDir(ref)
if err != nil {
return nil, err
}
if isBundle(dirEntries) {
// Looks like a bundle directory
if !r.AllowedRefMask.Allowed(RefBundleDir) {
return nil, fmt.Errorf("cannot render bundle directory %q: %w", ref, ErrNotAllowed)
}
return sqliteToDeclcfg(ctx, ref)
return r.renderBundleDirectory(ref)
}

// Otherwise, assume it is a declarative config root directory.
if !r.AllowedRefMask.Allowed(RefDCDir) {
return nil, fmt.Errorf("cannot render declarative config directory: %w", ErrNotAllowed)
}
return declcfg.LoadFS(ctx, os.DirFS(ref))
}
// The only supported file type is an sqlite DB file,
// since declarative configs will be in a directory.
if err := checkDBFile(ref); err != nil {
return nil, err
}
if !r.AllowedRefMask.Allowed(RefSqliteFile) {
return nil, fmt.Errorf("cannot render sqlite file: %w", ErrNotAllowed)
}

db, err := sqlite.Open(ref)
if err != nil {
return nil, err
}
return r.imageToDeclcfg(ctx, ref)
defer db.Close()
return sqliteToDeclcfg(ctx, db)
}

func (r Render) imageToDeclcfg(ctx context.Context, imageRef string) (*declcfg.DeclarativeConfig, error) {
Expand All @@ -169,7 +191,12 @@ func (r Render) imageToDeclcfg(ctx context.Context, imageRef string) (*declcfg.D
if !r.AllowedRefMask.Allowed(RefSqliteImage) {
return nil, fmt.Errorf("cannot render sqlite image: %w", ErrNotAllowed)
}
cfg, err = sqliteToDeclcfg(ctx, filepath.Join(tmpDir, dbFile))
db, err := sqlite.Open(filepath.Join(tmpDir, dbFile))
if err != nil {
return nil, err
}
defer db.Close()
cfg, err = sqliteToDeclcfg(ctx, db)
if err != nil {
return nil, err
}
Expand All @@ -190,10 +217,11 @@ func (r Render) imageToDeclcfg(ctx context.Context, imageRef string) (*declcfg.D
return nil, err
}

cfg, err = bundleToDeclcfg(img.Bundle)
bundle, err := bundleToDeclcfg(img.Bundle)
if err != nil {
return nil, err
}
cfg = &declcfg.DeclarativeConfig{Bundles: []declcfg.Bundle{*bundle}}
} else {
labelKeys := sets.StringKeySet(labels)
labelVals := []string{}
Expand Down Expand Up @@ -221,17 +249,11 @@ func checkDBFile(ref string) error {
return nil
}

func sqliteToDeclcfg(ctx context.Context, dbFile string) (*declcfg.DeclarativeConfig, error) {
func sqliteToDeclcfg(ctx context.Context, db *sql.DB) (*declcfg.DeclarativeConfig, error) {
logDeprecationMessage.Do(func() {
sqlite.LogSqliteDeprecation()
})

db, err := sqlite.Open(dbFile)
if err != nil {
return nil, err
}
defer db.Close()

migrator, err := sqlite.NewSQLLiteMigrator(db)
if err != nil {
return nil, err
Expand Down Expand Up @@ -303,7 +325,7 @@ func populateDBRelatedImages(ctx context.Context, cfg *declcfg.DeclarativeConfig
return nil
}

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

dBundle := declcfg.Bundle{
return &declcfg.Bundle{
Schema: "olm.bundle",
Name: bundle.Name,
Package: bundle.Package,
Expand All @@ -332,9 +354,7 @@ func bundleToDeclcfg(bundle *registry.Bundle) (*declcfg.DeclarativeConfig, error
RelatedImages: relatedImages,
Objects: objs,
CsvJSON: string(csvJson),
}

return &declcfg.DeclarativeConfig{Bundles: []declcfg.Bundle{dBundle}}, nil
}, nil
}

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

if !allImages.Has(b.BundleImage) {
if b.BundleImage != "" && !allImages.Has(b.BundleImage) {
relatedImages = append(relatedImages, declcfg.RelatedImage{
Image: b.BundleImage,
})
Expand Down Expand Up @@ -454,3 +474,72 @@ func combineConfigs(cfgs []declcfg.DeclarativeConfig) *declcfg.DeclarativeConfig
}
return out
}

func isBundle(entries []os.DirEntry) bool {
foundManifests := false
foundMetadata := false
for _, e := range entries {
if e.IsDir() {
switch e.Name() {
case "manifests":
foundManifests = true
case "metadata":
foundMetadata = true
}
}
if foundMetadata && foundManifests {
return true
}
}
return false
}

type imageReferenceTemplateData struct {
Package string
Name string
Version string
}

func (r *Render) renderBundleDirectory(ref string) (*declcfg.DeclarativeConfig, error) {
img, err := registry.NewImageInput(image.SimpleReference(""), ref)
if err != nil {
return nil, err
}
if err := r.templateBundleImageRef(img.Bundle); err != nil {
return nil, fmt.Errorf("failed templating image reference from bundle for %q: %v", ref, err)
}
fbcBundle, err := bundleToDeclcfg(img.Bundle)
if err != nil {
return nil, err
}
return &declcfg.DeclarativeConfig{Bundles: []declcfg.Bundle{*fbcBundle}}, nil
}

func (r *Render) templateBundleImageRef(bundle *registry.Bundle) error {
if r.ImageRefTemplate == nil {
return nil
}

var pkgProp property.Package
for _, p := range bundle.Properties {
if p.Type != property.TypePackage {
continue
}
if err := json.Unmarshal(p.Value, &pkgProp); err != nil {
return err
}
break
}

var buf strings.Builder
tmplInput := imageReferenceTemplateData{
Package: bundle.Package,
Name: bundle.Name,
Version: pkgProp.Version,
}
if err := r.ImageRefTemplate.Execute(&buf, tmplInput); err != nil {
return err
}
bundle.BundleImage = buf.String()
return nil
}

0 comments on commit a21f962

Please sign in to comment.