Skip to content

Commit 5aab955

Browse files
author
Mikalai Radchuk
committed
Add annotations to BundleDeployment
This adds annotations to `BundleDeployment` objects created by operator-controller in response to `Operator` objects. This makes it easier to filter bundles during resolution and makes us less dependant on image bundle format. Signed-off-by: Mikalai Radchuk <mradchuk@redhat.com>
1 parent 65bfc69 commit 5aab955

File tree

8 files changed

+77
-49
lines changed

8 files changed

+77
-49
lines changed

internal/catalogmetadata/filter/bundle_predicates.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ func WithPackageName(packageName string) Predicate[catalogmetadata.Bundle] {
1313
}
1414
}
1515

16+
func WithBundleName(bundleName string) Predicate[catalogmetadata.Bundle] {
17+
return func(bundle *catalogmetadata.Bundle) bool {
18+
return bundle.Name == bundleName
19+
}
20+
}
21+
1622
func InMastermindsSemverRange(semverRange *mmsemver.Constraints) Predicate[catalogmetadata.Bundle] {
1723
return func(bundle *catalogmetadata.Bundle) bool {
1824
bVersion, err := bundle.Version()

internal/controllers/operator_controller.go

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -179,9 +179,15 @@ func (r *OperatorReconciler) reconcile(ctx context.Context, op *operatorsv1alpha
179179
setInstalledStatusConditionFailed(&op.Status.Conditions, err.Error(), op.GetGeneration())
180180
return ctrl.Result{}, err
181181
}
182+
182183
// Ensure a BundleDeployment exists with its bundle source from the bundle
183184
// image we just looked up in the solution.
184-
dep := r.generateExpectedBundleDeployment(*op, bundle.Image, bundleProvisioner)
185+
dep, err := r.generateExpectedBundleDeployment(*op, bundle, bundleProvisioner)
186+
if err != nil {
187+
op.Status.InstalledBundleResource = ""
188+
setInstalledStatusConditionFailed(&op.Status.Conditions, err.Error(), op.GetGeneration())
189+
return ctrl.Result{}, err
190+
}
185191
if err := r.ensureBundleDeployment(ctx, dep); err != nil {
186192
// originally Reason: operatorsv1alpha1.ReasonInstallationFailed
187193
op.Status.InstalledBundleResource = ""
@@ -264,18 +270,28 @@ func (r *OperatorReconciler) bundleFromSolution(solution *solver.Solution, packa
264270
return nil, fmt.Errorf("bundle for package %q not found in solution", packageName)
265271
}
266272

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

280+
bundleVersion, err := bundle.Version()
281+
if err != nil {
282+
return nil, err
283+
}
284+
274285
bd := &unstructured.Unstructured{Object: map[string]interface{}{
275286
"apiVersion": rukpakv1alpha1.GroupVersion.String(),
276287
"kind": rukpakv1alpha1.BundleDeploymentKind,
277288
"metadata": map[string]interface{}{
278289
"name": o.GetName(),
290+
"annotations": map[string]string{
291+
"operators.operatorframework.io/package-name": bundle.Package,
292+
"operators.operatorframework.io/bundle-name": bundle.Name,
293+
"operators.operatorframework.io/bundle-version": bundleVersion.String(),
294+
},
279295
},
280296
"spec": map[string]interface{}{
281297
// TODO: Don't assume plain provisioner
@@ -287,7 +303,7 @@ func (r *OperatorReconciler) generateExpectedBundleDeployment(o operatorsv1alpha
287303
// TODO: Don't assume image type
288304
"type": string(rukpakv1alpha1.SourceTypeImage),
289305
"image": map[string]interface{}{
290-
"ref": bundlePath,
306+
"ref": bundle.Image,
291307
},
292308
},
293309
},
@@ -304,7 +320,7 @@ func (r *OperatorReconciler) generateExpectedBundleDeployment(o operatorsv1alpha
304320
BlockOwnerDeletion: pointer.Bool(true),
305321
},
306322
})
307-
return bd
323+
return bd, nil
308324
}
309325

310326
// SetupWithManager sets up the controller with the Manager.

internal/resolution/variablesources/bundle_deployment.go

Lines changed: 7 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -36,20 +36,15 @@ func (o *BundleDeploymentVariableSource) GetVariables(ctx context.Context) ([]de
3636
return nil, err
3737
}
3838

39-
processed := map[string]struct{}{}
4039
for _, bundleDeployment := range bundleDeployments.Items {
41-
sourceImage := bundleDeployment.Spec.Template.Spec.Source.Image
42-
if sourceImage != nil && sourceImage.Ref != "" {
43-
if _, ok := processed[sourceImage.Ref]; ok {
44-
continue
45-
}
46-
processed[sourceImage.Ref] = struct{}{}
47-
ips, err := NewInstalledPackageVariableSource(o.catalogClient, bundleDeployment.Spec.Template.Spec.Source.Image.Ref)
48-
if err != nil {
49-
return nil, err
50-
}
51-
variableSources = append(variableSources, ips)
40+
pkgName := bundleDeployment.Annotations["operators.operatorframework.io/package-name"]
41+
bundleName := bundleDeployment.Annotations["operators.operatorframework.io/bundle-name"]
42+
bundleVersion := bundleDeployment.Annotations["operators.operatorframework.io/bundle-version"]
43+
if pkgName == "" || bundleName == "" || bundleVersion == "" {
44+
continue
5245
}
46+
ips := NewInstalledPackageVariableSource(o.catalogClient, pkgName, bundleName, bundleVersion)
47+
variableSources = append(variableSources, ips)
5348
}
5449

5550
return variableSources.GetVariables(ctx)

internal/resolution/variablesources/bundle_deployment_test.go

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -31,10 +31,15 @@ func BundleDeploymentFakeClient(objects ...client.Object) client.Client {
3131
return fake.NewClientBuilder().WithScheme(scheme).WithObjects(objects...).Build()
3232
}
3333

34-
func bundleDeployment(name, image string) *rukpakv1alpha1.BundleDeployment {
34+
func bundleDeployment(pkgName, bundleName, bundleVersion, image string) *rukpakv1alpha1.BundleDeployment {
3535
return &rukpakv1alpha1.BundleDeployment{
3636
ObjectMeta: metav1.ObjectMeta{
37-
Name: name,
37+
Name: pkgName,
38+
Annotations: map[string]string{
39+
"operators.operatorframework.io/package-name": pkgName,
40+
"operators.operatorframework.io/bundle-name": bundleName,
41+
"operators.operatorframework.io/bundle-version": bundleVersion,
42+
},
3843
},
3944
Spec: rukpakv1alpha1.BundleDeploymentSpec{
4045
ProvisionerClassName: "core-rukpak-io-plain",
@@ -115,10 +120,10 @@ var _ = Describe("BundleDeploymentVariableSource", func() {
115120
fakeCatalogClient = testutil.NewFakeCatalogClient(testBundleList)
116121
})
117122

118-
It("should produce RequiredPackage variables", func() {
119-
cl := BundleDeploymentFakeClient(bundleDeployment("prometheus", "quay.io/operatorhubio/prometheus@sha256:3e281e587de3d03011440685fc4fb782672beab044c1ebadc42788ce05a21c35"))
123+
It("should produce InstalledPackage variables", func() {
124+
cl := BundleDeploymentFakeClient(bundleDeployment("prometheus", "operatorhub/prometheus/0.37.0", "0.37.0", "quay.io/operatorhubio/prometheus@sha256:3e281e587de3d03011440685fc4fb782672beab044c1ebadc42788ce05a21c35"))
120125

121-
bdVariableSource := variablesources.NewBundleDeploymentVariableSource(cl, &fakeCatalogClient, &MockRequiredPackageSource{})
126+
bdVariableSource := variablesources.NewBundleDeploymentVariableSource(cl, &fakeCatalogClient, &MockVariableSource{})
122127
variables, err := bdVariableSource.GetVariables(context.Background())
123128
Expect(err).ToNot(HaveOccurred())
124129

@@ -137,10 +142,10 @@ var _ = Describe("BundleDeploymentVariableSource", func() {
137142
})))
138143
})
139144
It("should return an error if the bundleDeployment image doesn't match any operator resource", func() {
140-
cl := BundleDeploymentFakeClient(bundleDeployment("prometheus", "quay.io/operatorhubio/prometheus@sha256:nonexistent"))
145+
cl := BundleDeploymentFakeClient(bundleDeployment("prometheus", "operatorhub/prometheus/9.9.9", "9.9.9", "quay.io/operatorhubio/prometheus@sha256:nonexistent"))
141146

142-
bdVariableSource := variablesources.NewBundleDeploymentVariableSource(cl, &fakeCatalogClient, &MockRequiredPackageSource{})
147+
bdVariableSource := variablesources.NewBundleDeploymentVariableSource(cl, &fakeCatalogClient, &MockVariableSource{})
143148
_, err := bdVariableSource.GetVariables(context.Background())
144-
Expect(err.Error()).To(Equal("bundleImage \"quay.io/operatorhubio/prometheus@sha256:nonexistent\" not found"))
149+
Expect(err.Error()).To(Equal(`bundle for package "prometheus" with name "operatorhub/prometheus/9.9.9" at version "9.9.9" not found`))
145150
})
146151
})

internal/resolution/variablesources/bundles_and_dependencies_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ var _ = Describe("BundlesAndDepsVariableSource", func() {
225225
fakeCatalogClient = testutil.NewFakeCatalogClient(testBundleList)
226226
bdvs = variablesources.NewBundlesAndDepsVariableSource(
227227
&fakeCatalogClient,
228-
&MockRequiredPackageSource{
228+
&MockVariableSource{
229229
ResultSet: []deppy.Variable{
230230
// must match data in fakeCatalogClient
231231
olmvariables.NewRequiredPackageVariable("test-package", []*catalogmetadata.Bundle{
@@ -257,7 +257,7 @@ var _ = Describe("BundlesAndDepsVariableSource", func() {
257257
}),
258258
},
259259
},
260-
&MockRequiredPackageSource{
260+
&MockVariableSource{
261261
ResultSet: []deppy.Variable{
262262
// must match data in fakeCatalogClient
263263
olmvariables.NewRequiredPackageVariable("test-package-2", []*catalogmetadata.Bundle{
@@ -339,7 +339,7 @@ var _ = Describe("BundlesAndDepsVariableSource", func() {
339339

340340
bdvs = variablesources.NewBundlesAndDepsVariableSource(
341341
&emptyCatalogClient,
342-
&MockRequiredPackageSource{
342+
&MockVariableSource{
343343
ResultSet: []deppy.Variable{
344344
// must match data in fakeCatalogClient
345345
olmvariables.NewRequiredPackageVariable("test-package", []*catalogmetadata.Bundle{
@@ -380,20 +380,20 @@ var _ = Describe("BundlesAndDepsVariableSource", func() {
380380
It("should return error if an inner variable source returns an error", func() {
381381
bdvs = variablesources.NewBundlesAndDepsVariableSource(
382382
&fakeCatalogClient,
383-
&MockRequiredPackageSource{Error: errors.New("fake error")},
383+
&MockVariableSource{Error: errors.New("fake error")},
384384
)
385385
_, err := bdvs.GetVariables(context.TODO())
386386
Expect(err).To(HaveOccurred())
387387
Expect(err).To(MatchError("fake error"))
388388
})
389389
})
390390

391-
type MockRequiredPackageSource struct {
391+
type MockVariableSource struct {
392392
ResultSet []deppy.Variable
393393
Error error
394394
}
395395

396-
func (m *MockRequiredPackageSource) GetVariables(_ context.Context) ([]deppy.Variable, error) {
396+
func (m *MockVariableSource) GetVariables(_ context.Context) ([]deppy.Variable, error) {
397397
return m.ResultSet, m.Error
398398
}
399399

internal/resolution/variablesources/installed_package.go

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import (
55
"fmt"
66
"sort"
77

8+
mmsemver "github.com/Masterminds/semver/v3"
89
"github.com/operator-framework/deppy/pkg/deppy"
910
"github.com/operator-framework/deppy/pkg/deppy/input"
1011

@@ -17,7 +18,9 @@ var _ input.VariableSource = &InstalledPackageVariableSource{}
1718

1819
type InstalledPackageVariableSource struct {
1920
catalogClient BundleProvider
20-
bundleImage string
21+
pkgName string
22+
bundleName string
23+
bundleVersion string
2124
}
2225

2326
func (r *InstalledPackageVariableSource) GetVariables(ctx context.Context) ([]deppy.Variable, error) {
@@ -26,18 +29,20 @@ func (r *InstalledPackageVariableSource) GetVariables(ctx context.Context) ([]de
2629
return nil, err
2730
}
2831

32+
vr, err := mmsemver.NewConstraint(r.bundleVersion)
33+
if err != nil {
34+
return nil, err
35+
}
36+
2937
// find corresponding bundle for the installed content
30-
resultSet := catalogfilter.Filter(allBundles, catalogfilter.WithBundleImage(r.bundleImage))
38+
resultSet := catalogfilter.Filter(allBundles, catalogfilter.And(
39+
catalogfilter.WithPackageName(r.pkgName),
40+
catalogfilter.WithBundleName(r.bundleName),
41+
catalogfilter.InMastermindsSemverRange(vr),
42+
))
3143
if len(resultSet) == 0 {
3244
return nil, r.notFoundError()
3345
}
34-
35-
// TODO: fast follow - we should check whether we are already supporting the channel attribute in the operator spec.
36-
// if so, we should take the value from spec of the operator CR in the owner ref of the bundle deployment.
37-
// If that channel is set, we need to update the filter above to filter by channel as well.
38-
sort.SliceStable(resultSet, func(i, j int) bool {
39-
return catalogsort.ByVersion(resultSet[i], resultSet[j])
40-
})
4146
installedBundle := resultSet[0]
4247

4348
// now find the bundles that replace the installed bundle
@@ -56,12 +61,14 @@ func (r *InstalledPackageVariableSource) GetVariables(ctx context.Context) ([]de
5661
}
5762

5863
func (r *InstalledPackageVariableSource) notFoundError() error {
59-
return fmt.Errorf("bundleImage %q not found", r.bundleImage)
64+
return fmt.Errorf("bundle for package %q with name %q at version %q not found", r.pkgName, r.bundleName, r.bundleVersion)
6065
}
6166

62-
func NewInstalledPackageVariableSource(catalogClient BundleProvider, bundleImage string) (*InstalledPackageVariableSource, error) {
67+
func NewInstalledPackageVariableSource(catalogClient BundleProvider, pkgName, bundleName, bundleVersion string) *InstalledPackageVariableSource {
6368
return &InstalledPackageVariableSource{
6469
catalogClient: catalogClient,
65-
bundleImage: bundleImage,
66-
}, nil
70+
pkgName: pkgName,
71+
bundleName: bundleName,
72+
bundleVersion: bundleVersion,
73+
}
6774
}

internal/resolution/variablesources/installed_package_test.go

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ var _ = Describe("InstalledPackageVariableSource", func() {
2020
var (
2121
ipvs *variablesources.InstalledPackageVariableSource
2222
fakeCatalogClient testutil.FakeCatalogClient
23-
bundleImage string
2423
)
2524

2625
BeforeEach(func() {
@@ -95,11 +94,11 @@ var _ = Describe("InstalledPackageVariableSource", func() {
9594
InChannels: []*catalogmetadata.Channel{&channel},
9695
},
9796
}
98-
var err error
99-
bundleImage = "registry.io/repo/test-package@v2.0.0"
97+
pkgName := "test-package"
98+
bundleName := "test-package.v2.0.0"
99+
bundleVersion := "2.0.0"
100100
fakeCatalogClient = testutil.NewFakeCatalogClient(bundleList)
101-
ipvs, err = variablesources.NewInstalledPackageVariableSource(&fakeCatalogClient, bundleImage)
102-
Expect(err).NotTo(HaveOccurred())
101+
ipvs = variablesources.NewInstalledPackageVariableSource(&fakeCatalogClient, pkgName, bundleName, bundleVersion)
103102
})
104103

105104
It("should return the correct package variable", func() {

internal/resolution/variablesources/operator_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,7 @@ var _ = Describe("OperatorVariableSource", func() {
116116
It("should produce RequiredPackage variables", func() {
117117
cl := FakeClient(operator("prometheus"), operator("packageA"))
118118
fakeCatalogClient := testutil.NewFakeCatalogClient(testBundleList)
119-
opVariableSource := variablesources.NewOperatorVariableSource(cl, &fakeCatalogClient, &MockRequiredPackageSource{})
119+
opVariableSource := variablesources.NewOperatorVariableSource(cl, &fakeCatalogClient, &MockVariableSource{})
120120
variables, err := opVariableSource.GetVariables(context.Background())
121121
Expect(err).ToNot(HaveOccurred())
122122

0 commit comments

Comments
 (0)