Skip to content

Commit

Permalink
feat: Implement reconciliation for ManagedCluster and create ManagedC…
Browse files Browse the repository at this point in the history
…lusterView

- Added reconciliation logic for `ManagedCluster`
- Added tests for `ManagedClusterReconciler` reconciliation logic and ensure ManagedClusterViews function.
- Reconciliation for ManagedCluster creates ManagedClusterView which pulls the ‘odf-info’ configmap onto the hub.
- Updated RBAC rules in `config/rbac/role.yaml` to include permissions for ManagedClusterView resources. (ManagedCluster RBAC already there)
- Plumbing to make the controllers work like the items above
- Updated go.mod and go.sum to include `github.com/stolostron/multicloud-operators-foundation`.
- Fixes to functions, tests and adding new predicate

Signed-off-by: vbadrina <[email protected]>
  • Loading branch information
vbnrh committed Jul 19, 2024
1 parent 6570168 commit d743b7b
Show file tree
Hide file tree
Showing 10 changed files with 575 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ metadata:
]
capabilities: Basic Install
console.openshift.io/plugins: '["odf-multicluster-console"]'
createdAt: "2024-04-03T11:58:23Z"
createdAt: "2024-07-12T13:14:27Z"
olm.skipRange: ""
operators.openshift.io/infrastructure-features: '["disconnected"]'
operators.operatorframework.io/builder: operator-sdk-v1.34.1
Expand Down Expand Up @@ -224,6 +224,16 @@ spec:
- roles
verbs:
- '*'
- apiGroups:
- view.open-cluster-management.io
resources:
- managedclusterviews
verbs:
- create
- get
- list
- update
- watch
- apiGroups:
- work.open-cluster-management.io
resources:
Expand Down
10 changes: 10 additions & 0 deletions config/rbac/role.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -161,6 +161,16 @@ rules:
- roles
verbs:
- '*'
- apiGroups:
- view.open-cluster-management.io
resources:
- managedclusterviews
verbs:
- create
- get
- list
- update
- watch
- apiGroups:
- work.open-cluster-management.io
resources:
Expand Down
137 changes: 137 additions & 0 deletions controllers/managedcluster_controller.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
package controllers

import (
"context"
"fmt"
"log/slog"
"strings"

"github.com/red-hat-storage/odf-multicluster-orchestrator/controllers/utils"
viewv1beta1 "github.com/stolostron/multicloud-operators-foundation/pkg/apis/view/v1beta1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/types"
clusterv1 "open-cluster-management.io/api/cluster/v1"
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/predicate"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
)

type ManagedClusterReconciler struct {
Client client.Client
Logger *slog.Logger
}

const (
OdfInfoClusterClaimNamespacedName = "odfinfo.odf.openshift.io"
)

// +kubebuilder:rbac:groups=view.open-cluster-management.io,resources=managedclusterviews,verbs=get;list;watch;create;update
func (r *ManagedClusterReconciler) Reconcile(ctx context.Context, req reconcile.Request) (reconcile.Result, error) {
logger := r.Logger.With("ManagedCluster", req.NamespacedName)
logger.Info("Reconciling ManagedCluster")

var managedCluster clusterv1.ManagedCluster
if err := r.Client.Get(ctx, req.NamespacedName, &managedCluster); err != nil {
if client.IgnoreNotFound(err) != nil {
logger.Error("Failed to get ManagedCluster", "error", err)
}
return ctrl.Result{}, client.IgnoreNotFound(err)
}

if err := r.processManagedClusterViews(ctx, managedCluster); err != nil {
logger.Error("Failed to ensure ManagedClusterView", "error", err)
return ctrl.Result{}, err
}

logger.Info("Successfully reconciled ManagedCluster", "name", managedCluster.Name)

return ctrl.Result{}, nil
}

func hasRequiredODFKey(mc *clusterv1.ManagedCluster) bool {
claims := mc.Status.ClusterClaims
for _, claim := range claims {
if claim.Name == OdfInfoClusterClaimNamespacedName {
return true
}
}
return false

}
func (r *ManagedClusterReconciler) SetupWithManager(mgr ctrl.Manager) error {
r.Logger.Info("Setting up ManagedClusterReconciler with manager")
managedClusterPredicate := predicate.Funcs{
UpdateFunc: func(e event.UpdateEvent) bool {
obj, ok := e.ObjectNew.(*clusterv1.ManagedCluster)
if !ok {
return false
}
return hasRequiredODFKey(obj)
},
CreateFunc: func(e event.CreateEvent) bool {
obj, ok := e.Object.(*clusterv1.ManagedCluster)
if !ok {
return false
}
return hasRequiredODFKey(obj)
},

DeleteFunc: func(e event.DeleteEvent) bool {
return false
},
GenericFunc: func(e event.GenericEvent) bool {
return false
},
}

return ctrl.NewControllerManagedBy(mgr).
For(&clusterv1.ManagedCluster{}, builder.WithPredicates(managedClusterPredicate, predicate.ResourceVersionChangedPredicate{})).
Owns(&viewv1beta1.ManagedClusterView{}).
Complete(r)
}

func (r *ManagedClusterReconciler) processManagedClusterViews(ctx context.Context, managedCluster clusterv1.ManagedCluster) error {
resourceType := "ConfigMap"
odfInfoConfigMapNamespacedName, err := getNamespacedNameForClusterInfo(managedCluster)
if err != nil {
return fmt.Errorf("error while getting NamespacedName of the %s. %w", resourceType, err)
}

enabled := true
disabled := false
mcvOwnerRef := &metav1.OwnerReference{
APIVersion: managedCluster.APIVersion,
Kind: managedCluster.Kind,
UID: managedCluster.UID,
Name: managedCluster.Name,
Controller: &enabled,
BlockOwnerDeletion: &disabled,
}

mcv, operationResult, err := utils.CreateOrUpdateManagedClusterView(ctx, r.Client, odfInfoConfigMapNamespacedName.Name, odfInfoConfigMapNamespacedName.Namespace, resourceType, managedCluster.Name, mcvOwnerRef)
if err != nil {
return fmt.Errorf("failed to create or update ManagedClusterView. %w", err)

}
r.Logger.Info(fmt.Sprintf("ManagedClusterView was %s", operationResult), "ManagedClusterView", mcv.Name)

return nil
}

func getNamespacedNameForClusterInfo(managedCluster clusterv1.ManagedCluster) (types.NamespacedName, error) {
clusterClaims := managedCluster.Status.ClusterClaims
for _, claim := range clusterClaims {
if claim.Name == OdfInfoClusterClaimNamespacedName {
namespacedName := strings.Split(claim.Value, "/")
if len(namespacedName) != 2 {
return types.NamespacedName{}, fmt.Errorf("invalid format for namespaced name claim: expected 'namespace/name', got '%s'", claim.Value)
}
return types.NamespacedName{Namespace: namespacedName[0], Name: namespacedName[1]}, nil
}
}

return types.NamespacedName{}, fmt.Errorf("cannot find ClusterClaim %q in ManagedCluster status", OdfInfoClusterClaimNamespacedName)
}
Loading

0 comments on commit d743b7b

Please sign in to comment.