Skip to content

render: include olm.bundle.object properties in rendered bundle images #807

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

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
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)
Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the breaking change that go-apidiff is complaining about. This function was introduced with FBC code, so it hasn't been in the wild long.

I'm perfectly willing to refactor to avoid a breaking change if others think its necessary here.

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