Skip to content

Commit c918a85

Browse files
committed
Translate legacy "bundle dependencies" to properties.
Operator dependencies are expressed as properties, but there was initially a notion of dependencies as distinct from properties. For that reason, catalog-operator must support existing catalog images that serve separate Bundle.dependencies and Bundle.properties fields by translating the three legacy dependency types (olm.label, olm.package, and olm.gvk) into their corresponding properties. Signed-off-by: Ben Luddy <[email protected]>
1 parent 568d145 commit c918a85

File tree

5 files changed

+274
-141
lines changed

5 files changed

+274
-141
lines changed

pkg/controller/registry/resolver/installabletypes.go

+13-7
Original file line numberDiff line numberDiff line change
@@ -60,24 +60,30 @@ func NewBundleInstallableFromOperator(o *Operator) (BundleInstallable, error) {
6060
if src == nil {
6161
return BundleInstallable{}, fmt.Errorf("unable to resolve the source of bundle %s", o.Identifier())
6262
}
63+
id := bundleId(o.Identifier(), o.Channel(), src.Catalog)
6364
var constraints []solver.Constraint
65+
if src.Catalog.Virtual() && src.Subscription == nil {
66+
// CSVs already associated with a Subscription
67+
// may be replaced, but freestanding CSVs must
68+
// appear in any solution.
69+
constraints = append(constraints, PrettyConstraint(
70+
solver.Mandatory(),
71+
fmt.Sprintf("clusterserviceversion %s exists and is not referenced by a subscription", o.Identifier()),
72+
))
73+
}
6474
for _, p := range o.bundle.GetProperties() {
6575
if p.GetType() == operatorregistry.DeprecatedType {
6676
constraints = append(constraints, PrettyConstraint(
6777
solver.Prohibited(),
68-
fmt.Sprintf("bundle %s is deprecated", bundleId(o.Identifier(), o.Channel(), src.Catalog)),
78+
fmt.Sprintf("bundle %s is deprecated", id),
6979
))
7080
break
7181
}
7282
}
73-
return NewBundleInstallable(o.Identifier(), o.Channel(), src.Catalog, constraints...), nil
74-
}
75-
76-
func NewBundleInstallable(bundle, channel string, catalog registry.CatalogKey, constraints ...solver.Constraint) BundleInstallable {
7783
return BundleInstallable{
78-
identifier: bundleId(bundle, channel, catalog),
84+
identifier: id,
7985
constraints: constraints,
80-
}
86+
}, nil
8187
}
8288

