Skip to content
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
3 changes: 2 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@

MOD_FLAGS := $(shell (go version | grep -q -E "1\.(11|12)") && echo -mod=vendor)
CMDS := $(addprefix bin/, $(shell ls ./cmd))
SPECIFIC_UNIT_TEST := $(if $(TEST),-run $(TEST),)

.PHONY: build test vendor clean

Expand All @@ -15,7 +16,7 @@ static: extra_flags=-ldflags '-w -extldflags "-static"'
static: build

unit:
go test $(MOD_FLAGS) -count=1 -v -race ./pkg/...
go test $(MOD_FLAGS) $(SPECIFIC_UNIT_TEST) -count=1 -v -race ./pkg/...

image:
docker build .
Expand Down
100 changes: 19 additions & 81 deletions pkg/configmap/configmap.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,14 @@ package configmap
import (
"errors"
"fmt"
libbundle "github.com/operator-framework/operator-registry/pkg/lib/bundle"
"github.com/operator-framework/operator-registry/pkg/registry"
"strings"

"github.com/sirupsen/logrus"
corev1 "k8s.io/api/core/v1"
"strings"

"github.com/operator-framework/operator-registry/pkg/api"
"github.com/operator-framework/operator-registry/pkg/registry"
"github.com/operator-framework/operator-registry/pkg/sqlite"
)

func NewBundleLoader() *BundleLoader {
Expand All @@ -21,12 +24,6 @@ func NewBundleLoaderWithLogger(logger *logrus.Entry) *BundleLoader {
}
}

// Manifest contains a bundle and a PackageManifest.
type Manifest struct {
Bundle *registry.Bundle
PackageManifest *registry.PackageManifest
}

