github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/controllers/apps/clusterdefinition_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 apps
    21  
    22  import (
    23  	"context"
    24  	"runtime"
    25  	"time"
    26  
    27  	"golang.org/x/exp/slices"
    28  	corev1 "k8s.io/api/core/v1"
    29  	k8sruntime "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/controller"
    34  	"sigs.k8s.io/controller-runtime/pkg/log"
    35  
    36  	appsv1alpha1 "github.com/1aal/kubeblocks/apis/apps/v1alpha1"
    37  	appsconfig "github.com/1aal/kubeblocks/controllers/apps/configuration"
    38  	"github.com/1aal/kubeblocks/pkg/constant"
    39  	intctrlutil "github.com/1aal/kubeblocks/pkg/controllerutil"
    40  	viper "github.com/1aal/kubeblocks/pkg/viperx"
    41  )
    42  
    43  // +kubebuilder:rbac:groups=apps.kubeblocks.io,resources=clusterdefinitions,verbs=get;list;watch;create;update;patch;delete
    44  // +kubebuilder:rbac:groups=apps.kubeblocks.io,resources=clusterdefinitions/status,verbs=get;update;patch
    45  // +kubebuilder:rbac:groups=apps.kubeblocks.io,resources=clusterdefinitions/finalizers,verbs=update
    46  
    47  // ClusterDefinitionReconciler reconciles a ClusterDefinition object
    48  type ClusterDefinitionReconciler struct {
    49  	client.Client
    50  	Scheme   *k8sruntime.Scheme
    51  	Recorder record.EventRecorder
    52  }
    53  
    54  var clusterDefUpdateHandlers = map[string]func(client client.Client, ctx context.Context, clusterDef *appsv1alpha1.ClusterDefinition) error{}
    55  
    56  func init() {
    57  	viper.SetDefault(maxConcurReconClusterDefKey, runtime.NumCPU())
    58  }
    59  
    60  // Reconcile is part of the main kubernetes reconciliation loop which aims to
    61  // move the current state of the cluster closer to the desired state.
    62  //
    63  // For more details, check Reconcile and its Result here:
    64  // - https://pkg.go.dev/sigs.k8s.io/controller-runtime@v0.14.4/pkg/reconcile
    65  func (r *ClusterDefinitionReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) {
    66  	reqCtx := intctrlutil.RequestCtx{
    67  		Ctx:      ctx,
    68  		Req:      req,
    69  		Log:      log.FromContext(ctx).WithValues("clusterDefinition", req.NamespacedName),
    70  		Recorder: r.Recorder,
    71  	}
    72  
    73  	dbClusterDef := &appsv1alpha1.ClusterDefinition{}
    74  	if err := r.Client.Get(reqCtx.Ctx, reqCtx.Req.NamespacedName, dbClusterDef); err != nil {
    75  		return intctrlutil.CheckedRequeueWithError(err, reqCtx.Log, "")
    76  	}
    77  
    78  	res, err := intctrlutil.HandleCRDeletion(reqCtx, r, dbClusterDef, dbClusterDefFinalizerName, func() (*ctrl.Result, error) {
    79  		recordEvent := func() {
    80  			r.Recorder.Event(dbClusterDef, corev1.EventTypeWarning, "ExistsReferencedResources",
    81  				"cannot be deleted because of existing referencing Cluster or ClusterVersion.")
    82  		}
    83  		if res, err := intctrlutil.ValidateReferenceCR(reqCtx, r.Client, dbClusterDef,
    84  			constant.ClusterDefLabelKey, recordEvent, &appsv1alpha1.ClusterList{},
    85  			&appsv1alpha1.ClusterVersionList{}); res != nil || err != nil {
    86  			return res, err
    87  		}
    88  		return nil, r.deleteExternalResources(reqCtx, dbClusterDef)
    89  	})
    90  	if res != nil {
    91  		return *res, err
    92  	}
    93  
    94  	if dbClusterDef.Status.ObservedGeneration == dbClusterDef.Generation &&
    95  		slices.Contains(dbClusterDef.Status.GetTerminalPhases(), dbClusterDef.Status.Phase) {
    96  		return intctrlutil.Reconciled()
    97  	}
    98  
    99  	if err := appsconfig.ReconcileConfigSpecsForReferencedCR(r.Client, reqCtx, dbClusterDef); err != nil {
   100  		return intctrlutil.RequeueAfter(time.Second, reqCtx.Log, err.Error())
   101  	}
   102  
   103  	for _, handler := range clusterDefUpdateHandlers {
   104  		if err := handler(r.Client, reqCtx.Ctx, dbClusterDef); err != nil {
   105  			return intctrlutil.CheckedRequeueWithError(err, reqCtx.Log, "")
   106  		}
   107  	}
   108  
   109  	statusPatch := client.MergeFrom(dbClusterDef.DeepCopy())
   110  	dbClusterDef.Status.ObservedGeneration = dbClusterDef.Generation
   111  	dbClusterDef.Status.Phase = appsv1alpha1.AvailablePhase
   112  	if err = r.Client.Status().Patch(reqCtx.Ctx, dbClusterDef, statusPatch); err != nil {
   113  		return intctrlutil.CheckedRequeueWithError(err, reqCtx.Log, "")
   114  	}
   115  	intctrlutil.RecordCreatedEvent(r.Recorder, dbClusterDef)
   116  	return intctrlutil.Reconciled()
   117  }
   118  
   119  // SetupWithManager sets up the controller with the Manager.
   120  func (r *ClusterDefinitionReconciler) SetupWithManager(mgr ctrl.Manager) error {
   121  	return ctrl.NewControllerManagedBy(mgr).
   122  		For(&appsv1alpha1.ClusterDefinition{}).
   123  		WithOptions(controller.Options{
   124  			MaxConcurrentReconciles: viper.GetInt(maxConcurReconClusterDefKey),
   125  		}).
   126  		Complete(r)
   127  }
   128  
   129  func (r *ClusterDefinitionReconciler) deleteExternalResources(reqCtx intctrlutil.RequestCtx, clusterDef *appsv1alpha1.ClusterDefinition) error {
   130  	//
   131  	// delete any external resources associated with the cronJob
   132  	//
   133  	// Ensure that delete implementation is idempotent and safe to invoke
   134  	// multiple times for same object.
   135  	return appsconfig.DeleteConfigMapFinalizer(r.Client, reqCtx, clusterDef)
   136  }