Skip to content

Commit e617310

Browse files
perdasilvaPer Goncalves da Silva
andauthored
Add bundle olm.properties to revision annotations (#2471)
Signed-off-by: Per Goncalves da Silva <pegoncal@redhat.com> Co-authored-by: Per Goncalves da Silva <pegoncal@redhat.com>
1 parent 2bdb702 commit e617310

File tree

7 files changed

+189
-14
lines changed

7 files changed

+189
-14
lines changed

internal/operator-controller/applier/boxcutter.go

Lines changed: 22 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ import (
3333
ocv1 "github.com/operator-framework/operator-controller/api/v1"
3434
"github.com/operator-framework/operator-controller/internal/operator-controller/authorization"
3535
"github.com/operator-framework/operator-controller/internal/operator-controller/labels"
36+
"github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/bundle/source"
3637
"github.com/operator-framework/operator-controller/internal/shared/util/cache"
3738
)
3839

@@ -106,6 +107,27 @@ func (r *SimpleRevisionGenerator) GenerateRevision(
106107
return nil, err
107108
}
108109

110+
if revisionAnnotations == nil {
111+
revisionAnnotations = map[string]string{}
112+
}
113+
114+
// add bundle properties of interest to revision annotations
115+
bundleAnnotations, err := getBundleAnnotations(bundleFS)
116+
if err != nil {
117+
return nil, fmt.Errorf("error getting bundle annotations: %w", err)
118+
}
119+
120+
// we don't care about all of the bundle and csv annotations as they can be quite confusing
121+
// e.g. 'createdAt', 'capabilities', etc.
122+
for _, key := range []string{
123+
// used by other operators that care about the bundle properties (e.g. maxOpenShiftVersion)
124+
source.PropertyOLMProperties,
125+
} {
126+
if value, ok := bundleAnnotations[key]; ok {
127+
revisionAnnotations[key] = value
128+
}
129+
}
130+
109131
// objectLabels
110132
objs := make([]ocv1.ClusterExtensionRevisionObject, 0, len(plain))
111133
for _, obj := range plain {
@@ -137,11 +159,6 @@ func (r *SimpleRevisionGenerator) GenerateRevision(
137159
Object: unstr,
138160
})
139161
}
140-
141-
if revisionAnnotations == nil {
142-
revisionAnnotations = map[string]string{}
143-
}
144-
145162
return r.buildClusterExtensionRevision(objs, ext, revisionAnnotations), nil
146163
}
147164

internal/operator-controller/applier/boxcutter_test.go

Lines changed: 95 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,15 @@ import (
3636
"github.com/operator-framework/operator-controller/internal/operator-controller/applier"
3737
"github.com/operator-framework/operator-controller/internal/operator-controller/authorization"
3838
"github.com/operator-framework/operator-controller/internal/operator-controller/labels"
39+
"github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/util/testing/bundlefs"
40+
"github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/util/testing/clusterserviceversion"
41+
)
42+
43+
var (
44+
dummyBundle = bundlefs.Builder().
45+
WithPackageName("test-package").
46+
WithCSV(clusterserviceversion.Builder().WithName("test-csv").Build()).
47+
Build()
3948
)
4049

4150
func Test_SimpleRevisionGenerator_GenerateRevisionFromHelmRelease(t *testing.T) {
@@ -195,7 +204,7 @@ func Test_SimpleRevisionGenerator_GenerateRevision(t *testing.T) {
195204
},
196205
}
197206

198-
rev, err := b.GenerateRevision(t.Context(), fstest.MapFS{}, ext, map[string]string{}, map[string]string{})
207+
rev, err := b.GenerateRevision(t.Context(), dummyBundle, ext, map[string]string{}, map[string]string{})
199208
require.NoError(t, err)
200209

201210
t.Log("by checking the olm.operatorframework.io/owner-name label is set to the name of the ClusterExtension")
@@ -254,8 +263,88 @@ func Test_SimpleRevisionGenerator_GenerateRevision(t *testing.T) {
254263
}, rev.Spec.Phases)
255264
}
256265

