Skip to content

Commit 58b5ab1

Browse files
authored
added feature to handle user managed operators (BYO) (#1078)
* fixed make bundle-manifests to check for yq first Signed-off-by: Henry Li <[email protected]> * updated controller-gen to v0.14.0 Signed-off-by: Henry Li <[email protected]> * added UserManaged field to OperandRegistry - for BYO scenario Signed-off-by: Henry Li <[email protected]> * updated operator/operand reconcile for userManaged - so that controllers do not try to create/update/manage Subscription for an operator which is userManaged - so that controllers do not spit out errors as it did previously - so that controllers update OperandRequest with Running status - operands in OperandConfig should still be created - currently missing proper cleanup when OperandRequest is deleted Signed-off-by: Henry Li <[email protected]> * updated user managed logic to properly cleanup Signed-off-by: Henry Li <[email protected]> * increase lint timeout to 300s Signed-off-by: Henry Li <[email protected]> * fixed test regression by adding opreq-control label Signed-off-by: Henry Li <[email protected]> * updated to handle BYO operator in different ns - when the user managed operator is out of the watch scope of ODLM - i.e. ODLM cannot see the Subscription Signed-off-by: Henry Li <[email protected]> * updated DeleteRedundantCSV to ignore any CSVs where olm.copiedFrom label exists Signed-off-by: Henry Li <[email protected]> --------- Signed-off-by: Henry Li <[email protected]>
1 parent 67f51c8 commit 58b5ab1

13 files changed

+233
-80
lines changed

Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -200,7 +200,7 @@ manifests: controller-gen ## Generate manifests e.g. CRD, RBAC etc.
200200
generate: controller-gen ## Generate code e.g. API etc.
201201
$(CONTROLLER_GEN) object:headerFile="hack/boilerplate.go.txt" paths="./..."
202202

203-
bundle-manifests:
203+
bundle-manifests: yq
204204
$(KUSTOMIZE) build config/manifests | $(OPERATOR_SDK) generate bundle \
205205
-q --overwrite --version $(OPERATOR_VERSION) $(BUNDLE_METADATA_OPTS)
206206
$(OPERATOR_SDK) bundle validate ./bundle

api/v1alpha1/operandregistry_types.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,8 @@ type Operator struct {
7676
// OperatorConfig is the name of the OperatorConfig
7777
// +optional
7878
OperatorConfig string `json:"operatorConfig,omitempty"`
79+
// UserManaged is a flag to indicate whether operator is managed by user
80+
UserManaged bool `json:"userManaged,omitempty"`
7981
}
8082

8183
// +kubebuilder:validation:Enum=public;private

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 0 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

bundle/manifests/operand-deployment-lifecycle-manager.clusterserviceversion.yaml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ metadata:
129129
categories: Developer Tools, Monitoring, Logging & Tracing, Security
130130
certified: "false"
131131
containerImage: icr.io/cpopen/odlm:latest
132-
createdAt: "2024-04-04T03:40:45Z"
132+
createdAt: "2024-08-27T18:18:40Z"
133133
description: The Operand Deployment Lifecycle Manager provides a Kubernetes CRD-based API to manage the lifecycle of operands.
134134
nss.operator.ibm.com/managed-operators: ibm-odlm
135135
olm.skipRange: '>=1.2.0 <4.3.5'
@@ -664,15 +664,15 @@ spec:
664664
ephemeral-storage: 256Mi
665665
memory: 200Mi
666666
securityContext:
667-
seccompProfile:
668-
type: RuntimeDefault
669667
allowPrivilegeEscalation: false
670668
capabilities:
671669
drop:
672670
- ALL
673671
privileged: false
674672
readOnlyRootFilesystem: true
675673
runAsNonRoot: true
674+
seccompProfile:
675+
type: RuntimeDefault
676676
serviceAccount: operand-deployment-lifecycle-manager
677677
serviceAccountName: operand-deployment-lifecycle-manager
678678
terminationGracePeriodSeconds: 10

bundle/manifests/operator.ibm.com_operandregistries.yaml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2055,6 +2055,10 @@ spec:
20552055
items:
20562056
type: string
20572057
type: array
2058+
userManaged:
2059+
description: UserManaged is a flag to indicate whether operator
2060+
is managed by user
2061+
type: boolean
20582062
required:
20592063
- channel
20602064
- name

common/Makefile.common.mk

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,7 @@ fetch-test-crds:
8686

8787
CONTROLLER_GEN ?= $(shell pwd)/common/bin/controller-gen
8888
controller-gen: ## Download controller-gen locally if necessary.
89-
$(call go-get-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.6.1)
89+
$(call go-get-tool,$(CONTROLLER_GEN),sigs.k8s.io/controller-tools/cmd/controller-gen@v0.14.0)
9090

9191
KIND ?= $(shell pwd)/common/bin/kind
9292
kind: ## Download kind locally if necessary.

common/scripts/lint_go.sh

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,4 +15,4 @@
1515
# limitations under the License.
1616
#
1717

18-
GOGC=25 golangci-lint run -c ./common/config/.golangci.yml --timeout=180s
18+
GOGC=25 golangci-lint run -c ./common/config/.golangci.yml --timeout=300s

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2051,6 +2051,10 @@ spec:
20512051
items:
20522052
type: string
20532053
type: array
2054+
userManaged:
2055+
description: UserManaged is a flag to indicate whether operator
2056+
is managed by user
2057+
type: boolean
20542058
required:
20552059
- channel
20562060
- name

controllers/operandrequest/operandrequest_controller.go

Lines changed: 13 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -238,18 +238,19 @@ func (r *Reconciler) checkFinalizer(ctx context.Context, requestInstance *operat
238238
for _, m := range requestInstance.Status.Members {
239239
remainingOperands.Add(m.Name)
240240
}
241-
existingSub := &olmv1alpha1.SubscriptionList{}
242-
243-
opts := []client.ListOption{
244-
client.MatchingLabels(map[string]string{constant.OpreqLabel: "true"}),
245-
}
246-
247-
if err := r.Client.List(ctx, existingSub, opts...); err != nil {
248-
return err
249-
}
250-
if len(existingSub.Items) == 0 {
251-
return nil
252-
}
241+
// TODO: update to check OperandRequest status to see if member is user managed or not
242+
// existingSub := &olmv1alpha1.SubscriptionList{}
243+
244+
// opts := []client.ListOption{
245+
// client.MatchingLabels(map[string]string{constant.OpreqLabel: "true"}),
246+
// }
247+
248+
// if err := r.Client.List(ctx, existingSub, opts...); err != nil {
249+
// return err
250+
// }
251+
// if len(existingSub.Items) == 0 {
252+
// return nil
253+
// }
253254
// Delete all the subscriptions that created by current request
254255
if err := r.absentOperatorsAndOperands(ctx, requestInstance, &remainingOperands); err != nil {
255256
return err

controllers/operandrequest/reconcile_operand.go

Lines changed: 68 additions & 52 deletions
Original file line numberDiff line numberDiff line change
@@ -99,53 +99,67 @@ func (r *Reconciler) reconcileOperand(ctx context.Context, requestInstance *oper
9999
namespace := r.GetOperatorNamespace(opdRegistry.InstallMode, opdRegistry.Namespace)
100100

101101
sub, err := r.GetSubscription(ctx, operatorName, namespace, registryInstance.Namespace, opdRegistry.PackageName)
102-
103-
if sub == nil && err == nil {
104-
klog.Warningf("There is no Subscription %s or %s in the namespace %s and %s", operatorName, opdRegistry.PackageName, namespace, registryInstance.Namespace)
105-
continue
106-
107-
} else if err != nil {
102+
if err != nil {
108103
merr.Add(errors.Wrapf(err, "failed to get the Subscription %s in the namespace %s and %s", operatorName, namespace, registryInstance.Namespace))
109104
return merr
110105
}
111106

112-
if _, ok := sub.Labels[constant.OpreqLabel]; !ok {
113-
// Subscription existing and not managed by OperandRequest controller
114-
klog.Warningf("Subscription %s in the namespace %s isn't created by ODLM", sub.Name, sub.Namespace)
115-
}
107+
if !opdRegistry.UserManaged {
108+
if sub == nil {
109+
klog.Warningf("There is no Subscription %s or %s in the namespace %s and %s", operatorName, opdRegistry.PackageName, namespace, registryInstance.Namespace)
110+
continue
111+
}
116112

117-
// It the installplan is not created yet, ODLM will try later
118-
if sub.Status.Install == nil || sub.Status.InstallPlanRef.Name == "" {
119-
klog.Warningf("The Installplan for Subscription %s is not ready. Will check it again", sub.Name)
120-
requestInstance.SetMemberStatus(operand.Name, operatorv1alpha1.OperatorInstalling, "", &r.Mutex)
121-
continue
122-
}
113+
if _, ok := sub.Labels[constant.OpreqLabel]; !ok {
114+
// Subscription existing and not managed by OperandRequest controller
115+
klog.Warningf("Subscription %s in the namespace %s isn't created by ODLM", sub.Name, sub.Namespace)
116+
}
123117

124-
// If the installplan is deleted after is completed, ODLM won't block the CR update.
125-
ipName := sub.Status.InstallPlanRef.Name
126-
ipNamespace := sub.Namespace
127-
ip := &olmv1alpha1.InstallPlan{}
128-
ipKey := types.NamespacedName{
129-
Name: ipName,
130-
Namespace: ipNamespace,
131-
}
132-
if err := r.Client.Get(ctx, ipKey, ip); err != nil {
133-
if !apierrors.IsNotFound(err) {
134-
merr.Add(errors.Wrapf(err, "failed to get Installplan"))
118+
// It the installplan is not created yet, ODLM will try later
119+
if sub.Status.Install == nil || sub.Status.InstallPlanRef.Name == "" {
120+
klog.Warningf("The Installplan for Subscription %s is not ready. Will check it again", sub.Name)
121+
requestInstance.SetMemberStatus(operand.Name, operatorv1alpha1.OperatorInstalling, "", &r.Mutex)
122+
continue
135123
}
136-
} else if ip.Status.Phase == olmv1alpha1.InstallPlanPhaseFailed {
137-
klog.Errorf("installplan %s/%s is failed", ipNamespace, ipName)
138-
requestInstance.SetMemberStatus(operand.Name, operatorv1alpha1.OperatorFailed, "", &r.Mutex)
139-
continue
124+
125+
// If the installplan is deleted after is completed, ODLM won't block the CR update.
126+
ipName := sub.Status.InstallPlanRef.Name
127+
ipNamespace := sub.Namespace
128+
ip := &olmv1alpha1.InstallPlan{}
129+
ipKey := types.NamespacedName{
130+
Name: ipName,
131+
Namespace: ipNamespace,
132+
}
133+
if err := r.Client.Get(ctx, ipKey, ip); err != nil {
134+
if !apierrors.IsNotFound(err) {
135+
merr.Add(errors.Wrapf(err, "failed to get Installplan"))
136+
}
137+
} else if ip.Status.Phase == olmv1alpha1.InstallPlanPhaseFailed {
138+
klog.Errorf("installplan %s/%s is failed", ipNamespace, ipName)
139+
requestInstance.SetMemberStatus(operand.Name, operatorv1alpha1.OperatorFailed, "", &r.Mutex)
140+
continue
141+
}
142+
140143
}
141144

142-
csv, err := r.GetClusterServiceVersion(ctx, sub)
145+
var csv *olmv1alpha1.ClusterServiceVersion
143146

144-
// If can't get CSV, requeue the request
145-
if err != nil {
146-
merr.Add(err)
147-
requestInstance.SetMemberStatus(operand.Name, operatorv1alpha1.OperatorFailed, "", &r.Mutex)
148-
continue
147+
if opdRegistry.UserManaged {
148+
csvList, err := r.GetClusterServiceVersionListFromPackage(ctx, opdRegistry.PackageName, opdRegistry.Namespace)
149+
if err != nil {
150+
merr.Add(err)
151+
requestInstance.SetMemberStatus(operand.Name, operatorv1alpha1.OperatorFailed, "", &r.Mutex)
152+
continue
153+
}
154+
csv = csvList[0]
155+
} else {
156+
csv, err = r.GetClusterServiceVersion(ctx, sub)
157+
// If can't get CSV, requeue the request
158+
if err != nil {
159+
merr.Add(err)
160+
requestInstance.SetMemberStatus(operand.Name, operatorv1alpha1.OperatorFailed, "", &r.Mutex)
161+
continue
162+
}
149163
}
150164

151165
if csv == nil {
@@ -160,20 +174,22 @@ func (r *Reconciler) reconcileOperand(ctx context.Context, requestInstance *oper
160174
continue
161175
}
162176

163-
// find the OperandRequest which has the same operator's channel version as existing subscription.
164-
// ODLM will only reconcile Operand based on OperandConfig for this OperandRequest
165-
var requestList []string
166-
reg, _ := regexp.Compile(`^(.*)\.(.*)\.(.*)\/request`)
167-
for anno, version := range sub.Annotations {
168-
if reg.MatchString(anno) && version == sub.Spec.Channel {
169-
requestList = append(requestList, anno)
177+
if !opdRegistry.UserManaged {
178+
// find the OperandRequest which has the same operator's channel version as existing subscription.
179+
// 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+
}
170186
}
171-
}
172187

173-
if len(requestList) == 0 || !util.Contains(requestList, requestInstance.Namespace+"."+requestInstance.Name+"."+operand.Name+"/request") {
174-
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)
175-
requestInstance.SetMemberStatus(operand.Name, operatorv1alpha1.OperatorFailed, "", &r.Mutex)
176-
continue
188+
if len(requestList) == 0 || !util.Contains(requestList, requestInstance.Namespace+"."+requestInstance.Name+"."+operand.Name+"/request") {
189+
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)
190+
requestInstance.SetMemberStatus(operand.Name, operatorv1alpha1.OperatorFailed, "", &r.Mutex)
191+
continue
192+
}
177193
}
178194

179195
klog.V(3).Info("Generating customresource base on ClusterServiceVersion: ", csv.GetName())
@@ -185,11 +201,11 @@ func (r *Reconciler) reconcileOperand(ctx context.Context, requestInstance *oper
185201
if err == nil {
186202
// Check the requested Service Config if exist in specific OperandConfig
187203
opdConfig := configInstance.GetService(operand.Name)
188-
if opdConfig == nil {
204+
if opdConfig == nil && !opdRegistry.UserManaged {
189205
klog.V(2).Infof("There is no service: %s from the OperandConfig instance: %s/%s, Skip reconciling Operands", operand.Name, registryKey.Namespace, req.Registry)
190206
continue
191207
}
192-
err = r.reconcileCRwithConfig(ctx, opdConfig, configInstance.Name, configInstance.Namespace, csv, requestInstance, operand.Name, sub.Namespace, &r.Mutex)
208+
err = r.reconcileCRwithConfig(ctx, opdConfig, configInstance.Name, configInstance.Namespace, csv, requestInstance, operand.Name, csv.Namespace, &r.Mutex)
193209
if err != nil {
194210
merr.Add(err)
195211
requestInstance.SetMemberStatus(operand.Name, "", operatorv1alpha1.ServiceFailed, &r.Mutex)
@@ -203,7 +219,7 @@ func (r *Reconciler) reconcileOperand(ctx context.Context, requestInstance *oper
203219
}
204220

205221
} else {
206-
err = r.reconcileCRwithRequest(ctx, requestInstance, operand, types.NamespacedName{Name: requestInstance.Name, Namespace: requestInstance.Namespace}, i, sub.Namespace, &r.Mutex)
222+
err = r.reconcileCRwithRequest(ctx, requestInstance, operand, types.NamespacedName{Name: requestInstance.Name, Namespace: requestInstance.Namespace}, i, csv.Namespace, &r.Mutex)
207223
if err != nil {
208224
merr.Add(err)
209225
requestInstance.SetMemberStatus(operand.Name, "", operatorv1alpha1.ServiceFailed, &r.Mutex)

0 commit comments

Comments
 (0)