Skip to content

Commit 7f5d804

Browse files
authored
implement channel fallback in catalog discovery (#1079)
* implement channel fallback in catalog discovery Signed-off-by: Daniel Fan <[email protected]> * fix lint issue Signed-off-by: Daniel Fan <[email protected]> * add packagemanifest into scheme Signed-off-by: Daniel Fan <[email protected]> --------- Signed-off-by: Daniel Fan <[email protected]>
1 parent 58b5ab1 commit 7f5d804

15 files changed

+767
-151
lines changed

api/v1alpha1/operandregistry_types.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,9 @@ type Operator struct {
5858
PackageName string `json:"packageName"`
5959
// Name of the channel to track.
6060
Channel string `json:"channel"`
61+
// List of channels to fallback when the main channel is not available.
62+
// +optional
63+
FallbackChannels []string `json:"fallbackChannels,omitempty"`
6164
// Description of a common service.
6265
// +optional
6366
Description string `json:"description,omitempty"`

bundle/manifests/operator.ibm.com_operandregistries.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,12 @@ spec:
7070
description:
7171
description: Description of a common service.
7272
type: string
73+
fallbackChannels:
74+
description: List of channels to fallback when the main channel
75+
is not available.
76+
items:
77+
type: string
78+
type: array
7379
installMode:
7480
description: |-
7581
The install mode of an operator, either namespace or cluster.

config/crd/bases/operator.ibm.com_operandregistries.yaml

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,12 @@ spec:
6666
description:
6767
description: Description of a common service.
6868
type: string
69+
fallbackChannels:
70+
description: List of channels to fallback when the main channel
71+
is not available.
72+
items:
73+
type: string
74+
type: array
6975
installMode:
7076
description: |-
7177
The install mode of an operator, either namespace or cluster.

controllers/operandbindinfo/operandbindinfo_suite_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"github.com/onsi/gomega/gexec"
2929
olmv1 "github.com/operator-framework/api/pkg/operators/v1"
3030
olmv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
31+
operatorsv1 "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators/v1"
3132
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
3233
"k8s.io/client-go/rest"
3334
ctrl "sigs.k8s.io/controller-runtime"
@@ -92,6 +93,8 @@ var _ = BeforeSuite(func(done Done) {
9293
Expect(err).NotTo(HaveOccurred())
9394
err = jaegerv1.AddToScheme(clientgoscheme.Scheme)
9495
Expect(err).NotTo(HaveOccurred())
96+
err = operatorsv1.AddToScheme(clientgoscheme.Scheme)
97+
Expect(err).NotTo(HaveOccurred())
9598

9699
k8sClient, err = client.New(cfg, client.Options{Scheme: clientgoscheme.Scheme})
97100
Expect(err).ToNot(HaveOccurred())

controllers/operandconfig/operandconfig_suite_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"github.com/onsi/gomega/gexec"
2929
olmv1 "github.com/operator-framework/api/pkg/operators/v1"
3030
olmv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
31+
operatorsv1 "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators/v1"
3132
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
3233
"k8s.io/client-go/rest"
3334
ctrl "sigs.k8s.io/controller-runtime"
@@ -93,6 +94,8 @@ var _ = BeforeSuite(func(done Done) {
9394
Expect(err).NotTo(HaveOccurred())
9495
err = jaegerv1.AddToScheme(clientgoscheme.Scheme)
9596
Expect(err).NotTo(HaveOccurred())
97+
err = operatorsv1.AddToScheme(clientgoscheme.Scheme)
98+
Expect(err).NotTo(HaveOccurred())
9699

97100
k8sClient, err = client.New(cfg, client.Options{Scheme: clientgoscheme.Scheme})
98101
Expect(err).ToNot(HaveOccurred())

controllers/operandregistry/operandregistry_suite_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"github.com/onsi/gomega/gexec"
2929
olmv1 "github.com/operator-framework/api/pkg/operators/v1"
3030
olmv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
31+
operatorsv1 "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators/v1"
3132
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
3233
"k8s.io/client-go/rest"
3334
ctrl "sigs.k8s.io/controller-runtime"
@@ -92,6 +93,8 @@ var _ = BeforeSuite(func(done Done) {
9293
Expect(err).NotTo(HaveOccurred())
9394
err = jaegerv1.AddToScheme(clientgoscheme.Scheme)
9495
Expect(err).NotTo(HaveOccurred())
96+
err = operatorsv1.AddToScheme(clientgoscheme.Scheme)
97+
Expect(err).NotTo(HaveOccurred())
9598

9699
k8sClient, err = client.New(cfg, client.Options{Scheme: clientgoscheme.Scheme})
97100
Expect(err).ToNot(HaveOccurred())

controllers/operandrequest/operandrequest_suite_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ import (
2828
"github.com/onsi/gomega/gexec"
2929
olmv1 "github.com/operator-framework/api/pkg/operators/v1"
3030
olmv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
31+
operatorsv1 "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators/v1"
3132
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
3233
"k8s.io/client-go/rest"
3334
ctrl "sigs.k8s.io/controller-runtime"
@@ -86,6 +87,8 @@ var _ = BeforeSuite(func(done Done) {
8687
Expect(err).NotTo(HaveOccurred())
8788
err = jaegerv1.AddToScheme(clientgoscheme.Scheme)
8889
Expect(err).NotTo(HaveOccurred())
90+
err = operatorsv1.AddToScheme(clientgoscheme.Scheme)
91+
Expect(err).NotTo(HaveOccurred())
8992

9093
k8sClient, err = client.New(cfg, client.Options{Scheme: clientgoscheme.Scheme})
9194
Expect(err).ToNot(HaveOccurred())

controllers/operandrequest/reconcile_operand.go

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,6 @@ import (
2222
"encoding/hex"
2323
"encoding/json"
2424
"fmt"
25-
"regexp"
2625
"strconv"
2726
"strings"
2827
"sync"
@@ -175,17 +174,10 @@ func (r *Reconciler) reconcileOperand(ctx context.Context, requestInstance *oper
175174
}
176175

177176
if !opdRegistry.UserManaged {
178-
// find the OperandRequest which has the same operator's channel version as existing subscription.
177+
// find the OperandRequest which has the same operator's channel or fallback channels as existing subscription.
179178
// ODLM will only reconcile Operand based on OperandConfig for this OperandRequest
180-
var requestList []string
181-
reg, _ := regexp.Compile(`^(.*)\.(.*)\.(.*)\/request`)
182-
for anno, version := range sub.Annotations {
183-
if reg.MatchString(anno) && version == sub.Spec.Channel {
184-
requestList = append(requestList, anno)
185-
}
186-
}
187-
188-
if len(requestList) == 0 || !util.Contains(requestList, requestInstance.Namespace+"."+requestInstance.Name+"."+operand.Name+"/request") {
179+
channels := []string{opdRegistry.Channel}
180+
if channels = append(channels, opdRegistry.FallbackChannels...); !util.Contains(channels, sub.Spec.Channel) {
189181
klog.Infof("Subscription %s in the namespace %s is NOT managed by %s/%s, Skip reconciling Operands", sub.Name, sub.Namespace, requestInstance.Namespace, requestInstance.Name)
190182
requestInstance.SetMemberStatus(operand.Name, operatorv1alpha1.OperatorFailed, "", &r.Mutex)
191183
continue

controllers/operandrequest/reconcile_operator.go

Lines changed: 10 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,7 @@ func (r *Reconciler) reconcileSubscription(ctx context.Context, requestInstance
238238

239239
// check if sub.Spec.Channel and opt.Channel are valid semantic version
240240
// set annotation channel back to previous one if sub.Spec.Channel is lower than opt.Channel
241+
// To avoid upgrade from one maintenance version to another maintenance version like from v3 to v3.23
241242
subChanel := util.FindSemantic(sub.Spec.Channel)
242243
optChannel := util.FindSemantic(opt.Channel)
243244
if semver.IsValid(subChanel) && semver.IsValid(optChannel) && semver.Compare(subChanel, optChannel) < 0 {
@@ -249,11 +250,11 @@ func (r *Reconciler) reconcileSubscription(ctx context.Context, requestInstance
249250
} else {
250251
requestInstance.SetNotFoundOperatorFromRegistryCondition(operand.Name, operatorv1alpha1.ResourceTypeSub, corev1.ConditionFalse, mu)
251252

252-
if minChannel := util.FindMinSemver(sub.Annotations, sub.Spec.Channel); minChannel != "" {
253+
if minChannel := util.FindMinSemverFromAnnotations(sub.Annotations, sub.Spec.Channel); minChannel != "" {
253254
sub.Spec.Channel = minChannel
254255
}
255256

256-
// update the spec iff channel in sub matches channel in opreg
257+
// update the spec iff channel in sub matches channel
257258
if sub.Spec.Channel == opt.Channel {
258259
isMatchedChannel = true
259260
sub.Spec.CatalogSource = opt.SourceName
@@ -422,7 +423,7 @@ func (r *Reconciler) uninstallOperatorsAndOperands(ctx context.Context, operandN
422423
}
423424
}
424425

425-
uninstallOperator, uninstallOperand := checkSubAnnotationsForUninstall(requestInstance.ObjectMeta.Name, requestInstance.ObjectMeta.Namespace, op.Name, sub)
426+
uninstallOperator, uninstallOperand := checkSubAnnotationsForUninstall(requestInstance.ObjectMeta.Name, requestInstance.ObjectMeta.Namespace, op.Name, op.InstallMode, sub)
426427
if !uninstallOperand && !uninstallOperator {
427428
if err = r.updateSubscription(ctx, requestInstance, sub); err != nil {
428429
requestInstance.SetMemberStatus(op.Name, operatorv1alpha1.OperatorFailed, "", &r.Mutex)
@@ -731,21 +732,15 @@ func CheckSingletonServices(operator string) bool {
731732
// It returns two boolean values: uninstallOperator and uninstallOperand.
732733
// If uninstallOperator is true, it means the operator should be uninstalled.
733734
// If uninstallOperand is true, it means the operand should be uninstalled.
734-
func checkSubAnnotationsForUninstall(reqName, reqNs, opName string, sub *olmv1alpha1.Subscription) (bool, bool) {
735+
func checkSubAnnotationsForUninstall(reqName, reqNs, opName, installMode string, sub *olmv1alpha1.Subscription) (bool, bool) {
735736
uninstallOperator := true
736737
uninstallOperand := true
737738

738-
var curChannel string
739-
if sub.GetAnnotations() != nil {
740-
curChannel = sub.Annotations[reqNs+"."+reqName+"."+opName+"/request"]
741-
}
742-
743739
delete(sub.Annotations, reqNs+"."+reqName+"."+opName+"/request")
744740
delete(sub.Annotations, reqNs+"."+reqName+"."+opName+"/operatorNamespace")
745741

746742
var opreqNsSlice []string
747743
var operatorNameSlice []string
748-
var channelSlice []string
749744
namespaceReg, _ := regexp.Compile(`^(.*)\.(.*)\.(.*)\/operatorNamespace`)
750745
channelReg, _ := regexp.Compile(`^(.*)\.(.*)\.(.*)\/request`)
751746

@@ -755,7 +750,6 @@ func checkSubAnnotationsForUninstall(reqName, reqNs, opName string, sub *olmv1al
755750
}
756751

757752
if channelReg.MatchString(key) {
758-
channelSlice = append(channelSlice, value)
759753
// Extract the operator name from the key
760754
keyParts := strings.Split(key, "/")
761755
annoPrefix := strings.Split(keyParts[0], ".")
@@ -773,10 +767,11 @@ func checkSubAnnotationsForUninstall(reqName, reqNs, opName string, sub *olmv1al
773767
uninstallOperator = false
774768
}
775769

776-
// If the removed/uninstalled <prefix>/request annotation's value is NOT the same as all other <prefix>/request annotation's values.
777-
// or the operator namespace in one of remaining annotation is the same as the operator name in removed/uninstalled <prefix>/request
778-
// the operand should NOT be uninstalled.
779-
if util.Differs(channelSlice, curChannel) || util.Contains(operatorNameSlice, opName) {
770+
// When one of following conditions are met, the operand will NOT be uninstalled:
771+
// 1. operator is not uninstalled AND intallMode is no-op.
772+
// 2. operator is uninstalled AND at least one other <prefix>/operatorNamespace annotation exists.
773+
// 2. remaining <prefix>/request annotation's values contain the same operator name
774+
if (!uninstallOperator && installMode == operatorv1alpha1.InstallModeNoop) || (uninstallOperator && len(opreqNsSlice) != 0) || util.Contains(operatorNameSlice, opName) {
780775
uninstallOperand = false
781776
}
782777

0 commit comments

Comments
 (0)