266+
func Test_SimpleRevisionGenerator_GenerateRevision_BundleAnnotations(t *testing.T) {
267+
r := &FakeManifestProvider{
268+
GetFn: func(_ fs.FS, _ *ocv1.ClusterExtension) ([]client.Object, error) {
269+
return []client.Object{}, nil
270+
},
271+
}
272+
273+
b := applier.SimpleRevisionGenerator{
274+
Scheme: k8scheme.Scheme,
275+
ManifestProvider: r,
276+
}
277+
278+
ext := &ocv1.ClusterExtension{
279+
ObjectMeta: metav1.ObjectMeta{
280+
Name: "test-extension",
281+
},
282+
Spec: ocv1.ClusterExtensionSpec{
283+
Namespace: "test-namespace",
284+
ServiceAccount: ocv1.ServiceAccountReference{
285+
Name: "test-sa",
286+
},
287+
},
288+
}
289+
290+
t.Run("bundle properties are copied to the olm.properties annotation", func(t *testing.T) {
291+
bundleFS := bundlefs.Builder().
292+
WithPackageName("test-package").
293+
WithBundleProperty("olm.bundle.property", "some-value").
294+
WithBundleProperty("olm.another.bundle.property", "some-other-value").
295+
WithCSV(clusterserviceversion.Builder().WithName("test-csv").Build()).
296+
Build()
297+
298+
rev, err := b.GenerateRevision(t.Context(), bundleFS, ext, map[string]string{}, map[string]string{})
299+
require.NoError(t, err)
300+
301+
t.Log("by checking bundle properties are added to the revision annotations")
302+
require.NotNil(t, rev.Annotations)
303+
require.JSONEq(t, `[{"type":"olm.bundle.property","value":"some-value"},{"type":"olm.another.bundle.property","value":"some-other-value"}]`, rev.Annotations["olm.properties"])
304+
})
305+
306+
t.Run("olm.properties should not be present if there are no bundle properties", func(t *testing.T) {
307+
bundleFS := bundlefs.Builder().
308+
WithPackageName("test-package").
309+
WithCSV(clusterserviceversion.Builder().WithName("test-csv").Build()).
310+
Build()
311+
312+
rev, err := b.GenerateRevision(t.Context(), bundleFS, ext, map[string]string{}, map[string]string{})
313+
require.NoError(t, err)
314+
315+
t.Log("by checking olm.properties is not present in the revision annotations")
316+
_, ok := rev.Annotations["olm.properties"]
317+
require.False(t, ok, "olm.properties should not be present in the revision annotations")
318+
})
319+
320+
t.Run("csv annotations are not added to the revision annotations", func(t *testing.T) {
321+
bundleFS := bundlefs.Builder().
322+
WithPackageName("test-package").
323+
WithBundleProperty("olm.bundle.property", "some-value").
324+
WithCSV(clusterserviceversion.Builder().
325+
WithName("test-csv").
326+
WithAnnotations(map[string]string{
327+
"some.csv.annotation": "some-other-value",
328+
}).
329+
Build()).
330+
Build()
331+
332+
rev, err := b.GenerateRevision(t.Context(), bundleFS, ext, map[string]string{}, map[string]string{})
333+
require.NoError(t, err)
334+
335+
t.Log("by checking csv annotations are not added to the revision annotations")
336+
_, ok := rev.Annotations["olm.csv.annotation"]
337+
require.False(t, ok, "csv annotation should not be present in the revision annotations")
338+
})
339+
340+
t.Run("errors getting bundle properties are surfaced", func(t *testing.T) {
341+
_, err := b.GenerateRevision(t.Context(), fstest.MapFS{}, ext, map[string]string{}, map[string]string{})
342+
require.Error(t, err)
343+
require.Contains(t, err.Error(), "metadata/annotations.yaml: file does not exist")
344+
})
345+
}
346+
257347
func Test_SimpleRevisionGenerator_Renderer_Integration(t *testing.T) {
258-
bundleFS := fstest.MapFS{}
259348
ext := &ocv1.ClusterExtension{
260349
ObjectMeta: metav1.ObjectMeta{
261350
Name: "test-extension",
@@ -264,7 +353,7 @@ func Test_SimpleRevisionGenerator_Renderer_Integration(t *testing.T) {
264353
r := &FakeManifestProvider{
265354
GetFn: func(b fs.FS, e *ocv1.ClusterExtension) ([]client.Object, error) {
266355
t.Log("by checking renderer was called with the correct parameters")
267-
require.Equal(t, bundleFS, b)
356+
require.Equal(t, dummyBundle, b)
268357
require.Equal(t, ext, e)
269358
return nil, nil
270359
},
@@ -274,7 +363,7 @@ func Test_SimpleRevisionGenerator_Renderer_Integration(t *testing.T) {
274363
ManifestProvider: r,
275364
}
276365

277-
_, err := b.GenerateRevision(t.Context(), bundleFS, ext, map[string]string{}, map[string]string{})
366+
_, err := b.GenerateRevision(t.Context(), dummyBundle, ext, map[string]string{}, map[string]string{})
278367
require.NoError(t, err)
279368
}
280369

@@ -312,7 +401,7 @@ func Test_SimpleRevisionGenerator_AppliesObjectLabelsAndRevisionAnnotations(t *t
312401
"other": "value",
313402
}
314403

315-
rev, err := b.GenerateRevision(t.Context(), fstest.MapFS{}, &ocv1.ClusterExtension{
404+
rev, err := b.GenerateRevision(t.Context(), dummyBundle, &ocv1.ClusterExtension{
316405
Spec: ocv1.ClusterExtensionSpec{
317406
Namespace: "test-namespace",
318407
ServiceAccount: ocv1.ServiceAccountReference{Name: "test-sa"},
@@ -386,7 +475,7 @@ func Test_SimpleRevisionGenerator_PropagatesProgressDeadlineMinutes(t *testing.T
386475
ext.Spec.ProgressDeadlineMinutes = *pd
387476
}
388477

389-
rev, err := b.GenerateRevision(t.Context(), fstest.MapFS{}, ext, empty, empty)
478+
rev, err := b.GenerateRevision(t.Context(), dummyBundle, ext, empty, empty)
390479
require.NoError(t, err)
391480
require.Equal(t, tc.want.progressDeadlineMinutes, rev.Spec.ProgressDeadlineMinutes)
392481
})

internal/operator-controller/applier/provider.go

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,7 +85,6 @@ func (r *RegistryV1ManifestProvider) Get(bundleFS fs.FS, ext *ocv1.ClusterExtens
8585
opts = append(opts, render.WithTargetNamespaces(*watchNS))
8686
}
8787
}
88-
8988
return r.BundleRenderer.Render(rv1, ext.Spec.Namespace, opts...)
9089
}
9190

@@ -102,7 +101,7 @@ func (r *RegistryV1HelmChartProvider) Get(bundleFS fs.FS, ext *ocv1.ClusterExten
102101

103102
chrt := &chart.Chart{Metadata: &chart.Metadata{}}
104103
// The need to get the underlying bundle in order to extract its annotations
105-
// will go away once with have a bundle interface that can surface the annotations independently of the
104+
// will go away once we have a bundle interface that can surface the annotations independently of the
106105
// underlying bundle format...
107106
rv1, err := source.FromFS(bundleFS).GetBundle()
108107
if err != nil {
@@ -149,3 +148,14 @@ func extensionConfigBytes(ext *ocv1.ClusterExtension) []byte {
149148
}
150149
return nil
151150
}
151+
152+
func getBundleAnnotations(bundleFS fs.FS) (map[string]string, error) {
153+
// The need to get the underlying bundle in order to extract its annotations
154+
// will go away once we have a bundle interface that can surface the annotations independently of the
155+
// underlying bundle format...
156+
rv1, err := source.FromFS(bundleFS).GetBundle()
157+
if err != nil {
158+
return nil, err
159+
}
160+
return rv1.CSV.GetAnnotations(), nil
161+
}

internal/operator-controller/rukpak/bundle/source/source.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,10 @@ import (
2020
registry "github.com/operator-framework/operator-controller/internal/operator-controller/rukpak/operator-registry"
2121
)
2222

23+
const (
24+
PropertyOLMProperties = "olm.properties"
25+
)
26+
2327
type BundleSource interface {
2428
GetBundle() (bundle.RegistryV1, error)
2529
}
@@ -173,6 +177,9 @@ func copyMetadataPropertiesToCSV(csv *v1alpha1.ClusterServiceVersion, fsys fs.FS
173177
if err != nil {
174178
return fmt.Errorf("failed to marshal registry+v1 properties to json: %w", err)
175179
}
176-
csv.Annotations["olm.properties"] = string(allPropertiesJSON)
180+
if csv.Annotations == nil {
181+
csv.Annotations = map[string]string{}
182+
}
183+
csv.Annotations[PropertyOLMProperties] = string(allPropertiesJSON)
177184
return nil
178185
}

test/e2e/features/install.feature

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -390,3 +390,30 @@ Feature: Install ClusterExtension
390390
Revision has not rolled out for 1 minutes.
391391
"""
392392
And ClusterExtension reports Progressing transition between 1 and 2 minutes since its creation
393+
394+
@BoxcutterRuntime
395+
Scenario: ClusterExtensionRevision is annotated with bundle properties
396+
When ClusterExtension is applied
397+
"""
398+
apiVersion: olm.operatorframework.io/v1
399+
kind: ClusterExtension
400+
metadata:
401+
name: ${NAME}
402+
spec:
403+
namespace: ${TEST_NAMESPACE}
404+
serviceAccount:
405+
name: olm-sa
406+
source:
407+
sourceType: Catalog
408+
catalog:
409+
packageName: test
410+
version: 1.2.0
411+
selector:
412+
matchLabels:
413+
"olm.operatorframework.io/metadata.name": test-catalog
414+
"""
415+
# The annotation key and value come from the bundle's metadata/properties.yaml file
416+
Then ClusterExtensionRevision "${NAME}-1" contains annotation "olm.properties" with value
417+
"""
418+
[{"type":"olm.test-property","value":"some-value"}]
419+
"""

test/e2e/steps/steps.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ func RegisterSteps(sc *godog.ScenarioContext) {
7373
sc.Step(`^(?i)ClusterExtensionRevision "([^"]+)" reports ([[:alnum:]]+) as ([[:alnum:]]+) with Reason ([[:alnum:]]+)$`, ClusterExtensionRevisionReportsConditionWithoutMsg)
7474
sc.Step(`^(?i)ClusterExtension reports ([[:alnum:]]+) transition between (\d+) and (\d+) minutes since its creation$`, ClusterExtensionReportsConditionTransitionTime)
7575
sc.Step(`^(?i)ClusterExtensionRevision "([^"]+)" is archived$`, ClusterExtensionRevisionIsArchived)
76+
sc.Step(`^(?i)ClusterExtensionRevision "([^"]+)" contains annotation "([^"]+)" with value$`, ClusterExtensionRevisionHasAnnotationWithValue)
7677

7778
sc.Step(`^(?i)resource "([^"]+)" is installed$`, ResourceAvailable)
7879
sc.Step(`^(?i)resource "([^"]+)" is available$`, ResourceAvailable)
@@ -485,6 +486,27 @@ func ClusterExtensionRevisionIsArchived(ctx context.Context, revisionName string
485486
return waitForCondition(ctx, "clusterextensionrevision", substituteScenarioVars(revisionName, scenarioCtx(ctx)), "Progressing", "False", ptr.To("Archived"), nil)
486487
}
487488

489+
func ClusterExtensionRevisionHasAnnotationWithValue(ctx context.Context, revisionName, annotationKey string, annotationValue *godog.DocString) error {
490+
sc := scenarioCtx(ctx)
491+
revisionName = substituteScenarioVars(strings.TrimSpace(revisionName), sc)
492+
expectedValue := ""
493+
if annotationValue != nil {
494+
expectedValue = annotationValue.Content
495+
}
496+
waitFor(ctx, func() bool {
497+
obj, err := getResource("clusterextensionrevision", revisionName, "")
498+
if err != nil {
499+
logger.V(1).Error(err, "failed to get clusterextensionrevision", "name", revisionName)
500+
return false
501+
}
502+
if obj.GetAnnotations() == nil {
503+
return false
504+
}
505+
return obj.GetAnnotations()[annotationKey] == expectedValue
506+
})
507+
return nil
508+
}
509+
488510
func ResourceAvailable(ctx context.Context, resource string) error {
489511
sc := scenarioCtx(ctx)
490512
resource = substituteScenarioVars(resource, sc)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
properties:
2+
- type: olm.test-property
3+
value: "some-value"

0 commit comments

Comments
 (0)