8389
type GenericInstallable struct {

pkg/controller/registry/resolver/operators.go

+121-52
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,9 @@ import (
1111
"k8s.io/apimachinery/pkg/runtime/schema"
1212

1313
"github.com/operator-framework/api/pkg/operators/v1alpha1"
14+
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry"
1415
"github.com/operator-framework/operator-registry/pkg/api"
1516
opregistry "github.com/operator-framework/operator-registry/pkg/registry"
16-
17-
"github.com/operator-framework/operator-lifecycle-manager/pkg/controller/registry"
1817
)
1918

2019
type APISet map[opregistry.APIKey]struct{}
@@ -197,6 +196,7 @@ type OperatorSourceInfo struct {
197196
StartingCSV string
198197
Catalog registry.CatalogKey
199198
DefaultChannel bool
199+
Subscription *v1alpha1.Subscription
200200
}
201201

202202
func (i *OperatorSourceInfo) String() string {
@@ -216,7 +216,6 @@ type OperatorSurface interface {
216216
SourceInfo() *OperatorSourceInfo
217217
Bundle() *api.Bundle
218218
Inline() bool
219-
Dependencies() []*api.Dependency
220219
Properties() []*api.Property
221220
Skips() []string
222221
}
@@ -229,7 +228,6 @@ type Operator struct {
229228
version *semver.Version
230229
bundle *api.Bundle
231230
sourceInfo *OperatorSourceInfo
232-
dependencies []*api.Dependency
233231
properties []*api.Property
234232
skips []string
235233
}
@@ -261,21 +259,27 @@ func NewOperatorFromBundle(bundle *api.Bundle, startingCSV string, sourceKey reg
261259
// legacy support - if the api doesn't contain properties/dependencies, build them from required/provided apis
262260
properties := bundle.Properties
263261
if len(properties) == 0 {
264-
properties, err = apisToProperties(provided)
262+
properties, err = providedAPIsToProperties(provided)
265263
if err != nil {
266264
return nil, err
267265
}
268266
}
269-
dependencies := bundle.Dependencies
270-
if len(dependencies) == 0 {
271-
dependencies, err = apisToDependencies(required)
267+
if len(bundle.Dependencies) > 0 {
268+
ps, err := legacyDependenciesToProperties(bundle.Dependencies)
269+
if err != nil {
270+
return nil, fmt.Errorf("failed to translate legacy dependencies to properties: %w", err)
271+
}
272+
properties = append(properties, ps...)
273+
} else {
274+
ps, err := requiredAPIsToProperties(required)
272275
if err != nil {
273276
return nil, err
274277
}
278+
properties = append(properties, ps...)
275279
}
276280

277281
// legacy support - if the grpc api doesn't contain required/provided apis, fallback to csv parsing
278-
if len(required) == 0 && len(provided) == 0 && len(properties) == 0 && len(dependencies) == 0 {
282+
if len(required) == 0 && len(provided) == 0 && len(properties) == 0 {
279283
// fallback to csv parsing
280284
if bundle.CsvJson == "" {
281285
if bundle.GetBundlePath() != "" {
@@ -307,7 +311,6 @@ func NewOperatorFromBundle(bundle *api.Bundle, startingCSV string, sourceKey reg
307311
requiredAPIs: required,
308312
bundle: bundle,
309313
sourceInfo: sourceInfo,
310-
dependencies: dependencies,
311314
properties: properties,
312315
skips: bundle.Skips,
313316
}, nil
@@ -338,23 +341,22 @@ func NewOperatorFromV1Alpha1CSV(csv *v1alpha1.ClusterServiceVersion) (*Operator,
338341
requiredAPIs[opregistry.APIKey{Group: api.Group, Version: api.Version, Kind: api.Kind, Plural: api.Name}] = struct{}{}
339342
}
340343

341-
dependencies, err := apisToDependencies(requiredAPIs)
344+
properties, err := providedAPIsToProperties(providedAPIs)
342345
if err != nil {
343346
return nil, err
344347
}
345-
346-
properties, err := apisToProperties(providedAPIs)
348+
dependencies, err := requiredAPIsToProperties(requiredAPIs)
347349
if err != nil {
348350
return nil, err
349351
}
352+
properties = append(properties, dependencies...)
350353

351354
return &Operator{
352355
name: csv.GetName(),
353356
version: &csv.Spec.Version.Version,
354357
providedAPIs: providedAPIs,
355358
requiredAPIs: requiredAPIs,
356359
sourceInfo: &ExistingOperator,
357-
dependencies: dependencies,
358360
properties: properties,
359361
}, nil
360362
}
@@ -413,48 +415,48 @@ func (o *Operator) Inline() bool {
413415
return o.bundle != nil && o.bundle.GetBundlePath() == ""
414416
}
415417

416-
func (o *Operator) Dependencies() []*api.Dependency {
417-
return o.dependencies
418-
}
419-
420418
func (o *Operator) Properties() []*api.Property {
421419
return o.properties
422420
}
423421

424422
func (o *Operator) DependencyPredicates() (predicates []OperatorPredicate, err error) {
425423
predicates = make([]OperatorPredicate, 0)
426-
for _, d := range o.Dependencies() {
427-
var p OperatorPredicate
428-
if d == nil || d.Type == "" {
429-
continue
430-
}
431-
p, err = PredicateForDependency(d)
424+
for _, property := range o.Properties() {
425+
predicate, err := PredicateForProperty(property)
432426
if err != nil {
433-
return
427+
return nil, err
428+
}
429+
if predicate == nil {
430+
continue
434431
}
435-
predicates = append(predicates, p)
432+
predicates = append(predicates, predicate)
436433
}
437434
return
438435
}
439436

440-
// TODO: this should go in its own dependency/predicate builder package
441-
// TODO: can we make this more extensible, i.e. via cue
442-
func PredicateForDependency(dependency *api.Dependency) (OperatorPredicate, error) {
443-
p, ok := predicates[dependency.Type]
437+
func PredicateForProperty(property *api.Property) (OperatorPredicate, error) {
438+
if property == nil {
439+
return nil, nil
440+
}
441+
p, ok := predicates[property.Type]
444442
if !ok {
445-
return nil, fmt.Errorf("no predicate for dependency type %s", dependency.Type)
443+
return nil, nil
446444
}
447-
return p(dependency.Value)
445+
return p(property.Value)
448446
}
449447

450448
var predicates = map[string]func(string) (OperatorPredicate, error){
451-
opregistry.GVKType: predicateForGVKDependency,
452-
opregistry.PackageType: predicateForPackageDependency,
453-
opregistry.LabelType: predicateForLabelDependency,
449+
"olm.gvk.required": predicateForRequiredGVKProperty,
450+
"olm.package.required": predicateForRequiredPackageProperty,
451+
"olm.label.required": predicateForRequiredLabelProperty,
454452
}
455453

456-
func predicateForGVKDependency(value string) (OperatorPredicate, error) {
457-
var gvk opregistry.GVKDependency
454+
func predicateForRequiredGVKProperty(value string) (OperatorPredicate, error) {
455+
var gvk struct {
456+
Group string `json:"group"`
457+
Version string `json:"version"`
458+
Kind string `json:"kind"`
459+
}
458460
if err := json.Unmarshal([]byte(value), &gvk); err != nil {
459461
return nil, err
460462
}
@@ -465,44 +467,51 @@ func predicateForGVKDependency(value string) (OperatorPredicate, error) {
465467
}), nil
466468
}
467469

468-
func predicateForPackageDependency(value string) (OperatorPredicate, error) {
469-
var pkg opregistry.PackageDependency
470+
func predicateForRequiredPackageProperty(value string) (OperatorPredicate, error) {
471+
var pkg struct {
472+
PackageName string `json:"packageName"`
473+
VersionRange string `json:"versionRange"`
474+
}
470475
if err := json.Unmarshal([]byte(value), &pkg); err != nil {
471476
return nil, err
472477
}
473-
ver, err := semver.ParseRange(pkg.Version)
478+
ver, err := semver.ParseRange(pkg.VersionRange)
474479
if err != nil {
475480
return nil, err
476481
}
477-
478482
return And(WithPackage(pkg.PackageName), WithVersionInRange(ver)), nil
479483
}
480484

481-
func predicateForLabelDependency(value string) (OperatorPredicate, error) {
482-
var label opregistry.LabelDependency
485+
func predicateForRequiredLabelProperty(value string) (OperatorPredicate, error) {
486+
var label struct {
487+
Label string `json:"label"`
488+
}
483489
if err := json.Unmarshal([]byte(value), &label); err != nil {
484490
return nil, err
485491
}
486-
487492
return WithLabel(label.Label), nil
488493
}
489494

490-
func apisToDependencies(apis APISet) (out []*api.Dependency, err error) {
495+
func requiredAPIsToProperties(apis APISet) (out []*api.Property, err error) {
491496
if len(apis) == 0 {
492497
return
493498
}
494-
out = make([]*api.Dependency, 0)
499+
out = make([]*api.Property, 0)
495500
for a := range apis {
496-
val, err := json.Marshal(opregistry.GVKDependency{
501+
val, err := json.Marshal(struct {
502+
Group string `json:"group"`
503+
Version string `json:"version"`
504+
Kind string `json:"kind"`
505+
}{
497506
Group: a.Group,
498-
Kind: a.Kind,
499507
Version: a.Version,
508+
Kind: a.Kind,
500509
})
501510
if err != nil {
502511
return nil, err
503512
}
504-
out = append(out, &api.Dependency{
505-
Type: opregistry.GVKType,
513+
out = append(out, &api.Property{
514+
Type: "olm.gvk.required",
506515
Value: string(val),
507516
})
508517
}
@@ -512,13 +521,13 @@ func apisToDependencies(apis APISet) (out []*api.Dependency, err error) {
512521
return nil, nil
513522
}
514523

515-
func apisToProperties(apis APISet) (out []*api.Property, err error) {
524+
func providedAPIsToProperties(apis APISet) (out []*api.Property, err error) {
516525
out = make([]*api.Property, 0)
517526
for a := range apis {
518527
val, err := json.Marshal(opregistry.GVKProperty{
519528
Group: a.Group,
520-
Kind: a.Kind,
521529
Version: a.Version,
530+
Kind: a.Kind,
522531
})
523532
if err != nil {
524533
panic(err)
@@ -533,3 +542,63 @@ func apisToProperties(apis APISet) (out []*api.Property, err error) {
533542
}
534543
return nil, nil
535544
}
545+
546+
func legacyDependenciesToProperties(dependencies []*api.Dependency) ([]*api.Property, error) {
547+
var result []*api.Property
548+
for _, dependency := range dependencies {
549+
switch dependency.Type {
550+
case "olm.gvk":
551+
type gvk struct {
552+
Group string `json:"group"`
553+
Version string `json:"version"`
554+
Kind string `json:"kind"`
555+
}
556+
var vfrom gvk
557+
if err := json.Unmarshal([]byte(dependency.Value), &vfrom); err != nil {
558+
return nil, fmt.Errorf("failed to unmarshal legacy 'olm.gvk' dependency: %w", err)
559+
}
560+
vto := gvk{
561+
Group: vfrom.Group,
562+
Version: vfrom.Version,
563+
Kind: vfrom.Kind,
564+
}
565+
vb, err := json.Marshal(&vto)
566+
if err != nil {
567+
return nil, fmt.Errorf("unexpected error marshaling generated 'olm.package.required' property: %w", err)
568+
}
569+
result = append(result, &api.Property{
570+
Type: "olm.gvk.required",
571+
Value: string(vb),
572+
})
573+
case "olm.package":
574+
var vfrom struct {
575+
PackageName string `json:"packageName"`
576+
VersionRange string `json:"version"`
577+
}
578+
if err := json.Unmarshal([]byte(dependency.Value), &vfrom); err != nil {
579+
return nil, fmt.Errorf("failed to unmarshal legacy 'olm.package' dependency: %w", err)
580+
}
581+
vto := struct {
582+
PackageName string `json:"packageName"`
583+
VersionRange string `json:"versionRange"`
584+
}{
585+
PackageName: vfrom.PackageName,
586+
VersionRange: vfrom.VersionRange,
587+
}
588+
vb, err := json.Marshal(&vto)
589+
if err != nil {
590+
return nil, fmt.Errorf("unexpected error marshaling generated 'olm.package.required' property: %w", err)
591+
}
592+
result = append(result, &api.Property{
593+
Type: "olm.package.required",
594+
Value: string(vb),
595+
})
596+
case "olm.label":
597+
result = append(result, &api.Property{
598+
Type: "olm.label.required",
599+
Value: dependency.Value,
600+
})
601+
}
602+
}
603+
return result, nil
604+
}

0 commit comments

Comments
 (0)