Skip to content

Commit

Permalink
Merge pull request #358 from openshift-cherrypick-robot/cherry-pick-3…
Browse files Browse the repository at this point in the history
…55-to-release-4.15

Bug 2255310:[release-4.15] Surface dependent Operator upgradeable conditions
  • Loading branch information
openshift-merge-bot[bot] authored Dec 20, 2023
2 parents 17e2881 + 9ce106b commit 74c8734
Show file tree
Hide file tree
Showing 24 changed files with 1,052 additions and 13 deletions.
10 changes: 9 additions & 1 deletion bundle/manifests/odf-operator.clusterserviceversion.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ metadata:
categories: Storage
console.openshift.io/plugins: '["odf-console"]'
containerImage: quay.io/ocs-dev/odf-operator:latest
createdAt: "2023-11-28T10:45:09Z"
createdAt: "2023-12-12T05:53:20Z"
description: OpenShift Data Foundation provides a common control plane for storage
solutions on OpenShift Container Platform.
features.operators.openshift.io/token-auth-aws: "true"
Expand Down Expand Up @@ -336,6 +336,14 @@ spec:
- patch
- update
- watch
- apiGroups:
- operators.coreos.com
resources:
- operatorconditions
verbs:
- get
- list
- watch
- apiGroups:
- operators.coreos.com
resources:
Expand Down
8 changes: 8 additions & 0 deletions config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,14 @@ rules:
- patch
- update
- watch
- apiGroups:
- operators.coreos.com
resources:
- operatorconditions
verbs:
- get
- list
- watch
- apiGroups:
- operators.coreos.com
resources:
Expand Down
146 changes: 144 additions & 2 deletions controllers/subscription_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -23,30 +23,40 @@ import (
"github.com/go-logr/logr"
"go.uber.org/multierr"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
ctrl "sigs.k8s.io/controller-runtime"
"sigs.k8s.io/controller-runtime/pkg/builder"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/log"
"sigs.k8s.io/controller-runtime/pkg/predicate"
"sigs.k8s.io/controller-runtime/pkg/reconcile"

operatorv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
operatorv2 "github.com/operator-framework/api/pkg/operators/v2"
"github.com/operator-framework/operator-lib/conditions"
odfv1alpha1 "github.com/red-hat-storage/odf-operator/api/v1alpha1"
"github.com/red-hat-storage/odf-operator/pkg/util"
)

// SubscriptionReconciler reconciles a Subscription object
type SubscriptionReconciler struct {
client.Client
Scheme *runtime.Scheme
Recorder *EventReporter
Scheme *runtime.Scheme
Recorder *EventReporter
ConditionName string
OperatorCondition conditions.Condition
}

//+kubebuilder:rbac:groups=operators.coreos.com,resources=subscriptions,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=operators.coreos.com,resources=subscriptions/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=operators.coreos.com,resources=subscriptions/finalizers,verbs=update
//+kubebuilder:rbac:groups=operators.coreos.com,resources=installplans,verbs=get;list;watch;create;update;patch;delete
//+kubebuilder:rbac:groups=operators.coreos.com,resources=clusterserviceversions/finalizers,verbs=update
//+kubebuilder:rbac:groups=operators.coreos.com,resources=operatorconditions,verbs=get;list;watch

// For more details, check Reconcile and its Result here:
// - https://pkg.go.dev/sigs.k8s.io/[email protected]/pkg/reconcile
Expand All @@ -65,6 +75,11 @@ func (r *SubscriptionReconciler) Reconcile(ctx context.Context, req ctrl.Request
return ctrl.Result{}, err
}

err = r.setOperatorCondition(logger, req.NamespacedName.Namespace)
if err != nil {
return ctrl.Result{}, err
}

