Skip to content

Commit

Permalink
add support for handling multiple bundle types
Browse files Browse the repository at this point in the history
Signed-off-by: Bryce Palmer <bpalmer@redhat.com>
  • Loading branch information
everettraven committed May 31, 2023
1 parent b982ad0 commit e53ddd1
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 4 deletions.
31 changes: 27 additions & 4 deletions internal/controllers/operator_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -152,9 +152,19 @@ func (r *OperatorReconciler) reconcile(ctx context.Context, op *operatorsv1alpha
op.Status.ResolvedBundleResource = bundleImage
setResolvedStatusConditionSuccess(&op.Status.Conditions, fmt.Sprintf("resolved to %q", bundleImage), op.GetGeneration())

mediaType, err := bundleEntity.MediaType()
if err != nil {
setInstalledStatusConditionFailed(&op.Status.Conditions, err.Error(), op.GetGeneration())
return ctrl.Result{}, err
}
bundleProvisioner, err := mapBundleMediaTypeToBundleProvisioner(mediaType)
if err != nil {
setInstalledStatusConditionFailed(&op.Status.Conditions, err.Error(), op.GetGeneration())
return ctrl.Result{}, err
}
// Ensure a BundleDeployment exists with its bundle source from the bundle
// image we just looked up in the solution.
dep := r.generateExpectedBundleDeployment(*op, bundleImage)
dep := r.generateExpectedBundleDeployment(*op, bundleImage, bundleProvisioner)
if err := r.ensureBundleDeployment(ctx, dep); err != nil {
// originally Reason: operatorsv1alpha1.ReasonInstallationFailed
op.Status.InstalledBundleResource = ""
Expand Down Expand Up @@ -240,12 +250,13 @@ func (r *OperatorReconciler) getBundleEntityFromSolution(solution *solver.Soluti
return nil, fmt.Errorf("entity for package %q not found in solution", packageName)
}

func (r *OperatorReconciler) generateExpectedBundleDeployment(o operatorsv1alpha1.Operator, bundlePath string) *unstructured.Unstructured {
func (r *OperatorReconciler) generateExpectedBundleDeployment(o operatorsv1alpha1.Operator, bundlePath string, bundleProvisioner string) *unstructured.Unstructured {
// We use unstructured here to avoid problems of serializing default values when sending patches to the apiserver.
// If you use a typed object, any default values from that struct get serialized into the JSON patch, which could
// cause unrelated fields to be patched back to the default value even though that isn't the intention. Using an
// unstructured ensures that the patch contains only what is specified. Using unstructured like this is basically
// identical to "kubectl apply -f"

bd := &unstructured.Unstructured{Object: map[string]interface{}{
"apiVersion": rukpakv1alpha1.GroupVersion.String(),
"kind": rukpakv1alpha1.BundleDeploymentKind,
Expand All @@ -257,8 +268,7 @@ func (r *OperatorReconciler) generateExpectedBundleDeployment(o operatorsv1alpha
"provisionerClassName": "core-rukpak-io-plain",
"template": map[string]interface{}{
"spec": map[string]interface{}{
// TODO: Don't assume registry provisioner
"provisionerClassName": "core-rukpak-io-registry",
"provisionerClassName": bundleProvisioner,
"source": map[string]interface{}{
// TODO: Don't assume image type
"type": string(rukpakv1alpha1.SourceTypeImage),
Expand Down Expand Up @@ -357,6 +367,19 @@ func isBundleDepStale(bd *rukpakv1alpha1.BundleDeployment) bool {
return bd != nil && bd.Status.ObservedGeneration != bd.GetGeneration()
}

// mapBundleMediaTypeToBundleProvisioner maps an olm.bundle.mediatype property to a
// rukpak bundle provisioner class name that is capable of unpacking the bundle type
func mapBundleMediaTypeToBundleProvisioner(mediaType string) (string, error) {
switch mediaType {
case entity.MediaTypePlain:
return "core-rukpak-io-plain", nil
case entity.MediaTypeRegistry, "":
return "core-rukpak-io-registry", nil
default:
return "", fmt.Errorf("unknown bundle mediatype: %s", mediaType)
}
}

// setResolvedStatusConditionSuccess sets the resolved status condition to success.
func setResolvedStatusConditionSuccess(conditions *[]metav1.Condition, message string, generation int64) {
apimeta.SetStatusCondition(conditions, metav1.Condition{
Expand Down
30 changes: 30 additions & 0 deletions internal/resolution/variable_sources/entity/bundle_entity.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,14 @@ import (
)

const PropertyBundlePath = "olm.bundle.path"
const PropertyBundleMediaType = "olm.bundle.mediatype"

type MediaType string

const (
MediaTypePlain = "plain+v0"
MediaTypeRegistry = "registry+v1"
)

type ChannelProperties struct {
property.Channel
Expand Down Expand Up @@ -58,6 +66,7 @@ type BundleEntity struct {
channelProperties *ChannelProperties
semVersion *semver.Version
bundlePath string
mediaType string
mu sync.RWMutex
}

Expand Down Expand Up @@ -124,6 +133,27 @@ func (b *BundleEntity) BundlePath() (string, error) {
return b.bundlePath, nil
}

func (b *BundleEntity) MediaType() (string, error) {
if err := b.loadMediaType(); err != nil {
return "", err
}

return b.mediaType, nil
}

func (b *BundleEntity) loadMediaType() error {
b.mu.Lock()
defer b.mu.Unlock()
if b.mediaType == "" {
mediaType, err := loadFromEntity[string](b.Entity, PropertyBundleMediaType, optional)
if err != nil {
return fmt.Errorf("error determining bundle mediatype for entity '%s': %w", b.ID, err)
}
b.mediaType = mediaType
}
return nil
}

func (b *BundleEntity) loadPackage() error {
b.mu.Lock()
defer b.mu.Unlock()
Expand Down
29 changes: 29 additions & 0 deletions internal/resolution/variable_sources/entity/bundle_entity_test.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package entity_test

import (
"fmt"
"testing"

"github.com/blang/semver/v4"
Expand Down Expand Up @@ -267,4 +268,32 @@ var _ = Describe("BundleEntity", func() {
Expect(err.Error()).To(Equal("error determining bundle path for entity 'operatorhub/prometheus/0.14.0': property 'olm.bundle.path' ('badBundlePath') could not be parsed: invalid character 'b' looking for beginning of value"))
})
})

Describe("MediaType", func() {
It("should return the bundle mediatype property if present", func() {
entity := input.NewEntity("operatorhub/prometheus/0.14.0", map[string]string{
olmentity.PropertyBundleMediaType: fmt.Sprintf(`"%s"`, olmentity.MediaTypePlain),
})
bundleEntity := olmentity.NewBundleEntity(entity)
mediaType, err := bundleEntity.MediaType()
Expect(err).ToNot(HaveOccurred())
Expect(mediaType).To(Equal(olmentity.MediaTypePlain))
})
It("should not return an error if the property is not found", func() {
entity := input.NewEntity("operatorhub/prometheus/0.14.0", map[string]string{})
bundleEntity := olmentity.NewBundleEntity(entity)
mediaType, err := bundleEntity.MediaType()
Expect(mediaType).To(BeEmpty())
Expect(err).To(BeNil())
})
It("should return error if the property is malformed", func() {
entity := input.NewEntity("operatorhub/prometheus/0.14.0", map[string]string{
olmentity.PropertyBundleMediaType: "badtype",
})
bundleEntity := olmentity.NewBundleEntity(entity)
mediaType, err := bundleEntity.MediaType()
Expect(mediaType).To(BeEmpty())
Expect(err.Error()).To(Equal("error determining bundle mediatype for entity 'operatorhub/prometheus/0.14.0': property 'olm.bundle.mediatype' ('badtype') could not be parsed: invalid character 'b' looking for beginning of value"))
})
})
})

0 comments on commit e53ddd1

Please sign in to comment.