Skip to content

Commit a5ce6e8

Browse files
committed
add support for handling multiple bundle types
Signed-off-by: Bryce Palmer <bpalmer@redhat.com>
1 parent 2ae839f commit a5ce6e8

File tree

3 files changed

+86
-4
lines changed

3 files changed

+86
-4
lines changed

internal/controllers/operator_controller.go

Lines changed: 27 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -157,9 +157,19 @@ func (r *OperatorReconciler) reconcile(ctx context.Context, op *operatorsv1alpha
157157
op.Status.ResolvedBundleResource = bundleImage
158158
setResolvedStatusConditionSuccess(&op.Status.Conditions, fmt.Sprintf("resolved to %q", bundleImage), op.GetGeneration())
159159

160+
mediaType, err := bundleEntity.MediaType()
161+
if err != nil {
162+
setInstalledStatusConditionFailed(&op.Status.Conditions, err.Error(), op.GetGeneration())
163+
return ctrl.Result{}, err
164+
}
165+
bundleProvisioner, err := mapBundleMediaTypeToBundleProvisioner(mediaType)
166+
if err != nil {
167+
setInstalledStatusConditionFailed(&op.Status.Conditions, err.Error(), op.GetGeneration())
168+
return ctrl.Result{}, err
169+
}
160170
// Ensure a BundleDeployment exists with its bundle source from the bundle
161171
// image we just looked up in the solution.
162-
dep := r.generateExpectedBundleDeployment(*op, bundleImage)
172+
dep := r.generateExpectedBundleDeployment(*op, bundleImage, bundleProvisioner)
163173
if err := r.ensureBundleDeployment(ctx, dep); err != nil {
164174
// originally Reason: operatorsv1alpha1.ReasonInstallationFailed
165175
op.Status.InstalledBundleResource = ""
@@ -245,12 +255,13 @@ func (r *OperatorReconciler) getBundleEntityFromSolution(solution *solver.Soluti
245255
return nil, fmt.Errorf("entity for package %q not found in solution", packageName)
246256
}
247257

248-
func (r *OperatorReconciler) generateExpectedBundleDeployment(o operatorsv1alpha1.Operator, bundlePath string) *unstructured.Unstructured {
258+
func (r *OperatorReconciler) generateExpectedBundleDeployment(o operatorsv1alpha1.Operator, bundlePath string, bundleProvisioner string) *unstructured.Unstructured {
249259
// We use unstructured here to avoid problems of serializing default values when sending patches to the apiserver.
250260
// If you use a typed object, any default values from that struct get serialized into the JSON patch, which could
251261
// cause unrelated fields to be patched back to the default value even though that isn't the intention. Using an
252262
// unstructured ensures that the patch contains only what is specified. Using unstructured like this is basically
253263
// identical to "kubectl apply -f"
264+
254265
bd := &unstructured.Unstructured{Object: map[string]interface{}{
255266
"apiVersion": rukpakv1alpha1.GroupVersion.String(),
256267
"kind": rukpakv1alpha1.BundleDeploymentKind,
@@ -262,8 +273,7 @@ func (r *OperatorReconciler) generateExpectedBundleDeployment(o operatorsv1alpha
262273
"provisionerClassName": "core-rukpak-io-plain",
263274
"template": map[string]interface{}{
264275
"spec": map[string]interface{}{
265-
// TODO: Don't assume registry provisioner
266-
"provisionerClassName": "core-rukpak-io-registry",
276+
"provisionerClassName": bundleProvisioner,
267277
"source": map[string]interface{}{
268278
// TODO: Don't assume image type
269279
"type": string(rukpakv1alpha1.SourceTypeImage),
@@ -364,6 +374,19 @@ func isBundleDepStale(bd *rukpakv1alpha1.BundleDeployment) bool {
364374
return bd != nil && bd.Status.ObservedGeneration != bd.GetGeneration()
365375
}
366376

377+
// mapBundleMediaTypeToBundleProvisioner maps an olm.bundle.mediatype property to a
378+
// rukpak bundle provisioner class name that is capable of unpacking the bundle type
379+
func mapBundleMediaTypeToBundleProvisioner(mediaType string) (string, error) {
380+
switch mediaType {
381+
case entity.MediaTypePlain:
382+
return "core-rukpak-io-plain", nil
383+
case entity.MediaTypeRegistry, "":
384+
return "core-rukpak-io-registry", nil
385+
default:
386+
return "", fmt.Errorf("unknown bundle mediatype: %s", mediaType)
387+
}
388+
}
389+
367390
// setResolvedStatusConditionSuccess sets the resolved status condition to success.
368391
func setResolvedStatusConditionSuccess(conditions *[]metav1.Condition, message string, generation int64) {
369392
apimeta.SetStatusCondition(conditions, metav1.Condition{

internal/resolution/variable_sources/entity/bundle_entity.go

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,14 @@ import (
1111
)
1212

1313
const PropertyBundlePath = "olm.bundle.path"
14+
const PropertyBundleMediaType = "olm.bundle.mediatype"
15+
16+
type MediaType string
17+
18+
const (
19+
MediaTypePlain = "plain+v0"
20+
MediaTypeRegistry = "registry+v1"
21+
)
1422

1523
type ChannelProperties struct {
1624
property.Channel
@@ -58,6 +66,7 @@ type BundleEntity struct {
5866
channelProperties *ChannelProperties
5967
semVersion *semver.Version
6068
bundlePath string
69+
mediaType string
6170
mu sync.RWMutex
6271
}
6372

@@ -124,6 +133,27 @@ func (b *BundleEntity) BundlePath() (string, error) {
124133
return b.bundlePath, nil
125134
}
126135

136+
func (b *BundleEntity) MediaType() (string, error) {
137+
if err := b.loadMediaType(); err != nil {
138+
return "", err
139+
}
140+
141+
return b.mediaType, nil
142+
}
143+
144+
func (b *BundleEntity) loadMediaType() error {
145+
b.mu.Lock()
146+
defer b.mu.Unlock()
147+
if b.mediaType == "" {
148+
mediaType, err := loadFromEntity[string](b.Entity, PropertyBundleMediaType, optional)
149+
if err != nil {
150+
return fmt.Errorf("error determining bundle mediatype for entity '%s': %w", b.ID, err)
151+
}
152+
b.mediaType = mediaType
153+
}
154+
return nil
155+
}
156+
127157
func (b *BundleEntity) loadPackage() error {
128158
b.mu.Lock()
129159
defer b.mu.Unlock()

internal/resolution/variable_sources/entity/bundle_entity_test.go

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package entity_test
22

33
import (
4+
"fmt"
45
"testing"
56

67
"github.com/blang/semver/v4"
@@ -267,4 +268,32 @@ var _ = Describe("BundleEntity", func() {
267268
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"))
268269
})
269270
})
271+
272+
Describe("MediaType", func() {
273+
It("should return the bundle mediatype property if present", func() {
274+
entity := input.NewEntity("operatorhub/prometheus/0.14.0", map[string]string{
275+
olmentity.PropertyBundleMediaType: fmt.Sprintf(`"%s"`, olmentity.MediaTypePlain),
276+
})
277+
bundleEntity := olmentity.NewBundleEntity(entity)
278+
mediaType, err := bundleEntity.MediaType()
279+
Expect(err).ToNot(HaveOccurred())
280+
Expect(mediaType).To(Equal(olmentity.MediaTypePlain))
281+
})
282+
It("should not return an error if the property is not found", func() {
283+
entity := input.NewEntity("operatorhub/prometheus/0.14.0", map[string]string{})
284+
bundleEntity := olmentity.NewBundleEntity(entity)
285+
mediaType, err := bundleEntity.MediaType()
286+
Expect(mediaType).To(BeEmpty())
287+
Expect(err).To(BeNil())
288+
})
289+
It("should return error if the property is malformed", func() {
290+
entity := input.NewEntity("operatorhub/prometheus/0.14.0", map[string]string{
291+
olmentity.PropertyBundleMediaType: "badtype",
292+
})
293+
bundleEntity := olmentity.NewBundleEntity(entity)
294+
mediaType, err := bundleEntity.MediaType()
295+
Expect(mediaType).To(BeEmpty())
296+
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"))
297+
})
298+
})
270299
})

0 commit comments

Comments
 (0)