Skip to content

Commit

Permalink
render: include olm.bundle.object properties in rendered bundle images (
Browse files Browse the repository at this point in the history
#807)

Signed-off-by: Joe Lanford <joe.lanford@gmail.com>
  • Loading branch information
joelanford authored Nov 1, 2021
1 parent d5522b3 commit 7c8187a
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 179 deletions.
15 changes: 13 additions & 2 deletions alpha/action/render.go
Original file line number Diff line number Diff line change
Expand Up @@ -296,22 +296,33 @@ func populateDBRelatedImages(ctx context.Context, cfg *declcfg.DeclarativeConfig
}

func bundleToDeclcfg(bundle *registry.Bundle) (*declcfg.DeclarativeConfig, error) {
bundleProperties, err := registry.PropertiesFromBundle(bundle)
objs, props, err := registry.ObjectsAndPropertiesFromBundle(bundle)
if err != nil {
return nil, fmt.Errorf("get properties for bundle %q: %v", bundle.Name, err)
}
relatedImages, err := getRelatedImages(bundle)
if err != nil {
return nil, fmt.Errorf("get related images for bundle %q: %v", bundle.Name, err)
}
var csvJson []byte
for _, obj := range bundle.Objects {
if obj.GetKind() == "ClusterServiceVersion" {
csvJson, err = json.Marshal(obj)
if err != nil {
return nil, fmt.Errorf("marshal CSV JSON for bundle %q: %v", bundle.Name, err)
}
}
}

dBundle := declcfg.Bundle{
Schema: "olm.bundle",
Name: bundle.Name,
Package: bundle.Package,
Image: bundle.BundleImage,
Properties: bundleProperties,
Properties: props,
RelatedImages: relatedImages,
Objects: objs,
CsvJSON: string(csvJson),
}

return &declcfg.DeclarativeConfig{Bundles: []declcfg.Bundle{dBundle}}, nil
Expand Down
16 changes: 16 additions & 0 deletions alpha/action/render_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ func TestRender(t *testing.T) {
require.NoError(t, err)
foov2crd, err := bundleImageV2.ReadFile("testdata/foo-bundle-v0.2.0/manifests/foos.test.foo.crd.yaml")
require.NoError(t, err)
foov2csvNoRelatedImages, err := bundleImageV2NoCSVRelatedImages.ReadFile("testdata/foo-bundle-v0.2.0-no-csv-related-images/manifests/foo.v0.2.0.csv.yaml")
require.NoError(t, err)
foov2crdNoRelatedImages, err := bundleImageV2NoCSVRelatedImages.ReadFile("testdata/foo-bundle-v0.2.0-no-csv-related-images/manifests/foos.test.foo.crd.yaml")
require.NoError(t, err)

foov1csv, err = yaml.ToJSON(foov1csv)
require.NoError(t, err)
Expand All @@ -51,6 +55,10 @@ func TestRender(t *testing.T) {
require.NoError(t, err)
foov2crd, err = yaml.ToJSON(foov2crd)
require.NoError(t, err)
foov2csvNoRelatedImages, err = yaml.ToJSON(foov2csvNoRelatedImages)
require.NoError(t, err)
foov2crdNoRelatedImages, err = yaml.ToJSON(foov2crdNoRelatedImages)
require.NoError(t, err)

dir := t.TempDir()
dbFile := filepath.Join(dir, "index.db")
Expand Down Expand Up @@ -445,7 +453,11 @@ func TestRender(t *testing.T) {
property.MustBuildGVKRequired("test.bar", "v1alpha1", "Bar"),
property.MustBuildPackage("foo", "0.2.0"),
property.MustBuildPackageRequired("bar", "<0.1.0"),
property.MustBuildBundleObjectData(foov2csv),
property.MustBuildBundleObjectData(foov2crd),
},
Objects: []string{string(foov2csv), string(foov2crd)},
CsvJSON: string(foov2csv),
RelatedImages: []declcfg.RelatedImage{
{
Image: "test.registry/foo-operator/foo-2:v0.2.0",
Expand Down Expand Up @@ -491,7 +503,11 @@ func TestRender(t *testing.T) {
property.MustBuildGVKRequired("test.bar", "v1alpha1", "Bar"),
property.MustBuildPackage("foo", "0.2.0"),
property.MustBuildPackageRequired("bar", "<0.1.0"),
property.MustBuildBundleObjectData(foov2csvNoRelatedImages),
property.MustBuildBundleObjectData(foov2crdNoRelatedImages),
},
Objects: []string{string(foov2csvNoRelatedImages), string(foov2crdNoRelatedImages)},
CsvJSON: string(foov2csvNoRelatedImages),
RelatedImages: []declcfg.RelatedImage{
{
Image: "test.registry/foo-operator/foo-2:v0.2.0",
Expand Down
166 changes: 31 additions & 135 deletions pkg/registry/registry_to_model.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,90 +4,11 @@ import (
"encoding/json"
"fmt"
"sort"
"strings"

"github.com/operator-framework/operator-registry/alpha/model"
"github.com/operator-framework/operator-registry/alpha/property"
)

func ConvertRegistryBundleToModelBundles(b *Bundle) ([]model.Bundle, error) {
var bundles []model.Bundle
desc, err := b.csv.GetDescription()
if err != nil {
return nil, fmt.Errorf("Could not get description from bundle CSV:%s", err)
}

i, err := b.csv.GetIcons()
if err != nil {
return nil, fmt.Errorf("Could not get icon from bundle CSV:%s", err)
}
mIcon := &model.Icon{
MediaType: "",
Data: []byte{},
}
if len(i) > 0 {
mIcon.MediaType = i[0].MediaType
mIcon.Data = []byte(i[0].Base64data)
}

pkg := &model.Package{
Name: b.Annotations.PackageName,
Description: desc,
Icon: mIcon,
Channels: make(map[string]*model.Channel),
}

mb, err := registryBundleToModelBundle(b)
mb.Package = pkg
if err != nil {
return nil, err
}

for _, ch := range extractChannels(b.Annotations.Channels) {
newCh := &model.Channel{
Name: ch,
}
chBundle := mb
chBundle.Channel = newCh
bundles = append(bundles, *chBundle)
}
return bundles, nil
}

func registryBundleToModelBundle(b *Bundle) (*model.Bundle, error) {
bundleProps, err := PropertiesFromBundle(b)
if err != nil {
return nil, fmt.Errorf("error converting properties for internal model: %v", err)
}

csv, err := b.ClusterServiceVersion()
if err != nil {
return nil, fmt.Errorf("Could not get CVS for bundle: %s", err)
}
replaces, err := csv.GetReplaces()
if err != nil {
return nil, fmt.Errorf("Could not get Replaces from CSV for bundle: %s", err)
}
skips, err := csv.GetSkips()
if err != nil {
return nil, fmt.Errorf("Could not get Skips from CSV for bundle: %s", err)
}
relatedImages, err := convertToModelRelatedImages(csv)
if err != nil {
return nil, fmt.Errorf("Could not get Related images from bundle: %v", err)
}

return &model.Bundle{
Name: csv.Name,
Image: b.BundleImage,
Replaces: replaces,
Skips: skips,
Properties: bundleProps,
RelatedImages: relatedImages,
}, nil
}

func PropertiesFromBundle(b *Bundle) ([]property.Property, error) {
func ObjectsAndPropertiesFromBundle(b *Bundle) ([]string, []property.Property, error) {
providedGVKs := map[property.GVK]struct{}{}
requiredGVKs := map[property.GVKRequired]struct{}{}

Expand All @@ -99,14 +20,14 @@ func PropertiesFromBundle(b *Bundle) ([]property.Property, error) {
case property.TypeGVK:
var v property.GVK
if err := json.Unmarshal(p.Value, &v); err != nil {
return nil, property.ParseError{Idx: i, Typ: p.Type, Err: err}
return nil, nil, property.ParseError{Idx: i, Typ: p.Type, Err: err}
}
k := property.GVK{Group: v.Group, Kind: v.Kind, Version: v.Version}
providedGVKs[k] = struct{}{}
case property.TypePackage:
var v property.Package
if err := json.Unmarshal(p.Value, &v); err != nil {
return nil, property.ParseError{Idx: i, Typ: p.Type, Err: err}
return nil, nil, property.ParseError{Idx: i, Typ: p.Type, Err: err}
}
p := property.MustBuildPackage(v.PackageName, v.Version)
packageProvidedProperty = &p
Expand All @@ -124,27 +45,27 @@ func PropertiesFromBundle(b *Bundle) ([]property.Property, error) {
case property.TypeGVK:
var v property.GVK
if err := json.Unmarshal(p.Value, &v); err != nil {
return nil, property.ParseError{Idx: i, Typ: p.Type, Err: err}
return nil, nil, property.ParseError{Idx: i, Typ: p.Type, Err: err}
}
k := property.GVKRequired{Group: v.Group, Kind: v.Kind, Version: v.Version}
requiredGVKs[k] = struct{}{}
case property.TypePackage:
var v property.Package
if err := json.Unmarshal(p.Value, &v); err != nil {
return nil, property.ParseError{Idx: i, Typ: p.Type, Err: err}
return nil, nil, property.ParseError{Idx: i, Typ: p.Type, Err: err}
}
packageRequiredProps = append(packageRequiredProps, property.MustBuildPackageRequired(v.PackageName, v.Version))
}
}

version, err := b.Version()
if err != nil {
return nil, fmt.Errorf("get version: %v", err)
return nil, nil, fmt.Errorf("get version: %v", err)
}

providedApis, err := b.ProvidedAPIs()
if err != nil {
return nil, fmt.Errorf("get provided apis: %v", err)
return nil, nil, fmt.Errorf("get provided apis: %v", err)
}

for p := range providedApis {
Expand All @@ -155,7 +76,7 @@ func PropertiesFromBundle(b *Bundle) ([]property.Property, error) {
}
requiredApis, err := b.RequiredAPIs()
if err != nil {
return nil, fmt.Errorf("get required apis: %v", err)
return nil, nil, fmt.Errorf("get required apis: %v", err)
}
for p := range requiredApis {
k := property.GVKRequired{Group: p.Group, Kind: p.Kind, Version: p.Version}
Expand All @@ -164,67 +85,42 @@ func PropertiesFromBundle(b *Bundle) ([]property.Property, error) {
}
}

var out []property.Property
var (
props []property.Property
objects []string
)
for _, obj := range b.Objects {
objData, err := json.Marshal(obj)
if err != nil {
return nil, nil, fmt.Errorf("marshal object %s/%s (%s) to json: %v", obj.GetName(), obj.GetNamespace(), obj.GroupVersionKind(), err)
}
props = append(props, property.MustBuildBundleObjectData(objData))
objects = append(objects, string(objData))
}

if packageProvidedProperty == nil {
p := property.MustBuildPackage(b.Package, version)
packageProvidedProperty = &p
}
out = append(out, *packageProvidedProperty)
props = append(props, *packageProvidedProperty)

for p := range providedGVKs {
out = append(out, property.MustBuildGVK(p.Group, p.Version, p.Kind))
props = append(props, property.MustBuildGVK(p.Group, p.Version, p.Kind))
}

for p := range requiredGVKs {
out = append(out, property.MustBuildGVKRequired(p.Group, p.Version, p.Kind))
props = append(props, property.MustBuildGVKRequired(p.Group, p.Version, p.Kind))
}

out = append(out, packageRequiredProps...)
out = append(out, otherProps...)
props = append(props, packageRequiredProps...)
props = append(props, otherProps...)

sort.Slice(out, func(i, j int) bool {
if out[i].Type != out[j].Type {
return out[i].Type < out[j].Type
sort.Slice(props, func(i, j int) bool {
if props[i].Type != props[j].Type {
return props[i].Type < props[j].Type
}
return string(out[i].Value) < string(out[j].Value)
return string(props[i].Value) < string(props[j].Value)
})

return out, nil
}

func convertToModelRelatedImages(csv *ClusterServiceVersion) ([]model.RelatedImage, error) {
var objmap map[string]*json.RawMessage
if err := json.Unmarshal(csv.Spec, &objmap); err != nil {
return nil, err
}

rawValue, ok := objmap[relatedImages]
if !ok || rawValue == nil {
return nil, nil
}

type relatedImage struct {
Name string `json:"name"`
Ref string `json:"image"`
}
var relatedImages []relatedImage
if err := json.Unmarshal(*rawValue, &relatedImages); err != nil {
return nil, err
}
mrelatedImages := []model.RelatedImage{}
for _, img := range relatedImages {
mrelatedImages = append(mrelatedImages, model.RelatedImage{Name: img.Name, Image: img.Ref})
}
return mrelatedImages, nil
}

func extractChannels(annotationChannels string) []string {
var channels []string
for _, ch := range strings.Split(annotationChannels, ",") {
c := strings.TrimSpace(ch)
if c != "" {
channels = append(channels, ch)
}
}
return channels
return objects, props, nil
}
Loading

0 comments on commit 7c8187a

Please sign in to comment.