Skip to content

Commit 1991c57

Browse files
authored
add bundles and dependencies variable source (#97)
Adds bundles and dependencies variable source Signed-off-by: perdasilva <[email protected]>
1 parent f204fa3 commit 1991c57

File tree

3 files changed

+446
-0
lines changed

3 files changed

+446
-0
lines changed
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
package bundles_and_dependencies
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"sort"
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/required_package"
15+
"github.com/operator-framework/operator-controller/internal/resolution/variable_sources/utils/predicates"
16+
entitysort "github.com/operator-framework/operator-controller/internal/resolution/variable_sources/utils/sort"
17+
)
18+
19+
type BundleVariable struct {
20+
*input.SimpleVariable
21+
bundleEntity *olmentity.BundleEntity
22+
dependencies []*olmentity.BundleEntity
23+
}
24+
25+
func (b *BundleVariable) BundleEntity() *olmentity.BundleEntity {
26+
return b.bundleEntity
27+
}
28+
29+
func (b *BundleVariable) Dependencies() []*olmentity.BundleEntity {
30+
return b.dependencies
31+
}
32+
33+
func NewBundleVariable(bundleEntity *olmentity.BundleEntity, dependencyBundleEntities []*olmentity.BundleEntity) *BundleVariable {
34+
var dependencyIDs []deppy.Identifier
35+
for _, bundle := range dependencyBundleEntities {
36+
dependencyIDs = append(dependencyIDs, bundle.ID)
37+
}
38+
var constraints []deppy.Constraint
39+
if len(dependencyIDs) > 0 {
40+
constraints = append(constraints, constraint.Dependency(dependencyIDs...))
41+
}
42+
return &BundleVariable{
43+
SimpleVariable: input.NewSimpleVariable(bundleEntity.ID, constraints...),
44+
bundleEntity: bundleEntity,
45+
dependencies: dependencyBundleEntities,
46+
}
47+
}
48+
49+
var _ input.VariableSource = &BundlesAndDepsVariableSource{}
50+
51+
type BundlesAndDepsVariableSource struct {
52+
variableSources []input.VariableSource
53+
}
54+
55+
func NewBundlesAndDepsVariableSource(inputVariableSources ...input.VariableSource) *BundlesAndDepsVariableSource {
56+
return &BundlesAndDepsVariableSource{
57+
variableSources: inputVariableSources,
58+
}
59+
}
60+
61+
func (b *BundlesAndDepsVariableSource) GetVariables(ctx context.Context, entitySource input.EntitySource) ([]deppy.Variable, error) {
62+
var variables []deppy.Variable
63+
64+
// extract required package variables
65+
for _, variableSource := range b.variableSources {
66+
inputVariables, err := variableSource.GetVariables(ctx, entitySource)
67+
if err != nil {
68+
return nil, err
69+
}
70+
variables = append(variables, inputVariables...)
71+
}
72+
73+
// create bundle queue for dependency resolution
74+
var bundleEntityQueue []*olmentity.BundleEntity
75+
for _, variable := range variables {
76+
switch v := variable.(type) {
77+
case *required_package.RequiredPackageVariable:
78+
bundleEntityQueue = append(bundleEntityQueue, v.BundleEntities()...)
79+
}
80+
}
81+
82+
// build bundle and dependency variables
83+
visited := map[deppy.Identifier]struct{}{}
84+
for len(bundleEntityQueue) > 0 {
85+
// pop head of queue
86+
var head *olmentity.BundleEntity
87+
head, bundleEntityQueue = bundleEntityQueue[0], bundleEntityQueue[1:]
88+
89+
// ignore bundles that have already been processed
90+
if _, ok := visited[head.ID]; ok {
91+
continue
92+
}
93+
visited[head.ID] = struct{}{}
94+
95+
// get bundle dependencies
96+
dependencyEntityBundles, err := b.getEntityDependencies(ctx, head, entitySource)
97+
if err != nil {
98+
return nil, fmt.Errorf("could not determine dependencies for entity with id '%s': %s", head.ID, err)
99+
}
100+
101+
// add bundle dependencies to queue for processing
102+
bundleEntityQueue = append(bundleEntityQueue, dependencyEntityBundles...)
103+
104+
// create variable
105+
variables = append(variables, NewBundleVariable(head, dependencyEntityBundles))
106+
}
107+
108+
return variables, nil
109+
}
110+
111+
func (b *BundlesAndDepsVariableSource) getEntityDependencies(ctx context.Context, bundleEntity *olmentity.BundleEntity, entitySource input.EntitySource) ([]*olmentity.BundleEntity, error) {
112+
var dependencies []*olmentity.BundleEntity
113+
added := map[deppy.Identifier]struct{}{}
114+
115+
// gather required package dependencies
116+
// todo(perdasilva): disambiguate between not found and actual errors
117+
requiredPackages, _ := bundleEntity.RequiredPackages()
118+
for _, requiredPackage := range requiredPackages {
119+
semverRange, err := semver.ParseRange(requiredPackage.VersionRange)
120+
if err != nil {
121+
return nil, err
122+
}
123+
packageDependencyBundles, err := entitySource.Filter(ctx, input.And(predicates.WithPackageName(requiredPackage.PackageName), predicates.InSemverRange(semverRange)))
124+
if err != nil {
125+
return nil, err
126+
}
127+
if len(packageDependencyBundles) == 0 {
128+
return nil, fmt.Errorf("could not find package dependencies for bundle '%s'", bundleEntity.ID)
129+
}
130+
for i := 0; i < len(packageDependencyBundles); i++ {
131+
entity := packageDependencyBundles[i]
132+
if _, ok := added[entity.ID]; !ok {
133+
dependencies = append(dependencies, olmentity.NewBundleEntity(&entity))
134+
added[entity.ID] = struct{}{}
135+
}
136+
}
137+
}
138+
139+
// gather required gvk dependencies
140+
// todo(perdasilva): disambiguate between not found and actual errors
141+
gvkDependencies, _ := bundleEntity.RequiredGVKs()
142+
for i := 0; i < len(gvkDependencies); i++ {
143+
providedGvk := gvkDependencies[i].AsGVK()
144+
gvkDependencyBundles, err := entitySource.Filter(ctx, predicates.ProvidesGVK(&providedGvk))
145+
if err != nil {
146+
return nil, err
147+
}
148+
if len(gvkDependencyBundles) == 0 {
149+
return nil, fmt.Errorf("could not find gvk dependencies for bundle '%s'", bundleEntity.ID)
150+
}
151+
for i := 0; i < len(gvkDependencyBundles); i++ {
152+
entity := gvkDependencyBundles[i]
153+
if _, ok := added[entity.ID]; !ok {
154+
dependencies = append(dependencies, olmentity.NewBundleEntity(&entity))
155+
added[entity.ID] = struct{}{}
156+
}
157+
}
158+
}
159+
160+
// sort bundles in version order
161+
sort.SliceStable(dependencies, func(i, j int) bool {
162+
return entitysort.ByChannelAndVersion(dependencies[i].Entity, dependencies[j].Entity)
163+
})
164+
165+
return dependencies, nil
166+
}

0 commit comments

Comments
 (0)