Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

controllers: new controller for maintenance mode #264

Draft
wants to merge 1 commit into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -174,6 +174,7 @@ func main() {
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
OperatorNamespace: utils.GetOperatorNamespace(),
AvailableCrds: availCrds,
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "StorageClient")
os.Exit(1)
Expand Down Expand Up @@ -208,6 +209,17 @@ func main() {
os.Exit(1)
}

if availCrds[controller.MaintenanceModeCRDName] {
if err = (&controller.MaintenanceModeReconciler{
Client: mgr.GetClient(),
Scheme: mgr.GetScheme(),
OperatorNamespace: utils.GetOperatorNamespace(),
}).SetupWithManager(mgr); err != nil {
setupLog.Error(err, "unable to create controller", "controller", "MaintenanceMode")
os.Exit(1)
}
}

setupLog.Info("starting manager")
if err := mgr.Start(ctrl.SetupSignalHandler()); err != nil {
setupLog.Error(err, "problem running manager")
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ go 1.22.5
replace (
github.com/portworx/sched-ops => github.com/portworx/sched-ops v0.20.4-openstorage-rc3 // required by Rook v1.12
github.com/red-hat-storage/ocs-client-operator/api => ./api
github.com/red-hat-storage/ocs-operator/services/provider/api/v4 => github.com/rewantsoni/ocs-operator/services/provider/api/v4 v4.0.0-20241107075222-75593ba63c24
vbom.ml/util => github.com/fvbommel/util v0.0.0-20180919145318-efcd4e0f9787
)

Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -325,8 +325,8 @@ github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0leargg
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
github.com/ramendr/ramen/api v0.0.0-20241001141243-29d6f22ad237 h1:ig6ePD0yopC5Qi5BRmhsIsKaOkdsGXTSmG3HTYIpquo=
github.com/ramendr/ramen/api v0.0.0-20241001141243-29d6f22ad237/go.mod h1:nO6VM/+PEhcPGyFIQJdhY6ip822cA61PAy/s6IjenAA=
github.com/red-hat-storage/ocs-operator/services/provider/api/v4 v4.0.0-20241015071140-98c8184c6eec h1:M64BdwKMV3jKxcRsZiaGbRKsvlbhRGVZgcb4V/MFeWk=
github.com/red-hat-storage/ocs-operator/services/provider/api/v4 v4.0.0-20241015071140-98c8184c6eec/go.mod h1:t9GJk69TGXABBF8fFPB+ImpbA9mJyRS86wW6+Qn8xHo=
github.com/rewantsoni/ocs-operator/services/provider/api/v4 v4.0.0-20241107075222-75593ba63c24 h1:xZa3YL6iwcZsLHDfTVJT5PvXa8pv/u8buks/XuY6CEg=
github.com/rewantsoni/ocs-operator/services/provider/api/v4 v4.0.0-20241107075222-75593ba63c24/go.mod h1:t9GJk69TGXABBF8fFPB+ImpbA9mJyRS86wW6+Qn8xHo=
github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4=
github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8=
github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4=
Expand Down
207 changes: 207 additions & 0 deletions internal/controller/maintenancemode_controller.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,207 @@
package controller

import (
"context"
"fmt"
"github.com/go-logr/logr"
ramenv1alpha1 "github.com/ramendr/ramen/api/v1alpha1"
"github.com/red-hat-storage/ocs-client-operator/api/v1alpha1"
providerclient "github.com/red-hat-storage/ocs-operator/services/provider/api/v4/client"
storagev1 "k8s.io/api/storage/v1"
kerrors "k8s.io/apimachinery/pkg/api/errors"
"k8s.io/apimachinery/pkg/runtime"
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/controller/controllerutil"
"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"
"time"
)

const (
maintenanceModeFinalizer = "ocs-client-operator.ocs.openshift.io/maintenance-mode"
ramenReplicationIdLabel = "ramendr.openshift.io/replicationID"

Check failure on line 26 in internal/controller/maintenancemode_controller.go

View workflow job for this annotation

GitHub Actions / golangci-lint

var-naming: const ramenReplicationIdLabel should be ramenReplicationIDLabel (revive)
MaintenanceModeCRDName = "maintenancemodes.ramendr.openshift.io"
)

// MaintenanceModeReconciler reconciles a ClusterVersion object
type MaintenanceModeReconciler struct {
client.Client
OperatorNamespace string
Scheme *runtime.Scheme

log logr.Logger
ctx context.Context
maintenanceMode *ramenv1alpha1.MaintenanceMode
storageClass *storagev1.StorageClass
storageClient *v1alpha1.StorageClient
}

// SetupWithManager sets up the controller with the Manager.
func (r *MaintenanceModeReconciler) SetupWithManager(mgr ctrl.Manager) error {
generationChangePredicate := predicate.GenerationChangedPredicate{}
bldr := ctrl.NewControllerManagedBy(mgr).
For(&ramenv1alpha1.MaintenanceMode{}, builder.WithPredicates(generationChangePredicate)).
Watches(&storagev1.StorageClass{}, &handler.EnqueueRequestForObject{}).
Watches(&v1alpha1.StorageClient{}, &handler.EnqueueRequestForObject{})

return bldr.Complete(r)
}

//+kubebuilder:rbac:groups=ramendr.openshift.io,resources=maintenancemodes,verbs=get;list;update;create;watch;delete
//+kubebuilder:rbac:groups=ramendr.openshift.io,resources=maintenancemodes/status,verbs=get;update;patch
//+kubebuilder:rbac:groups=ramendr.openshift.io,resources=maintenancemodes/finalizers,verbs=update
//+kubebuilder:rbac:groups=ocs.openshift.io,resources=storageclients,verbs=get;list;watch
//+kubebuilder:rbac:groups=storage.k8s.io,resources=storageclasses,verbs=get;list;watch

func (r *MaintenanceModeReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
r.ctx = ctx
r.log = log.FromContext(ctx, "MaintenanceMode", req)
r.log.Info("Reconciling MaintenanceMode")

r.maintenanceMode = &ramenv1alpha1.MaintenanceMode{}
r.maintenanceMode.Name = req.Name
if err := r.get(r.maintenanceMode); err != nil {
if kerrors.IsNotFound(err) {
r.log.Info("Maintenance Mode resource not found. Ignoring since object might be deleted.")
return reconcile.Result{}, nil
}
r.log.Error(err, "failed to get the Maintenance Mode")
return reconcile.Result{}, err
}

err := r.findStorageClassForMaintenanceMode()
if err != nil {
return ctrl.Result{}, fmt.Errorf("failed to find storageClass for maintenance mode: %v ", err)
}

// If no storageClass for the targetID found, exit
if r.storageClass == nil {
r.log.Info("no storage class found for the maintenance mode")
return reconcile.Result{}, nil
}

err = r.findStorageClientLinkedWithStorageClass()
if err != nil {
return ctrl.Result{}, fmt.Errorf("failed to find storageClient for maintenance mode: %v ", err)
}

providerClient, err := providerclient.NewProviderClient(
r.ctx,
r.storageClient.Spec.StorageProviderEndpoint,
10*time.Second,
)
if err != nil {
return reconcile.Result{}, fmt.Errorf("failed to create provider client with endpoint %v: %v", r.storageClient.Spec.StorageProviderEndpoint, err)
}
// Close client-side connections.
defer providerClient.Close()

if r.maintenanceMode.GetDeletionTimestamp().IsZero() {

//ensure finalizer
if controllerutil.AddFinalizer(r.maintenanceMode, maintenanceModeFinalizer) {
r.log.Info("finalizer missing on the Maintenance Mode resource, adding...")
if err := r.Client.Update(r.ctx, r.maintenanceMode); err != nil {
return ctrl.Result{}, err
}
}

if r.maintenanceMode.Status.State != "" {
_, err := providerClient.StartMaintenanceMode(r.ctx, r.storageClient.Status.ConsumerID)
if err != nil {
return ctrl.Result{}, fmt.Errorf("failed to start maintenance mode: %v", err)
}
r.maintenanceMode.Status.State = ramenv1alpha1.MModeStateUnknown
}
response, err := providerClient.GetMaintenanceModeStatus(r.ctx, r.storageClient.Status.ConsumerID)
if err != nil {
return ctrl.Result{}, fmt.Errorf("failed to get maintenance mode status: %v", err)
}
r.maintenanceMode.Status.State = ramenv1alpha1.MModeState(response.MaintenanceStatus)
} else {
// deletion phase
if err := r.deletionPhase(providerClient); err != nil {
return ctrl.Result{}, err
}

//remove finalizer
if controllerutil.RemoveFinalizer(r.maintenanceMode, maintenanceModeFinalizer) {
if err := r.Client.Update(r.ctx, r.maintenanceMode); err != nil {
return ctrl.Result{}, err
}
r.log.Info("finallizer removed successfully")
}
}
return ctrl.Result{}, nil
}

func (r *MaintenanceModeReconciler) deletionPhase(providerClient *providerclient.OCSProviderClient) error {
_, err := providerClient.StopMaintenanceMode(r.ctx, r.storageClient.Status.ConsumerID)
if err != nil {
return err
}

response, err := providerClient.GetMaintenanceModeStatus(r.ctx, r.storageClient.Status.ConsumerID)
if err != nil {
return err
}
r.maintenanceMode.Status.State = ramenv1alpha1.MModeState(response.MaintenanceStatus)
return nil
}

func (r *MaintenanceModeReconciler) findStorageClassForMaintenanceMode() error {
storageClassList := &storagev1.StorageClassList{}

err := r.list(storageClassList)
if err != nil {
r.log.Error(err, "unable to list storage classes")
return err
}

for i := range storageClassList.Items {
storageClass := storageClassList.Items[i]
if storageClass.GetAnnotations()[ramenReplicationIdLabel] == r.maintenanceMode.Spec.TargetID {
r.storageClass = &storageClassList.Items[i]
return nil
}
}
return fmt.Errorf("failed to find storage class for maintenance mode %s", r.maintenanceMode.Spec.TargetID)
}

func (r *MaintenanceModeReconciler) findStorageClientLinkedWithStorageClass() error {
r.storageClient = &v1alpha1.StorageClient{}
val, ok := r.storageClass.GetAnnotations()[storageClientAnnotation]
if !ok {
return fmt.Errorf("no storage client linked to storage class %s", r.storageClass.Name)
}

r.storageClient.Name = val
err := r.get(r.storageClient)
if err != nil {
return fmt.Errorf("failed to get the storage client: %v", err)
}
return nil
}

func (r *MaintenanceModeReconciler) get(obj client.Object, opts ...client.GetOption) error {
return r.Get(r.ctx, client.ObjectKeyFromObject(obj), obj, opts...)
}

func (r *MaintenanceModeReconciler) list(obj client.ObjectList, opts ...client.ListOption) error {
return r.List(r.ctx, obj, opts...)
}

func (r *MaintenanceModeReconciler) update(obj client.Object, opts ...client.UpdateOption) error {

Check failure on line 198 in internal/controller/maintenancemode_controller.go

View workflow job for this annotation

GitHub Actions / golangci-lint

func `(*MaintenanceModeReconciler).update` is unused (unused)
return r.Update(r.ctx, obj, opts...)
}

func (r *MaintenanceModeReconciler) delete(obj client.Object, opts ...client.DeleteOption) error {

Check failure on line 202 in internal/controller/maintenancemode_controller.go

View workflow job for this annotation

GitHub Actions / golangci-lint

func `(*MaintenanceModeReconciler).delete` is unused (unused)
if err := r.Delete(r.ctx, obj, opts...); err != nil && !kerrors.IsNotFound(err) {
return err
}
return nil
}
8 changes: 5 additions & 3 deletions internal/controller/storageclaim_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,9 +53,10 @@ import (
)

const (
storageClaimFinalizer = "storageclaim.ocs.openshift.io"
storageClaimAnnotation = "ocs.openshift.io/storageclaim"
keyRotationAnnotation = "keyrotation.csiaddons.openshift.io/schedule"
storageClaimFinalizer = "storageclaim.ocs.openshift.io"
storageClaimAnnotation = "ocs.openshift.io/storageclaim"
storageClientAnnotation = "ocs.openshift.io/storageclient"
keyRotationAnnotation = "keyrotation.csiaddons.openshift.io/schedule"

pvClusterIDIndexName = "index:persistentVolumeClusterID"
vscClusterIDIndexName = "index:volumeSnapshotContentCSIDriver"
Expand Down Expand Up @@ -392,6 +393,7 @@ func (r *StorageClaimReconciler) reconcilePhases() (reconcile.Result, error) {
storageClass = r.getCephRBDStorageClass(data)
}
utils.AddAnnotation(storageClass, storageClaimAnnotation, r.storageClaim.Name)
utils.AddAnnotation(storageClass, storageClientAnnotation, r.storageClient.Name)
err = r.createOrReplaceStorageClass(storageClass)
if err != nil {
return reconcile.Result{}, fmt.Errorf("failed to create or update StorageClass: %s", err)
Expand Down
19 changes: 15 additions & 4 deletions internal/controller/storageclient_controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import (
"context"
"encoding/json"
"fmt"
extv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
"os"
"strings"
"time"
Expand Down Expand Up @@ -72,12 +73,13 @@ const (
type StorageClientReconciler struct {
ctx context.Context
client.Client
Log klog.Logger
Scheme *runtime.Scheme
Log klog.Logger
Scheme *runtime.Scheme
AvailableCrds map[string]bool
OperatorNamespace string

recorder *utils.EventReporter
storageClient *v1alpha1.StorageClient

OperatorNamespace string
}

// SetupWithManager sets up the controller with the Manager.
Expand Down Expand Up @@ -125,6 +127,15 @@ func (r *StorageClientReconciler) Reconcile(ctx context.Context, req ctrl.Reques
r.Log = log.FromContext(ctx, "StorageClient", req)
r.Log.Info("Reconciling StorageClient")

crd := &metav1.PartialObjectMetadata{}
crd.SetGroupVersionKind(extv1.SchemeGroupVersion.WithKind("CustomResourceDefinition"))
crd.Name = MaintenanceModeCRDName
if err := r.Client.Get(ctx, client.ObjectKeyFromObject(crd), crd); client.IgnoreNotFound(err) != nil {
r.Log.Error(err, "Failed to get CRD", "CRD", crd.Name)
return reconcile.Result{}, err
}
utils.AssertEqual(r.AvailableCrds[crd.Name], crd.UID != "", utils.ExitCodeThatShouldRestartTheProcess)

r.storageClient = &v1alpha1.StorageClient{}
r.storageClient.Name = req.Name
if err = r.get(r.storageClient); err != nil {
Expand Down

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

Loading
Loading