err = r.ensureSubscriptions(logger, req.NamespacedName.Namespace)
if err != nil {
return ctrl.Result{}, err
Expand Down Expand Up @@ -117,6 +132,45 @@ func (r *SubscriptionReconciler) ensureSubscriptions(logger logr.Logger, namespa
return err
}

func (r *SubscriptionReconciler) setOperatorCondition(logger logr.Logger, namespace string) error {
ocdList := &operatorv2.OperatorConditionList{}
err := r.Client.List(context.TODO(), ocdList, client.InNamespace(namespace))
if err != nil {
logger.Error(err, "failed to list OperatorConditions")
return err
}

condNames := append(GetVendorCsvNames(StorageClusterKind), GetVendorCsvNames(FlashSystemKind)...)

condMap := make(map[string]struct{}, len(condNames))
for i := range condNames {
condMap[condNames[i]] = struct{}{}
}

for ocdIdx := range ocdList.Items {
// skip operatorconditions of not dependent operators
if _, exist := condMap[ocdList.Items[ocdIdx].GetName()]; !exist {
continue
}

ocd := &ocdList.Items[ocdIdx]
cond := getNotUpgradeableCond(ocd)
if cond != nil {
// operator is not upgradeable
msg := fmt.Sprintf("%s:%s", ocd.GetName(), cond.Message)
logger.Info("setting operator upgradeable status", "status", cond.Status)
return r.OperatorCondition.Set(context.TODO(), cond.Status,
conditions.WithReason(cond.Reason), conditions.WithMessage(msg))
}
}

// all operators are upgradeable
status := metav1.ConditionTrue
logger.Info("setting operator upgradeable status", "status", status)
return r.OperatorCondition.Set(context.TODO(), status,
conditions.WithReason("Dependents"), conditions.WithMessage("No dependent reports not upgradeable status"))
}

// SetupWithManager sets up the controller with the Manager.
func (r *SubscriptionReconciler) SetupWithManager(mgr ctrl.Manager) error {

Expand Down Expand Up @@ -151,10 +205,98 @@ func (r *SubscriptionReconciler) SetupWithManager(mgr ctrl.Manager) error {
},
}

conditionPredicate := predicate.Funcs{
CreateFunc: func(e event.CreateEvent) bool {
return false
},
UpdateFunc: func(e event.UpdateEvent) bool {
// not mandatory but these checks wouldn't harm
if e.ObjectOld == nil || e.ObjectNew == nil {
return false
}
oldObj, _ := e.ObjectOld.(*operatorv2.OperatorCondition)
newObj, _ := e.ObjectNew.(*operatorv2.OperatorCondition)
if oldObj == nil || newObj == nil {
return false
}

// skip sending a reconcile event if our own condition is updated
if newObj.GetName() == r.ConditionName {
return false
}

// change in admin set conditions for upgradeability
oldOverride := util.Find(oldObj.Spec.Overrides, func(cond *metav1.Condition) bool {
return cond.Type == operatorv2.Upgradeable
})
newOverride := util.Find(newObj.Spec.Overrides, func(cond *metav1.Condition) bool {
return cond.Type == operatorv2.Upgradeable
})
if oldOverride != nil && newOverride == nil {
// override is removed
return true
}
if newOverride != nil {
if oldOverride == nil {
return true
}
return oldOverride.Status != newOverride.Status
}

// change in operator set conditions for upgradeability
oldCond := util.Find(oldObj.Status.Conditions, func(cond *metav1.Condition) bool {
return cond.Type == operatorv2.Upgradeable
})
newCond := util.Find(newObj.Status.Conditions, func(cond *metav1.Condition) bool {
return cond.Type == operatorv2.Upgradeable
})
if newCond != nil {
if oldCond == nil {
return true
}
return oldCond.Status != newCond.Status
}

return false
},
}
enqueueFromCondition := handler.EnqueueRequestsFromMapFunc(
func(ctx context.Context, obj client.Object) []reconcile.Request {
if _, ok := obj.(*operatorv2.OperatorCondition); !ok {
return []reconcile.Request{}
}
logger := log.FromContext(ctx)
sub, err := GetOdfSubscription(r.Client)
if err != nil {
logger.Error(err, "failed to get ODF Subscription")
return []reconcile.Request{}
}
return []reconcile.Request{{NamespacedName: types.NamespacedName{Name: sub.Name, Namespace: sub.Namespace}}}
},
)

return ctrl.NewControllerManagedBy(mgr).
For(&operatorv1alpha1.Subscription{},
builder.WithPredicates(generationChangedPredicate, subscriptionPredicate)).
Owns(&operatorv1alpha1.Subscription{},
builder.WithPredicates(generationChangedPredicate)).
Watches(&operatorv2.OperatorCondition{}, enqueueFromCondition, builder.WithPredicates(conditionPredicate)).
Complete(r)
}

