Skip to content

Commit 9892759

Browse files
committed
wip
1 parent 0b82490 commit 9892759

File tree

5 files changed

+195
-3
lines changed

5 files changed

+195
-3
lines changed

internal/controllers/operator_controller.go

+28-2
Original file line numberDiff line numberDiff line change
@@ -174,7 +174,7 @@ func (r *OperatorReconciler) reconcile(ctx context.Context, op *operatorsv1alpha
174174
}
175175
// Ensure a BundleDeployment exists with its bundle source from the bundle
176176
// image we just looked up in the solution.
177-
dep := r.generateExpectedBundleDeployment(*op, bundleImage, bundleProvisioner)
177+
dep := r.generateExpectedBundleDeployment(*op, bundleImage, bundleProvisioner, bundleEntity)
178178
if err := r.ensureBundleDeployment(ctx, dep); err != nil {
179179
// originally Reason: operatorsv1alpha1.ReasonInstallationFailed
180180
op.Status.InstalledBundleResource = ""
@@ -260,18 +260,44 @@ func (r *OperatorReconciler) getBundleEntityFromSolution(solution *solver.Soluti
260260
return nil, fmt.Errorf("entity for package %q not found in solution", packageName)
261261
}
262262

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

270+
// TODO(jmprusi): return err in func?
271+
bundlePath, err := bundleEntity.BundlePath()
272+
if err != nil {
273+
return nil
274+
}
275+
276+
channelName, err := bundleEntity.ChannelName()
277+
if err != nil {
278+
return nil
279+
}
280+
281+
packageName, err := bundleEntity.PackageName()
282+
if err != nil {
283+
return nil
284+
}
285+
286+
packageVersion, err := bundleEntity.Version()
287+
if err != nil {
288+
return nil
289+
}
290+
270291
bd := &unstructured.Unstructured{Object: map[string]interface{}{
271292
"apiVersion": rukpakv1alpha1.GroupVersion.String(),
272293
"kind": rukpakv1alpha1.BundleDeploymentKind,
273294
"metadata": map[string]interface{}{
274295
"name": o.GetName(),
296+
"annotations": map[string]interface{}{
297+
"operators.operatorframework.io/package": packageName,
298+
"operators.operatorframework.io/channel": channelName,
299+
"operators.operatorframework.io/version": packageVersion,
300+
},
275301
},
276302
"spec": map[string]interface{}{
277303
// TODO: Don't assume plain provisioner

internal/resolution/entitysources/catalogdsource.go

+7
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,10 @@ func (es *CatalogdEntitySource) Iterate(ctx context.Context, fn input.IteratorFu
7171
return nil
7272
}
7373

74+
type replacesProperty struct {
75+
Replaces string `json:"replaces"`
76+
}
77+
7478
func getEntities(ctx context.Context, client client.Client) (input.EntityList, error) {
7579
entities := input.EntityList{}
7680
bundleMetadatas, packageMetdatas, err := fetchMetadata(ctx, client)
@@ -106,6 +110,9 @@ func getEntities(ctx context.Context, client client.Client) (input.EntityList, e
106110
if catalogScopedEntryName == bundle.Name {
107111
channelValue, _ := json.Marshal(property.Channel{ChannelName: ch.Name, Priority: 0})
108112
props[property.TypeChannel] = string(channelValue)
113+
// TODO(jmprusi): Add the proper PropertyType for this
114+
replacesValue, _ := json.Marshal(replacesProperty{Replaces: b.Replaces})
115+
props["olm.replaces"] = string(replacesValue)
109116
entity := input.Entity{
110117
ID: deppy.IdentifierFromString(fmt.Sprintf("%s%s%s", bundle.Name, bundle.Spec.Package, ch.Name)),
111118
Properties: props,

internal/resolution/variable_sources/bundlesanddependencies/bundles_and_dependencies.go

+3
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ import (
1111
"github.com/operator-framework/deppy/pkg/deppy/input"
1212

1313
olmentity "github.com/operator-framework/operator-controller/internal/resolution/variable_sources/entity"
14+
"github.com/operator-framework/operator-controller/internal/resolution/variable_sources/installedpackage"
1415
"github.com/operator-framework/operator-controller/internal/resolution/variable_sources/requiredpackage"
1516
"github.com/operator-framework/operator-controller/internal/resolution/variable_sources/util/predicates"
1617
entitysort "github.com/operator-framework/operator-controller/internal/resolution/variable_sources/util/sort"
@@ -76,6 +77,8 @@ func (b *BundlesAndDepsVariableSource) GetVariables(ctx context.Context, entityS
7677
switch v := variable.(type) {
7778
case *requiredpackage.Variable:
7879
bundleEntityQueue = append(bundleEntityQueue, v.BundleEntities()...)
80+
case *installedpackage.InstalledPackageVariable:
81+
bundleEntityQueue = append(bundleEntityQueue, v.BundleEntities()...)
7982
}
8083
}
8184

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
package installedpackage
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
"fmt"
7+
8+
"github.com/blang/semver/v4"
9+
"github.com/operator-framework/deppy/pkg/deppy"
10+
"github.com/operator-framework/deppy/pkg/deppy/constraint"
11+
"github.com/operator-framework/deppy/pkg/deppy/input"
12+
13+
olmentity "github.com/operator-framework/operator-controller/internal/resolution/variable_sources/entity"
14+
"github.com/operator-framework/operator-controller/internal/resolution/variable_sources/util/predicates"
15+
"github.com/operator-framework/operator-controller/internal/resolution/variable_sources/util/sort"
16+
rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1"
17+
)
18+
19+
type InstalledPackageVariable struct {
20+
*input.SimpleVariable
21+
bundleEntities []*olmentity.BundleEntity
22+
}
23+
24+
func (r *InstalledPackageVariable) BundleEntities() []*olmentity.BundleEntity {
25+
return r.bundleEntities
26+
}
27+
28+
func NewInstalledPackageVariable(packageName string, bundleEntities []*olmentity.BundleEntity) *InstalledPackageVariable {
29+
id := deppy.IdentifierFromString(fmt.Sprintf("installed package %s", packageName))
30+
var entityIDs []deppy.Identifier
31+
for _, bundle := range bundleEntities {
32+
entityIDs = append(entityIDs, bundle.ID)
33+
}
34+
return &InstalledPackageVariable{
35+
SimpleVariable: input.NewSimpleVariable(id, constraint.Mandatory(), constraint.Dependency(entityIDs...)),
36+
bundleEntities: bundleEntities,
37+
}
38+
}
39+
40+
var _ input.VariableSource = &InstalledPackageVariableSource{}
41+
42+
type InstalledPackageVariableSource struct {
43+
packageName string
44+
version semver.Version
45+
channelName string
46+
}
47+
48+
func NewInstalledPackageVariableSource(packageName, version, channel string) (*InstalledPackageVariableSource, error) {
49+
if packageName == "" {
50+
return nil, fmt.Errorf("package name must not be empty")
51+
}
52+
53+
semverVersion, err := semver.Parse(version)
54+
if err != nil {
55+
return nil, err
56+
}
57+
58+
//TODO(jmprusi): check version and channel
59+
return &InstalledPackageVariableSource{
60+
packageName: packageName,
61+
version: semverVersion,
62+
channelName: channel,
63+
}, nil
64+
}
65+
66+
// TODO(jmprusi): move this somewhere else?
67+
type replacesProperty struct {
68+
Replaces string `json:"replaces"`
69+
}
70+
71+
// TODO(jmprusi): move this somewhere else?
72+
type packageProperty struct {
73+
Package string `json:"packageName"`
74+
Version string `json:"version"`
75+
}
76+
77+
func (r *InstalledPackageVariableSource) GetVariables(ctx context.Context, entitySource input.EntitySource) ([]deppy.Variable, error) {
78+
validRange, err := semver.ParseRange(">=" + r.version.String())
79+
if err != nil {
80+
return nil, err
81+
}
82+
resultSet, err := entitySource.Filter(ctx, input.And(predicates.WithPackageName(r.packageName), predicates.InChannel(r.channelName), predicates.InSemverRange(validRange)))
83+
if err != nil {
84+
return nil, err
85+
}
86+
if len(resultSet) == 0 {
87+
return nil, r.notFoundError()
88+
}
89+
resultSet = resultSet.Sort(sort.ByChannelAndVersion)
90+
var bundleEntities []*olmentity.BundleEntity
91+
for i := 0; i < len(resultSet); i++ {
92+
93+
replacesJSON := resultSet[i].Properties["olm.replaces"]
94+
packageJSON := resultSet[i].Properties["olm.package"]
95+
96+
if replacesJSON == "" || packageJSON == "" {
97+
continue
98+
}
99+
100+
// unmarshal replaces and packages
101+
var replaces replacesProperty
102+
var packages packageProperty
103+
if err := json.Unmarshal([]byte(replacesJSON), &replaces); err != nil {
104+
return nil, err
105+
}
106+
if err := json.Unmarshal([]byte(packageJSON), &packages); err != nil {
107+
return nil, err
108+
}
109+
110+
version, err := semver.Parse(packages.Version)
111+
if err != nil {
112+
return nil, err
113+
}
114+
115+
expectedReplace := fmt.Sprintf("%s.v%s", r.packageName, r.version.String())
116+
if r.version.Equals(version) || replaces.Replaces == expectedReplace {
117+
bundleEntities = append(bundleEntities, olmentity.NewBundleEntity(&resultSet[i]))
118+
}
119+
120+
}
121+
return []deppy.Variable{
122+
NewInstalledPackageVariable(r.packageName, bundleEntities),
123+
}, nil
124+
}
125+
126+
func (r *InstalledPackageVariableSource) notFoundError() error {
127+
return fmt.Errorf("package '%s' not installed", r.packageName)
128+
}
129+
130+
func NewInstalledPackage(bundleDeployment *rukpakv1alpha1.BundleDeployment) (*InstalledPackageVariableSource, error) {
131+
132+
// TODO(jmprusi): proper if ... validation
133+
version := bundleDeployment.Annotations["operators.operatorframework.io/version"]
134+
channel := bundleDeployment.Annotations["operators.operatorframework.io/channel"]
135+
pkg := bundleDeployment.Annotations["operators.operatorframework.io/package"]
136+
137+
return NewInstalledPackageVariableSource(pkg, version, channel)
138+
}

internal/resolution/variable_sources/olm/olm.go

+19-1
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@ import (
55

66
"github.com/operator-framework/deppy/pkg/deppy"
77
"github.com/operator-framework/deppy/pkg/deppy/input"
8-
"sigs.k8s.io/controller-runtime/pkg/client"
98

109
operatorsv1alpha1 "github.com/operator-framework/operator-controller/api/v1alpha1"
1110
"github.com/operator-framework/operator-controller/internal/resolution/variable_sources/bundlesanddependencies"
1211
"github.com/operator-framework/operator-controller/internal/resolution/variable_sources/crdconstraints"
12+
"github.com/operator-framework/operator-controller/internal/resolution/variable_sources/installedpackage"
1313
"github.com/operator-framework/operator-controller/internal/resolution/variable_sources/requiredpackage"
14+
rukpakv1alpha1 "github.com/operator-framework/rukpak/api/v1alpha1"
15+
"sigs.k8s.io/controller-runtime/pkg/client"
1416
)
1517

1618
var _ input.VariableSource = &VariableSource{}
@@ -45,6 +47,22 @@ func (o *VariableSource) GetVariables(ctx context.Context, entitySource input.En
4547
inputVariableSources = append(inputVariableSources, rps)
4648
}
4749

50+
bundleDeployments := rukpakv1alpha1.BundleDeploymentList{}
51+
if err := o.client.List(ctx, &bundleDeployments); err != nil {
52+
return nil, err
53+
}
54+
55+
for _, bundleDeployment := range bundleDeployments.Items {
56+
if _, ok := bundleDeployment.Annotations["operators.operatorframework.io/package"]; !ok {
57+
continue
58+
}
59+
ips, err := installedpackage.NewInstalledPackage(&bundleDeployment)
60+
if err != nil {
61+
return nil, err
62+
}
63+
inputVariableSources = append(inputVariableSources, ips)
64+
}
65+
4866
// build variable source pipeline
4967
variableSource := crdconstraints.NewCRDUniquenessConstraintsVariableSource(bundlesanddependencies.NewBundlesAndDepsVariableSource(inputVariableSources...))
5068
return variableSource.GetVariables(ctx, entitySource)

0 commit comments

Comments
 (0)