Skip to content

Commit 768d838

Browse files
authored
add crd uniqueness variable source (#98)
Signed-off-by: perdasilva <[email protected]>
1 parent 1991c57 commit 768d838

File tree

3 files changed

+431
-1
lines changed

3 files changed

+431
-1
lines changed

internal/resolution/variable_sources/bundles_and_dependencies/bundles_and_dependencies.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,7 @@ func (b *BundlesAndDepsVariableSource) GetVariables(ctx context.Context, entityS
9595
// get bundle dependencies
9696
dependencyEntityBundles, err := b.getEntityDependencies(ctx, head, entitySource)
9797
if err != nil {
98-
return nil, fmt.Errorf("could not determine dependencies for entity with id '%s': %s", head.ID, err)
98+
return nil, fmt.Errorf("could not determine dependencies for entity with id '%s': %w", head.ID, err)
9999
}
100100

101101
// add bundle dependencies to queue for processing
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
package crd_constraints
2+
3+
import (
4+
"context"
5+
"fmt"
6+
7+
"github.com/operator-framework/deppy/pkg/deppy"
8+
"github.com/operator-framework/deppy/pkg/deppy/constraint"
9+
"github.com/operator-framework/deppy/pkg/deppy/input"
10+
11+
"github.com/operator-framework/operator-controller/internal/resolution/variable_sources/bundles_and_dependencies"
12+
olmentity "github.com/operator-framework/operator-controller/internal/resolution/variable_sources/entity"
13+
)
14+
15+
type BundleUniquenessVariable struct {
16+
*input.SimpleVariable
17+
}
18+
19+
// NewBundleUniquenessVariable creates a new variable that instructs the resolver to choose at most a single bundle
20+
// from the input 'atMostID'. Examples:
21+
// 1. restrict the solution to at most a single bundle per package
22+
// 2. restrict the solution to at most a single bundler per provided gvk
23+
// this guarantees that no two operators provide the same gvk and no two version of the same operator are running at the same time
24+
func NewBundleUniquenessVariable(id deppy.Identifier, atMostIDs ...deppy.Identifier) *BundleUniquenessVariable {
25+
return &BundleUniquenessVariable{
26+
SimpleVariable: input.NewSimpleVariable(id, constraint.AtMost(1, atMostIDs...)),
27+
}
28+
}
29+
30+
var _ input.VariableSource = &CRDUniquenessConstraintsVariableSource{}
31+
32+
// CRDUniquenessConstraintsVariableSource produces variables that constraint the solution to
33+
// 1. at most 1 bundle per package
34+
// 2. at most 1 bundle per gvk (provided by the bundle)
35+
// these variables guarantee that no two operators provide the same gvk and no two version of
36+
// the same operator are running at the same time.
37+
// This variable source does not itself reach out to its entitySource. It produces its variables
38+
// by searching for BundleVariables that are produced by its 'inputVariableSource' and working out
39+
// which bundles correspond to which package and which gvks are provided by which bundle
40+
type CRDUniquenessConstraintsVariableSource struct {
41+
inputVariableSource input.VariableSource
42+
}
43+
44+
// NewCRDUniquenessConstraintsVariableSource creates a new instance of the CRDUniquenessConstraintsVariableSource.
45+
// its purpose if to provide variables with constraints that restrict the solutions to bundle sets where
46+
// no two bundles come from the same package and not two bundles provide the same gvk
47+
func NewCRDUniquenessConstraintsVariableSource(inputVariableSource input.VariableSource) *CRDUniquenessConstraintsVariableSource {
48+
return &CRDUniquenessConstraintsVariableSource{
49+
inputVariableSource: inputVariableSource,
50+
}
51+
}
52+
53+
func (g *CRDUniquenessConstraintsVariableSource) GetVariables(ctx context.Context, entitySource input.EntitySource) ([]deppy.Variable, error) {
54+
variables, err := g.inputVariableSource.GetVariables(ctx, entitySource)
55+
if err != nil {
56+
return nil, err
57+
}
58+
59+
// todo(perdasilva): better handle cases where a provided gvk is not found
60+
// not all packages will necessarily export a CRD
61+
62+
pkgToBundleMap := map[string]map[deppy.Identifier]struct{}{}
63+
gvkToBundleMap := map[string]map[deppy.Identifier]struct{}{}
64+
for _, variable := range variables {
65+
switch v := variable.(type) {
66+
case *bundles_and_dependencies.BundleVariable:
67+
bundleEntities := []*olmentity.BundleEntity{v.BundleEntity()}
68+
bundleEntities = append(bundleEntities, v.Dependencies()...)
69+
for _, bundleEntity := range bundleEntities {
70+
// get bundleID package and update map
71+
packageName, err := bundleEntity.PackageName()
72+
if err != nil {
73+
return nil, fmt.Errorf("error creating global constraints: %w", err)
74+
}
75+
76+
if _, ok := pkgToBundleMap[packageName]; !ok {
77+
pkgToBundleMap[packageName] = map[deppy.Identifier]struct{}{}
78+
}
79+
pkgToBundleMap[packageName][bundleEntity.ID] = struct{}{}
80+
81+
// get bundleID gvks and update map
82+
exportedGVKs, err := bundleEntity.ProvidedGVKs()
83+
if err != nil {
84+
return nil, fmt.Errorf("error creating global constraints: %w", err)
85+
}
86+
for i := 0; i < len(exportedGVKs); i++ {
87+
gvk := exportedGVKs[i].String()
88+
if _, ok := gvkToBundleMap[gvk]; !ok {
89+
gvkToBundleMap[gvk] = map[deppy.Identifier]struct{}{}
90+
}
91+
gvkToBundleMap[gvk][bundleEntity.ID] = struct{}{}
92+
}
93+
}
94+
}
95+
}
96+
97+
// create global constraint variables
98+
for packageName, bundleIDMap := range pkgToBundleMap {
99+
var bundleIDs []deppy.Identifier
100+
for bundleID := range bundleIDMap {
101+
bundleIDs = append(bundleIDs, bundleID)
102+
}
103+
varID := deppy.IdentifierFromString(fmt.Sprintf("%s package uniqueness", packageName))
104+
variables = append(variables, NewBundleUniquenessVariable(varID, bundleIDs...))
105+
}
106+
107+
for gvk, bundleIDMap := range gvkToBundleMap {
108+
var bundleIDs []deppy.Identifier
109+
for bundleID := range bundleIDMap {
110+
bundleIDs = append(bundleIDs, bundleID)
111+
}
112+
varID := deppy.IdentifierFromString(fmt.Sprintf("%s gvk uniqueness", gvk))
113+
variables = append(variables, NewBundleUniquenessVariable(varID, bundleIDs...))
114+
}
115+
116+
return variables, nil
117+
}

0 commit comments

Comments
 (0)