Skip to content

Commit

Permalink
implement channel fallback in catalog discovery (#1079)
Browse files Browse the repository at this point in the history
* 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]>
  • Loading branch information
Daniel-Fan authored Aug 30, 2024
1 parent 58b5ab1 commit 7f5d804
Show file tree
Hide file tree
Showing 15 changed files with 767 additions and 151 deletions.
3 changes: 3 additions & 0 deletions api/v1alpha1/operandregistry_types.go
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ type Operator struct {
PackageName string `json:"packageName"`
// Name of the channel to track.
Channel string `json:"channel"`
// List of channels to fallback when the main channel is not available.
// +optional
FallbackChannels []string `json:"fallbackChannels,omitempty"`
// Description of a common service.
// +optional
Description string `json:"description,omitempty"`
Expand Down
6 changes: 6 additions & 0 deletions bundle/manifests/operator.ibm.com_operandregistries.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,12 @@ spec:
description:
description: Description of a common service.
type: string
fallbackChannels:
description: List of channels to fallback when the main channel
is not available.
items:
type: string
type: array
installMode:
description: |-
The install mode of an operator, either namespace or cluster.
Expand Down
6 changes: 6 additions & 0 deletions config/crd/bases/operator.ibm.com_operandregistries.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ spec:
description:
description: Description of a common service.
type: string
fallbackChannels:
description: List of channels to fallback when the main channel
is not available.
items:
type: string
type: array
installMode:
description: |-
The install mode of an operator, either namespace or cluster.
Expand Down
3 changes: 3 additions & 0 deletions controllers/operandbindinfo/operandbindinfo_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/onsi/gomega/gexec"
olmv1 "github.com/operator-framework/api/pkg/operators/v1"
olmv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
operatorsv1 "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators/v1"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
ctrl "sigs.k8s.io/controller-runtime"
Expand Down Expand Up @@ -92,6 +93,8 @@ var _ = BeforeSuite(func(done Done) {
Expect(err).NotTo(HaveOccurred())
err = jaegerv1.AddToScheme(clientgoscheme.Scheme)
Expect(err).NotTo(HaveOccurred())
err = operatorsv1.AddToScheme(clientgoscheme.Scheme)
Expect(err).NotTo(HaveOccurred())

k8sClient, err = client.New(cfg, client.Options{Scheme: clientgoscheme.Scheme})
Expect(err).ToNot(HaveOccurred())
Expand Down
3 changes: 3 additions & 0 deletions controllers/operandconfig/operandconfig_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/onsi/gomega/gexec"
olmv1 "github.com/operator-framework/api/pkg/operators/v1"
olmv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
operatorsv1 "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators/v1"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
ctrl "sigs.k8s.io/controller-runtime"
Expand Down Expand Up @@ -93,6 +94,8 @@ var _ = BeforeSuite(func(done Done) {
Expect(err).NotTo(HaveOccurred())
err = jaegerv1.AddToScheme(clientgoscheme.Scheme)
Expect(err).NotTo(HaveOccurred())
err = operatorsv1.AddToScheme(clientgoscheme.Scheme)
Expect(err).NotTo(HaveOccurred())

k8sClient, err = client.New(cfg, client.Options{Scheme: clientgoscheme.Scheme})
Expect(err).ToNot(HaveOccurred())
Expand Down
3 changes: 3 additions & 0 deletions controllers/operandregistry/operandregistry_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/onsi/gomega/gexec"
olmv1 "github.com/operator-framework/api/pkg/operators/v1"
olmv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
operatorsv1 "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators/v1"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
ctrl "sigs.k8s.io/controller-runtime"
Expand Down Expand Up @@ -92,6 +93,8 @@ var _ = BeforeSuite(func(done Done) {
Expect(err).NotTo(HaveOccurred())
err = jaegerv1.AddToScheme(clientgoscheme.Scheme)
Expect(err).NotTo(HaveOccurred())
err = operatorsv1.AddToScheme(clientgoscheme.Scheme)
Expect(err).NotTo(HaveOccurred())

k8sClient, err = client.New(cfg, client.Options{Scheme: clientgoscheme.Scheme})
Expect(err).ToNot(HaveOccurred())
Expand Down
3 changes: 3 additions & 0 deletions controllers/operandrequest/operandrequest_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import (
"github.com/onsi/gomega/gexec"
olmv1 "github.com/operator-framework/api/pkg/operators/v1"
olmv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
operatorsv1 "github.com/operator-framework/operator-lifecycle-manager/pkg/package-server/apis/operators/v1"
clientgoscheme "k8s.io/client-go/kubernetes/scheme"
"k8s.io/client-go/rest"
ctrl "sigs.k8s.io/controller-runtime"
Expand Down Expand Up @@ -86,6 +87,8 @@ var _ = BeforeSuite(func(done Done) {
Expect(err).NotTo(HaveOccurred())
err = jaegerv1.AddToScheme(clientgoscheme.Scheme)
Expect(err).NotTo(HaveOccurred())
err = operatorsv1.AddToScheme(clientgoscheme.Scheme)
Expect(err).NotTo(HaveOccurred())

k8sClient, err = client.New(cfg, client.Options{Scheme: clientgoscheme.Scheme})
Expect(err).ToNot(HaveOccurred())
Expand Down
14 changes: 3 additions & 11 deletions controllers/operandrequest/reconcile_operand.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import (
"encoding/hex"
"encoding/json"
"fmt"
"regexp"
"strconv"
"strings"
"sync"
Expand Down Expand Up @@ -175,17 +174,10 @@ func (r *Reconciler) reconcileOperand(ctx context.Context, requestInstance *oper
}

if !opdRegistry.UserManaged {
// find the OperandRequest which has the same operator's channel version as existing subscription.
// find the OperandRequest which has the same operator's channel or fallback channels as existing subscription.
// ODLM will only reconcile Operand based on OperandConfig for this OperandRequest
var requestList []string
reg, _ := regexp.Compile(`^(.*)\.(.*)\.(.*)\/request`)
for anno, version := range sub.Annotations {
if reg.MatchString(anno) && version == sub.Spec.Channel {
requestList = append(requestList, anno)
}
}

if len(requestList) == 0 || !util.Contains(requestList, requestInstance.Namespace+"."+requestInstance.Name+"."+operand.Name+"/request") {
channels := []string{opdRegistry.Channel}
if channels = append(channels, opdRegistry.FallbackChannels...); !util.Contains(channels, sub.Spec.Channel) {
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)
requestInstance.SetMemberStatus(operand.Name, operatorv1alpha1.OperatorFailed, "", &r.Mutex)
continue
Expand Down
25 changes: 10 additions & 15 deletions controllers/operandrequest/reconcile_operator.go
Original file line number Diff line number Diff line change
Expand Up @@ -238,6 +238,7 @@ func (r *Reconciler) reconcileSubscription(ctx context.Context, requestInstance

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

if minChannel := util.FindMinSemver(sub.Annotations, sub.Spec.Channel); minChannel != "" {
if minChannel := util.FindMinSemverFromAnnotations(sub.Annotations, sub.Spec.Channel); minChannel != "" {
sub.Spec.Channel = minChannel
}

// update the spec iff channel in sub matches channel in opreg
// update the spec iff channel in sub matches channel
if sub.Spec.Channel == opt.Channel {
isMatchedChannel = true
sub.Spec.CatalogSource = opt.SourceName
Expand Down Expand Up @@ -422,7 +423,7 @@ func (r *Reconciler) uninstallOperatorsAndOperands(ctx context.Context, operandN
}
}

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

var curChannel string
if sub.GetAnnotations() != nil {
curChannel = sub.Annotations[reqNs+"."+reqName+"."+opName+"/request"]
}

delete(sub.Annotations, reqNs+"."+reqName+"."+opName+"/request")
delete(sub.Annotations, reqNs+"."+reqName+"."+opName+"/operatorNamespace")

var opreqNsSlice []string
var operatorNameSlice []string
var channelSlice []string
namespaceReg, _ := regexp.Compile(`^(.*)\.(.*)\.(.*)\/operatorNamespace`)
channelReg, _ := regexp.Compile(`^(.*)\.(.*)\.(.*)\/request`)

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

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

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

Expand Down
Loading

0 comments on commit 7f5d804

Please sign in to comment.