Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

render: support rendering FBC from bundle directories #748

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 124 additions & 35 deletions alpha/action/render.go
Original file line number Diff line number Diff line change
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
}
Loading
Loading