func getNotUpgradeableCond(ocd *operatorv2.OperatorCondition) *metav1.Condition {
cond := util.Find(ocd.Spec.Overrides, func(cd *metav1.Condition) bool {
return cd.Type == operatorv2.Upgradeable
})
if cond != nil {
if cond.Status != "True" {
return cond
}
// if upgradeable is overridden we should skip checking operator set conditions
return nil
}

return util.Find(ocd.Status.Conditions, func(cd *metav1.Condition) bool {
return cd.Type == operatorv2.Upgradeable && cd.Status != "True"
})
}
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ require (
github.com/onsi/gomega v1.27.10
github.com/openshift/api v0.0.0-20231212225112-7cca8a108d7b
github.com/openshift/custom-resource-status v1.1.2
github.com/operator-framework/api v0.17.7-0.20230626210316-aa3e49803e7b
github.com/operator-framework/api v0.20.0
github.com/prometheus/client_golang v1.17.0
github.com/red-hat-storage/ocs-operator/api/v4 v4.0.0-20231218123823-334ed88d3c2c
github.com/stretchr/testify v1.8.4
Expand Down Expand Up @@ -73,6 +73,7 @@ require (
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/noobaa/noobaa-operator/v5 v5.0.0-20231213124549-5d7b0417716d // indirect
github.com/operator-framework/operator-lib v0.11.1-0.20230717184314-6efbe3a22f6f
github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
Expand Down
6 changes: 4 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -647,8 +647,10 @@ github.com/openshift/build-machinery-go v0.0.0-20200917070002-f171684f77ab/go.mo
github.com/openshift/client-go v0.0.0-20210112165513-ebc401615f47/go.mod h1:u7NRAjtYVAKokiI9LouzTv4mhds8P4S1TwdVAfbjKSk=
github.com/openshift/custom-resource-status v1.1.2 h1:C3DL44LEbvlbItfd8mT5jWrqPfHnSOQoQf/sypqA6A4=
github.com/openshift/custom-resource-status v1.1.2/go.mod h1:DB/Mf2oTeiAmVVX1gN+NEqweonAPY0TKUwADizj8+ZA=
github.com/operator-framework/api v0.17.7-0.20230626210316-aa3e49803e7b h1:prJEMyFQde4yxxaTuvqx1A/ukuCg/EZ2MbfdZiJwlls=
github.com/operator-framework/api v0.17.7-0.20230626210316-aa3e49803e7b/go.mod h1:lnurXgadLnoZ7pufKMHkErr2BVOIZSpHtvEkHBcKvdk=
github.com/operator-framework/api v0.20.0 h1:A2YCRhr+6s0k3pRJacnwjh1Ue8BqjIGuQ2jvPg9XCB4=
github.com/operator-framework/api v0.20.0/go.mod h1:rXPOhrQ6mMeXqCmpDgt1ALoar9ZlHL+Iy5qut9R99a4=
github.com/operator-framework/operator-lib v0.11.1-0.20230717184314-6efbe3a22f6f h1:CQkdjRsbPtDd3YvENaPMZzw1eHPfujiZTrCzzSCPCsw=
github.com/operator-framework/operator-lib v0.11.1-0.20230717184314-6efbe3a22f6f/go.mod h1:fmVTfDgR/OMPg7eJvXWlyBVzCXUfHAOxIXO8W51HvKY=
github.com/pascaldekloe/goe v0.0.0-20180627143212-57f6aae5913c/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
github.com/pborman/uuid v0.0.0-20170612153648-e790cca94e6c/go.mod h1:VyrYX9gd7irzKovcSS6BIIEwPRkP2Wm2m9ufcdFSJ34=
github.com/pborman/uuid v1.2.0 h1:J7Q5mO4ysT1dv8hyrUGHb9+ooztCXu1D8MY8DZYsu3g=
Expand Down
18 changes: 16 additions & 2 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/log/zap"

operatorv1alpha1 "github.com/operator-framework/api/pkg/operators/v1alpha1"
operatorv2 "github.com/operator-framework/api/pkg/operators/v2"

ibmv1alpha1 "github.com/IBM/ibm-storage-odf-operator/api/v1alpha1"
ocsv1 "github.com/red-hat-storage/ocs-operator/api/v4/v1"
Expand Down Expand Up @@ -62,6 +63,7 @@ func init() {
utilruntime.Must(ibmv1alpha1.AddToScheme(scheme))

utilruntime.Must(operatorv1alpha1.AddToScheme(scheme))
utilruntime.Must(operatorv2.AddToScheme(scheme))
//+kubebuilder:scaffold:scheme

utilruntime.Must(consolev1.AddToScheme(scheme))
Expand Down Expand Up @@ -126,9 +128,21 @@ func main() {
os.Exit(1)
}

conditionName, err := util.GetConditionName(mgr.GetClient())
if err != nil {
setupLog.Error(err, "unable to get condition name")
os.Exit(1)
}
condition, err := util.NewUpgradeableCondition(mgr.GetClient())
if err != nil {
setupLog.Error(err, "unable to get OperatorCondition")
os.Exit(1)
}
subscriptionReconciler := &controllers.SubscriptionReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
ConditionName: conditionName,
OperatorCondition: condition,
}
if err = subscriptionReconciler.SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "Subscription")
Expand Down
18 changes: 18 additions & 0 deletions pkg/util/openshift.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ import (
"context"

configv1 "github.com/openshift/api/config/v1"
operatorv2 "github.com/operator-framework/api/pkg/operators/v2"
"github.com/operator-framework/operator-lib/conditions"
"sigs.k8s.io/controller-runtime/pkg/client"
)

Expand All @@ -35,3 +37,19 @@ func DetermineOpenShiftVersion(client client.Client) (string, error) {
}
return clusterVersion, nil
}

func getConditionFactory(client client.Client) conditions.Factory {
return conditions.InClusterFactory{Client: client}
}

func GetConditionName(client client.Client) (string, error) {
namespacedName, err := getConditionFactory(client).GetNamespacedName()
if err != nil {
return "", err
}
return namespacedName.Name, nil
}

func NewUpgradeableCondition(client client.Client) (conditions.Condition, error) {
return getConditionFactory(client).NewCondition(operatorv2.ConditionType(operatorv2.Upgradeable))
}
11 changes: 11 additions & 0 deletions pkg/util/strings.go
Original file line number Diff line number Diff line change
Expand Up @@ -36,3 +36,14 @@ func RemoveFromSlice(slice []string, s string) (result []string) {
}
return
}

// Find returns the first entry matching the function "f" or else return nil
func Find[T any](list []T, f func(item *T) bool) *T {
for idx := range list {
ele := &list[idx]
if f(ele) {
return ele
}
}
return nil
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 74c8734

Please sign in to comment.