github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/controllers/storage/storageprovider_controller.go (about)

     1  /*
     2  Copyright (C) 2022-2023 ApeCloud Co., Ltd
     3  
     4  This file is part of KubeBlocks project
     5  
     6  This program is free software: you can redistribute it and/or modify
     7  it under the terms of the GNU Affero General Public License as published by
     8  the Free Software Foundation, either version 3 of the License, or
     9  (at your option) any later version.
    10  
    11  This program is distributed in the hope that it will be useful
    12  but WITHOUT ANY WARRANTY; without even the implied warranty of
    13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    14  GNU Affero General Public License for more details.
    15  
    16  You should have received a copy of the GNU Affero General Public License
    17  along with this program.  If not, see <http://www.gnu.org/licenses/>.
    18  */
    19  
    20  package storage
    21  
    22  import (
    23  	"context"
    24  	"sync"
    25  
    26  	storagev1 "k8s.io/api/storage/v1"
    27  	"k8s.io/apimachinery/pkg/api/meta"
    28  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    29  	"k8s.io/apimachinery/pkg/runtime"
    30  	"k8s.io/client-go/tools/record"
    31  	ctrl "sigs.k8s.io/controller-runtime"
    32  	"sigs.k8s.io/controller-runtime/pkg/client"
    33  	"sigs.k8s.io/controller-runtime/pkg/handler"
    34  	"sigs.k8s.io/controller-runtime/pkg/log"
    35  	"sigs.k8s.io/controller-runtime/pkg/reconcile"
    36  
    37  	storagev1alpha1 "github.com/1aal/kubeblocks/apis/storage/v1alpha1"
    38  	intctrlutil "github.com/1aal/kubeblocks/pkg/controllerutil"
    39  )
    40  
    41  // StorageProviderReconciler reconciles a StorageProvider object
    42  type StorageProviderReconciler struct {
    43  	client.Client
    44  	Scheme   *runtime.Scheme
    45  	Recorder record.EventRecorder
    46  
    47  	mu                 sync.Mutex
    48  	driverDependencies map[string][]string // driver name => list of provider names
    49  }
    50  
    51  // +kubebuilder:rbac:groups=storage.kubeblocks.io,resources=storageproviders,verbs=get;list;watch;create;update;patch;delete
    52  // +kubebuilder:rbac:groups=storage.kubeblocks.io,resources=storageproviders/status,verbs=get;update;patch
    53  // +kubebuilder:rbac:groups=storage.kubeblocks.io,resources=storageproviders/finalizers,verbs=update
    54  
    55  // +kubebuilder:rbac:groups=storage.k8s.io,resources=csidrivers,verbs=get;list;watch
    56  
    57  // Reconcile is part of the main kubernetes reconciliation loop which aims to
    58  // move the current state of the cluster closer to the desired state.
    59  //
    60  // For more details, check Reconcile and its Result here:
    61  // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.14.1/pkg/reconcile
    62  func (r *StorageProviderReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
    63  	logger := log.FromContext(ctx).WithValues("storageprovider", req.NamespacedName)
    64  	reqCtx := intctrlutil.RequestCtx{
    65  		Ctx:      ctx,
    66  		Req:      req,
    67  		Log:      logger,
    68  		Recorder: r.Recorder,
    69  	}
    70  
    71  	// get provider object
    72  	provider := &storagev1alpha1.StorageProvider{}
    73  	if err := r.Get(ctx, req.NamespacedName, provider); err != nil {
    74  		return intctrlutil.CheckedRequeueWithError(err, reqCtx.Log, "failed to get StorageProvider")
    75  	}
    76  
    77  	// add dependency to CSIDrive
    78  	r.ensureDependency(provider)
    79  
    80  	// handle finalizer
    81  	res, err := intctrlutil.HandleCRDeletion(reqCtx, r, provider, storageFinalizerName, func() (*ctrl.Result, error) {
    82  		return nil, r.deleteExternalResources(reqCtx, provider)
    83  	})
    84  	if res != nil {
    85  		return *res, err
    86  	}
    87  
    88  	// check CSI driver if specified
    89  	if provider.Spec.CSIDriverName != "" {
    90  		err := r.checkCSIDriver(reqCtx, provider.Spec.CSIDriverName)
    91  		if err != nil {
    92  			// update status for the CSI driver check
    93  			if updateStatusErr := r.updateStatus(reqCtx, provider, err); updateStatusErr != nil {
    94  				return intctrlutil.CheckedRequeueWithError(updateStatusErr, reqCtx.Log,
    95  					"failed to update status")
    96  			}
    97  			return intctrlutil.CheckedRequeueWithError(err, reqCtx.Log,
    98  				"failed to check CSIDriver %s", provider.Spec.CSIDriverName)
    99  		}
   100  	}
   101  
   102  	// update status
   103  	if updateStatusErr := r.updateStatus(reqCtx, provider, nil); updateStatusErr != nil {
   104  		return intctrlutil.CheckedRequeueWithError(updateStatusErr, reqCtx.Log,
   105  			"failed to update status")
   106  	}
   107  
   108  	return intctrlutil.Reconciled()
   109  }
   110  
   111  func (r *StorageProviderReconciler) updateStatus(reqCtx intctrlutil.RequestCtx,
   112  	provider *storagev1alpha1.StorageProvider,
   113  	checkErr error) error {
   114  	var phase storagev1alpha1.StorageProviderPhase
   115  	var cond metav1.Condition
   116  	if checkErr == nil {
   117  		phase = storagev1alpha1.StorageProviderReady
   118  		cond = metav1.Condition{
   119  			Type:               storagev1alpha1.ConditionTypeCSIDriverInstalled,
   120  			Status:             metav1.ConditionTrue,
   121  			Reason:             CSIDriverObjectFound,
   122  			LastTransitionTime: metav1.Now(),
   123  			ObservedGeneration: provider.Generation,
   124  		}
   125  	} else {
   126  		phase = storagev1alpha1.StorageProviderNotReady
   127  		cond = metav1.Condition{
   128  			Type:               storagev1alpha1.ConditionTypeCSIDriverInstalled,
   129  			Status:             metav1.ConditionUnknown,
   130  			Reason:             CheckCSIDriverFailed,
   131  			Message:            checkErr.Error(),
   132  			LastTransitionTime: metav1.Now(),
   133  			ObservedGeneration: provider.Generation,
   134  		}
   135  	}
   136  
   137  	if phase == provider.Status.Phase {
   138  		return nil
   139  	}
   140  	patch := client.MergeFrom(provider.DeepCopy())
   141  	provider.Status.Phase = phase
   142  	meta.SetStatusCondition(&provider.Status.Conditions, cond)
   143  	return r.Client.Status().Patch(reqCtx.Ctx, provider, patch)
   144  }
   145  
   146  func (r *StorageProviderReconciler) checkCSIDriver(reqCtx intctrlutil.RequestCtx, driverName string) error {
   147  	// check existence of CSIDriver
   148  	return r.Client.Get(reqCtx.Ctx, client.ObjectKey{Name: driverName}, &storagev1.CSIDriver{})
   149  }
   150  
   151  func (r *StorageProviderReconciler) ensureDependency(provider *storagev1alpha1.StorageProvider) {
   152  	if provider.Spec.CSIDriverName == "" {
   153  		return
   154  	}
   155  	r.mu.Lock()
   156  	defer r.mu.Unlock()
   157  	if r.driverDependencies == nil {
   158  		r.driverDependencies = make(map[string][]string)
   159  	}
   160  	driverName := provider.Spec.CSIDriverName
   161  	list := r.driverDependencies[driverName]
   162  	for _, x := range list {
   163  		if x == provider.Name {
   164  			return
   165  		}
   166  	}
   167  	r.driverDependencies[driverName] = append(list, provider.Name)
   168  }
   169  
   170  func (r *StorageProviderReconciler) removeDependency(provider *storagev1alpha1.StorageProvider) {
   171  	if provider.Spec.CSIDriverName == "" {
   172  		return
   173  	}
   174  	r.mu.Lock()
   175  	defer r.mu.Unlock()
   176  	list := r.driverDependencies[provider.Spec.CSIDriverName]
   177  	for i, x := range list {
   178  		if x == provider.Name {
   179  			list[i] = list[len(list)-1]
   180  			r.driverDependencies[provider.Spec.CSIDriverName] = list[:len(list)-1]
   181  			return
   182  		}
   183  	}
   184  }
   185  
   186  func (r *StorageProviderReconciler) deleteExternalResources(
   187  	reqCtx intctrlutil.RequestCtx, provider *storagev1alpha1.StorageProvider) error {
   188  	r.removeDependency(provider)
   189  	return nil
   190  }
   191  
   192  // SetupWithManager sets up the controller with the Manager.
   193  func (r *StorageProviderReconciler) SetupWithManager(mgr ctrl.Manager) error {
   194  	return ctrl.NewControllerManagedBy(mgr).
   195  		For(&storagev1alpha1.StorageProvider{}).
   196  		Watches(&storagev1.CSIDriver{},
   197  			handler.EnqueueRequestsFromMapFunc(func(ctx context.Context, object client.Object) []reconcile.Request {
   198  				r.mu.Lock()
   199  				defer r.mu.Unlock()
   200  				driverName := object.GetName()
   201  				list := r.driverDependencies[driverName]
   202  				var ret []reconcile.Request
   203  				for _, x := range list {
   204  					ret = append(ret, reconcile.Request{
   205  						NamespacedName: client.ObjectKey{
   206  							Name: x,
   207  						},
   208  					})
   209  				}
   210  				return ret
   211  			})).
   212  		Complete(r)
   213  }