Skip to content

Commit

Permalink
Switch to catalogd's CatalogMetadata APIs
Browse files Browse the repository at this point in the history
Signed-off-by: Mikalai Radchuk <mradchuk@redhat.com>
  • Loading branch information
m1kola committed Aug 22, 2023
1 parent fd7cfb5 commit 4a43091
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 105 deletions.
52 changes: 3 additions & 49 deletions cmd/resolutioncli/entity_source.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,17 +18,13 @@ package main

import (
"context"
"encoding/json"
"fmt"

"github.com/operator-framework/deppy/pkg/deppy"
"github.com/operator-framework/deppy/pkg/deppy/input"
"github.com/operator-framework/operator-registry/alpha/action"
"github.com/operator-framework/operator-registry/alpha/declcfg"
"github.com/operator-framework/operator-registry/alpha/model"
"github.com/operator-framework/operator-registry/alpha/property"

olmentity "github.com/operator-framework/operator-controller/internal/resolution/entities"
"github.com/operator-framework/operator-controller/internal/resolution/entitysources"
)

type indexRefEntitySource struct {
Expand Down Expand Up @@ -106,7 +102,8 @@ func (es *indexRefEntitySource) entities(ctx context.Context) (input.EntityList,
return nil, err
}

entities, err := modelToEntities(model)
// TODO: update empty catalog name string to be catalog name once we support multiple catalogs in CLI
entities, err := entitysources.ModelToEntities(model, "")
if err != nil {
return nil, err
}
Expand All @@ -116,46 +113,3 @@ func (es *indexRefEntitySource) entities(ctx context.Context) (input.EntityList,

return es.entitiesCache, nil
}

// TODO: Reduce code duplication: share a function with catalogdEntitySource (see getEntities)
// We don't want to maintain two functions performing conversion into input.EntityList.
// For this we need some common format. So we need a package which will be able
// to convert from declfcg structs into CRD structs directly or via model.Model.
// One option would be to make this piece of code from catalogd reusable and exportable:
// https://github.com/operator-framework/catalogd/blob/9fe45a628de2e74d9cd73c3650fa2582aaac5213/pkg/controllers/core/catalog_controller.go#L200-L360
func modelToEntities(model model.Model) (input.EntityList, error) {
entities := input.EntityList{}

for _, pkg := range model {
for _, ch := range pkg.Channels {
for _, bundle := range ch.Bundles {
props := map[string]string{}

for _, prop := range bundle.Properties {
switch prop.Type {
case property.TypePackage:
// this is already a json marshalled object, so it doesn't need to be marshalled
// like the other ones
props[property.TypePackage] = string(prop.Value)
}
}

imgValue, err := json.Marshal(bundle.Image)
if err != nil {
return nil, err
}
props[olmentity.PropertyBundlePath] = string(imgValue)

channelValue, _ := json.Marshal(property.Channel{ChannelName: ch.Name, Priority: 0})
props[property.TypeChannel] = string(channelValue)
entity := input.Entity{
ID: deppy.IdentifierFromString(fmt.Sprintf("%s%s%s", bundle.Name, bundle.Package.Name, ch.Name)),
Properties: props,
}
entities = append(entities, entity)
}
}
}

return entities, nil
}
2 changes: 1 addition & 1 deletion config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ rules:
- apiGroups:
- catalogd.operatorframework.io
resources:
- bundlemetadata
- catalogmetadata
verbs:
- list
- watch
Expand Down
2 changes: 1 addition & 1 deletion internal/controllers/operator_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,9 +58,9 @@ type OperatorReconciler struct {

//+kubebuilder:rbac:groups=core.rukpak.io,resources=bundledeployments,verbs=get;list;watch;create;update;patch

//+kubebuilder:rbac:groups=catalogd.operatorframework.io,resources=bundlemetadata,verbs=list;watch
//+kubebuilder:rbac:groups=catalogd.operatorframework.io,resources=packages,verbs=list;watch
//+kubebuilder:rbac:groups=catalogd.operatorframework.io,resources=catalogs,verbs=list;watch
//+kubebuilder:rbac:groups=catalogd.operatorframework.io,resources=catalogmetadata,verbs=list;watch

func (r *OperatorReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
l := log.FromContext(ctx).WithName("operator-controller")
Expand Down
161 changes: 108 additions & 53 deletions internal/resolution/entitysources/catalogdsource.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ import (
catalogd "github.com/operator-framework/catalogd/api/core/v1alpha1"
"github.com/operator-framework/deppy/pkg/deppy"
"github.com/operator-framework/deppy/pkg/deppy/input"
"github.com/operator-framework/operator-registry/alpha/declcfg"
"github.com/operator-framework/operator-registry/alpha/model"
"github.com/operator-framework/operator-registry/alpha/property"
"sigs.k8s.io/controller-runtime/pkg/client"

Expand Down Expand Up @@ -71,70 +73,123 @@ func (es *CatalogdEntitySource) Iterate(ctx context.Context, fn input.IteratorFu
return nil
}

func getEntities(ctx context.Context, client client.Client) (input.EntityList, error) {
entityList := input.EntityList{}
bundleMetadatas, packageMetdatas, err := fetchMetadata(ctx, client)
if err != nil {
func getEntities(ctx context.Context, cl client.Client) (input.EntityList, error) {
allEntitiesList := input.EntityList{}

var catalogList catalogd.CatalogList
if err := cl.List(ctx, &catalogList); err != nil {
return nil, err
}
for _, bundle := range bundleMetadatas.Items {
props := map[string]string{}

// TODO: We should make sure all properties are forwarded
// through and avoid a lossy translation from FBC --> entity
for _, prop := range bundle.Spec.Properties {
switch prop.Type {
case property.TypePackage:
// this is already a json marshalled object, so it doesn't need to be marshalled
// like the other ones
props[property.TypePackage] = string(prop.Value)
case entities.PropertyBundleMediaType:
props[entities.PropertyBundleMediaType] = string(prop.Value)
}
for _, catalog := range catalogList.Items {
model, err := fetchCatalogModel(ctx, cl, catalog.Name)
if err != nil {
return nil, err

Check warning on line 86 in internal/resolution/entitysources/catalogdsource.go

View check run for this annotation

Codecov / codecov/patch

internal/resolution/entitysources/catalogdsource.go#L86

Added line #L86 was not covered by tests
}

imgValue, err := json.Marshal(bundle.Spec.Image)
catalogEntitiesList, err := ModelToEntities(model, catalog.Name)
if err != nil {
return nil, err
}
props[entities.PropertyBundlePath] = string(imgValue)
catalogScopedPkgName := fmt.Sprintf("%s-%s", bundle.Spec.Catalog.Name, bundle.Spec.Package)
bundlePkg := packageMetdatas[catalogScopedPkgName]
for _, ch := range bundlePkg.Spec.Channels {
for _, b := range ch.Entries {
catalogScopedEntryName := fmt.Sprintf("%s-%s", bundle.Spec.Catalog.Name, b.Name)
if catalogScopedEntryName == bundle.Name {
channelValue, _ := json.Marshal(property.Channel{ChannelName: ch.Name, Priority: 0})
props[property.TypeChannel] = string(channelValue)
replacesValue, _ := json.Marshal(entities.ChannelEntry{
Name: b.Name,
Replaces: b.Replaces,
})
props[entities.PropertyBundleChannelEntry] = string(replacesValue)
entity := input.Entity{
ID: deppy.IdentifierFromString(fmt.Sprintf("%s%s%s", bundle.Name, bundle.Spec.Package, ch.Name)),
Properties: props,
}
entityList = append(entityList, entity)
}
}
}

allEntitiesList = append(allEntitiesList, catalogEntitiesList...)
}
return entityList, nil

return allEntitiesList, nil
}

func fetchMetadata(ctx context.Context, client client.Client) (catalogd.BundleMetadataList, map[string]catalogd.Package, error) {
packageMetdatas := catalogd.PackageList{}
if err := client.List(ctx, &packageMetdatas); err != nil {
return catalogd.BundleMetadataList{}, nil, err
func fetchCatalogModel(ctx context.Context, cl client.Client, catalogName string) (model.Model, error) {
packages, err := fetch[declcfg.Package](ctx, cl, declcfg.SchemaPackage, catalogName)
if err != nil {
return nil, err

Check warning on line 103 in internal/resolution/entitysources/catalogdsource.go

View check run for this annotation

Codecov / codecov/patch

internal/resolution/entitysources/catalogdsource.go#L103

Added line #L103 was not covered by tests
}
bundleMetadatas := catalogd.BundleMetadataList{}
if err := client.List(ctx, &bundleMetadatas); err != nil {
return catalogd.BundleMetadataList{}, nil, err
channels, err := fetch[declcfg.Channel](ctx, cl, declcfg.SchemaChannel, catalogName)
if err != nil {
return nil, err

Check warning on line 107 in internal/resolution/entitysources/catalogdsource.go

View check run for this annotation

Codecov / codecov/patch

internal/resolution/entitysources/catalogdsource.go#L107

Added line #L107 was not covered by tests
}
packages := map[string]catalogd.Package{}
for _, pkg := range packageMetdatas.Items {
packages[pkg.Name] = pkg
bundles, err := fetch[declcfg.Bundle](ctx, cl, declcfg.SchemaBundle, catalogName)
if err != nil {
return nil, err

Check warning on line 111 in internal/resolution/entitysources/catalogdsource.go

View check run for this annotation

Codecov / codecov/patch

internal/resolution/entitysources/catalogdsource.go#L111

Added line #L111 was not covered by tests
}

cfg := &declcfg.DeclarativeConfig{
Packages: packages,
Channels: channels,
Bundles: bundles,
}
return bundleMetadatas, packages, nil
model, err := declcfg.ConvertToModel(*cfg)
if err != nil {
return nil, err

Check warning on line 121 in internal/resolution/entitysources/catalogdsource.go

View check run for this annotation

Codecov / codecov/patch

internal/resolution/entitysources/catalogdsource.go#L121

Added line #L121 was not covered by tests
}

return model, nil
}

type declcfgSchemas interface {
declcfg.Package | declcfg.Bundle | declcfg.Channel
}

// TODO: Cleanup once https://github.com/golang/go/issues/45380 implemented
// We should be able to get rid of the schema arg and switch based on the type based to this generic
func fetch[T declcfgSchemas](ctx context.Context, cl client.Client, schema, catalogName string) ([]T, error) {
cmList := catalogd.CatalogMetadataList{}
if err := cl.List(ctx, &cmList, client.MatchingLabels{"schema": schema, "catalog": catalogName}); err != nil {
return nil, err

Check warning on line 136 in internal/resolution/entitysources/catalogdsource.go

View check run for this annotation

Codecov / codecov/patch

internal/resolution/entitysources/catalogdsource.go#L136

Added line #L136 was not covered by tests
}

contents := []T{}
for _, cm := range cmList.Items {
var content T
if err := json.Unmarshal(cm.Spec.Content, &content); err != nil {
return nil, err

Check warning on line 143 in internal/resolution/entitysources/catalogdsource.go

View check run for this annotation

Codecov / codecov/patch

internal/resolution/entitysources/catalogdsource.go#L143

Added line #L143 was not covered by tests
}
contents = append(contents, content)
}

return contents, nil
}

func ModelToEntities(model model.Model, catalogName string) (input.EntityList, error) {
entityList := input.EntityList{}

for _, pkg := range model {
for _, ch := range pkg.Channels {
for _, bundle := range ch.Bundles {
props := map[string]string{}

for _, prop := range bundle.Properties {
switch prop.Type {
case property.TypePackage:
// this is already a json marshalled object, so it doesn't need to be marshalled
// like the other ones
props[property.TypePackage] = string(prop.Value)
case entities.PropertyBundleMediaType:
props[entities.PropertyBundleMediaType] = string(prop.Value)
}
}

imgValue, err := json.Marshal(bundle.Image)
if err != nil {
return nil, err

Check warning on line 172 in internal/resolution/entitysources/catalogdsource.go

View check run for this annotation

Codecov / codecov/patch

internal/resolution/entitysources/catalogdsource.go#L172

Added line #L172 was not covered by tests
}
props[entities.PropertyBundlePath] = string(imgValue)

channelValue, _ := json.Marshal(property.Channel{ChannelName: ch.Name, Priority: 0})
props[property.TypeChannel] = string(channelValue)
replacesValue, _ := json.Marshal(entities.ChannelEntry{
Name: bundle.Name,
Replaces: bundle.Replaces,
})
props[entities.PropertyBundleChannelEntry] = string(replacesValue)

catalogScopedEntryName := fmt.Sprintf("%s-%s", catalogName, bundle.Name)
entity := input.Entity{
ID: deppy.IdentifierFromString(fmt.Sprintf("%s%s%s", catalogScopedEntryName, bundle.Package.Name, ch.Name)),
Properties: props,
}
entityList = append(entityList, entity)
}
}
}

return entityList, nil
}
7 changes: 6 additions & 1 deletion test/e2e/e2e_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,9 @@ var _ = AfterSuite(func() {
}).Should(Succeed())

// speed up delete without waiting for gc
Expect(c.DeleteAllOf(ctx, &catalogd.BundleMetadata{})).To(Succeed())
Expect(c.DeleteAllOf(ctx, &catalogd.Package{})).To(Succeed())
Expect(c.DeleteAllOf(ctx, &catalogd.BundleMetadata{})).To(Succeed())
Expect(c.DeleteAllOf(ctx, &catalogd.CatalogMetadata{})).To(Succeed())

Eventually(func(g Gomega) {
// ensure resource cleanup
Expand All @@ -107,6 +108,10 @@ var _ = AfterSuite(func() {
bmd := &catalogd.BundleMetadataList{}
g.Expect(c.List(ctx, bmd)).To(Succeed())
g.Expect(bmd.Items).To(BeEmpty())

cmd := &catalogd.CatalogMetadataList{}
g.Expect(c.List(ctx, cmd)).To(Succeed())
g.Expect(bmd.Items).To(BeEmpty())
}).Should(Succeed())
})

Expand Down

0 comments on commit 4a43091

Please sign in to comment.