type BundleLoader struct {
logger *logrus.Entry
}
Expand All @@ -35,7 +32,7 @@ type BundleLoader struct {
// creates an operator registry Bundle object.
// If the Data section has a PackageManifest resource then it is also
// deserialized and included in the result.
func (l *BundleLoader) Load(cm *corev1.ConfigMap) (manifest *Manifest, err error) {
func (l *BundleLoader) Load(cm *corev1.ConfigMap) (bundle *api.Bundle, err error) {
if cm == nil {
err = errors.New("ConfigMap must not be <nil>")
return
Expand All @@ -45,48 +42,17 @@ func (l *BundleLoader) Load(cm *corev1.ConfigMap) (manifest *Manifest, err error
"configmap": fmt.Sprintf("%s/%s", cm.GetNamespace(), cm.GetName()),
})

bundle, _, bundleErr := loadBundle(logger, cm.Data)
bundle, skipped, bundleErr := loadBundle(logger, cm.Data)
if bundleErr != nil {
err = fmt.Errorf("failed to extract bundle from configmap - %v", bundleErr)
return
}

// get package manifest information from required annotations
annotations := cm.GetAnnotations()
if len(annotations) == 0 {
err = fmt.Errorf("missing required annoations on configmap %v", cm.GetName())
return
}

switch mediatype := annotations[libbundle.MediatypeLabel]; mediatype {
case "registry+v1":
// supported, proceed
default:
err = fmt.Errorf("failed to parse annotations due to unsupported media type %v", mediatype)
return
}

var packageChannels []registry.PackageChannel
channels := strings.Split(annotations[libbundle.ChannelsLabel], ",")
for _, channel := range channels {
packageChannels = append(packageChannels, registry.PackageChannel{
Name: channel,
})
}

manifest = &Manifest{
Bundle: bundle,
PackageManifest: &registry.PackageManifest{
PackageName: annotations[libbundle.PackageLabel],
Channels: packageChannels,
DefaultChannelName: annotations[libbundle.ChannelDefaultLabel],
},
}
l.logger.Debugf("couldn't unpack skipped: %#v", skipped)
return
}

func loadBundle(entry *logrus.Entry, data map[string]string) (bundle *registry.Bundle, skipped map[string]string, err error) {
bundle = &registry.Bundle{}
func loadBundle(entry *logrus.Entry, data map[string]string) (bundle *api.Bundle, skipped map[string]string, err error) {
bundle = &api.Bundle{Object: []string{}}
skipped = map[string]string{}

// Add kube resources to the bundle.
Expand All @@ -106,44 +72,16 @@ func loadBundle(entry *logrus.Entry, data map[string]string) (bundle *registry.B
continue
}

// It's a valid kube resource,
// could be a crd, csv or other raw kube manifest(s).
bundle.Add(resource)
if resource.GetKind() == sqlite.ClusterServiceVersionKind {
csvBytes, err := resource.MarshalJSON()
if err != nil {
return nil, nil, err
}
bundle.CsvJson = string(csvBytes)
}
bundle.Object = append(bundle.Object, content)
logger.Infof("added to bundle, Kind=%s", resource.GetKind())
}

return
}

func loadPackageManifest(entry *logrus.Entry, resources map[string]string) *registry.PackageManifest {
// Let's inspect if any of the skipped non kube resources is a PackageManifest type.
// The first one we run into will be selected.
for name, content := range resources {
logger := entry.WithFields(logrus.Fields{
"key": name,
})

// Is it a package yaml file?
reader := strings.NewReader(content)
packageManifest, decodeErr := registry.DecodePackageManifest(reader)
if decodeErr != nil {
logger.Infof("skipping, not a PackageManifest type - %v", decodeErr)
continue
}

logger.Infof("found a PackageManifest type resource - packageName=%s", packageManifest.PackageName)

return packageManifest
}

return nil
}

func extract(data map[string]string) []string {
resources := make([]string, 0)
for _, v := range data {
resources = append(resources, v)
}

return resources
}
88 changes: 40 additions & 48 deletions pkg/configmap/configmap_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,94 +2,78 @@ package configmap

import (
"os"
"strings"
"testing"

"github.com/operator-framework/operator-registry/pkg/api"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
"k8s.io/apimachinery/pkg/util/yaml"
)

func TestLoad(t *testing.T) {
tests := []struct {
name string
source string
assertFunc func(t *testing.T, manifestGot *Manifest)
assertFunc func(t *testing.T, bundleGot *api.Bundle)
}{
{
name: "BundleWithCsvAndCrd",
source: "testdata/bundle.cm.yaml",
assertFunc: func(t *testing.T, manifestGot *Manifest) {
assert.NotNil(t, manifestGot.Bundle)
assert.NotNil(t, manifestGot.PackageManifest)

csvGot, errGot := manifestGot.Bundle.ClusterServiceVersion()
assert.NoError(t, errGot)
assertFunc: func(t *testing.T, bundleGot *api.Bundle) {
csvGot := bundleGot.GetCsvJson()
assert.NotNil(t, csvGot)

crdListGot, errGot := manifestGot.Bundle.CustomResourceDefinitions()
assert.NoError(t, errGot)
assert.Equal(t, 1, len(crdListGot))
crdListGot := bundleGot.GetObject()
// 1 CSV + 1 CRD = 2 objects
assert.Equal(t, 2, len(crdListGot))
},
},
{
name: "BundleWithBuiltInKubeTypes",
source: "testdata/bundle-with-kube-resources.cm.yaml",
assertFunc: func(t *testing.T, manifestGot *Manifest) {
assert.NotNil(t, manifestGot.Bundle)
assert.NotNil(t, manifestGot.Bundle.Objects)

objects := manifestGot.Bundle.Objects
assertFunc: func(t *testing.T, bundleGot *api.Bundle) {
objects := bundleGot.GetObject()
assert.NotNil(t, objects)
assert.Equal(t, 1, len(objects))
assert.True(t, objects[0].GetKind() == "Foo")

unst := getUnstructured(t, objects[0])
assert.True(t, unst.GetKind() == "Foo")
},
},
{
name: "BundleWithMultipleCsvs",
source: "testdata/bundle-with-multiple-csvs.cm.yaml",
assertFunc: func(t *testing.T, manifestGot *Manifest) {
assert.NotNil(t, manifestGot.Bundle)

csvGot, errGot := manifestGot.Bundle.ClusterServiceVersion()
assert.NoError(t, errGot)
assertFunc: func(t *testing.T, bundleGot *api.Bundle) {
csvGot := bundleGot.GetCsvJson()
assert.NotNil(t, csvGot)
assert.True(t, csvGot.GetName() == "first" || csvGot.GetName() == "second")

unst := getUnstructured(t, csvGot)
assert.True(t, unst.GetName() == "first" || unst.GetName() == "second")
},
},
{
name: "BundleWithBadResource",
source: "testdata/bundle-with-bad-resource.cm.yaml",
assertFunc: func(t *testing.T, manifestGot *Manifest) {
assert.NotNil(t, manifestGot.Bundle)

csvGot, errGot := manifestGot.Bundle.ClusterServiceVersion()
assert.NoError(t, errGot)
assertFunc: func(t *testing.T, bundleGot *api.Bundle) {
csvGot := bundleGot.GetCsvJson()
assert.NotNil(t, csvGot)
},
},
{
name: "BundleWithAll",
source: "testdata/bundle-with-all.yaml",
assertFunc: func(t *testing.T, manifestGot *Manifest) {
assert.NotNil(t, manifestGot.Bundle)
assert.NotNil(t, manifestGot.PackageManifest)

csvGot, errGot := manifestGot.Bundle.ClusterServiceVersion()
assert.NoError(t, errGot)
assertFunc: func(t *testing.T, bundleGot *api.Bundle) {
csvGot := bundleGot.GetCsvJson()
assert.NotNil(t, csvGot)
assert.True(t, csvGot.GetName() == "kiali-operator.v1.4.2")

crdListGot, errGot := manifestGot.Bundle.CustomResourceDefinitions()
assert.NoError(t, errGot)
assert.Equal(t, 2, len(crdListGot))
unst := getUnstructured(t, csvGot)
assert.True(t, unst.GetName() == "kiali-operator.v1.4.2")

providedAPIList, errGot := manifestGot.Bundle.ProvidedAPIs()
assert.NoError(t, errGot)
assert.Equal(t, 2, len(providedAPIList))

requiredAPIList, errGot := manifestGot.Bundle.RequiredAPIs()
assert.NoError(t, errGot)
assert.Equal(t, 0, len(requiredAPIList))
objects := bundleGot.GetObject()
// 2 CRDs + 1 CSV == 3 objects
assert.Equal(t, 3, len(objects))
},
},
}
Expand All @@ -99,13 +83,13 @@ func TestLoad(t *testing.T) {
cm := loadfromFile(t, tt.source)

loader := NewBundleLoader()
manifestGot, errGot := loader.Load(cm)
bundleGot, errGot := loader.Load(cm)

assert.NoError(t, errGot)
assert.NotNil(t, manifestGot)
assert.NotNil(t, bundleGot)

if tt.assertFunc != nil {
tt.assertFunc(t, manifestGot)
tt.assertFunc(t, bundleGot)
}
})
}
Expand All @@ -122,3 +106,11 @@ func loadfromFile(t *testing.T, path string) *corev1.ConfigMap {

return bundle
}

func getUnstructured(t *testing.T, str string) *unstructured.Unstructured {
dec := yaml.NewYAMLOrJSONDecoder(strings.NewReader(str), 1)
unst := &unstructured.Unstructured{}
err := dec.Decode(unst)
assert.NoError(t, err)
return unst
}