github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/controllers/apps/class_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  	"fmt"
    25  
    26  	k8sruntime "k8s.io/apimachinery/pkg/runtime"
    27  	"k8s.io/client-go/tools/record"
    28  	ctrl "sigs.k8s.io/controller-runtime"
    29  	"sigs.k8s.io/controller-runtime/pkg/client"
    30  	"sigs.k8s.io/controller-runtime/pkg/log"
    31  	"sigs.k8s.io/controller-runtime/pkg/reconcile"
    32  
    33  	appsv1alpha1 "github.com/1aal/kubeblocks/apis/apps/v1alpha1"
    34  	"github.com/1aal/kubeblocks/pkg/class"
    35  	"github.com/1aal/kubeblocks/pkg/constant"
    36  	intctrlutil "github.com/1aal/kubeblocks/pkg/controllerutil"
    37  )
    38  
    39  // +kubebuilder:rbac:groups=apps.kubeblocks.io,resources=componentclassdefinitions,verbs=get;list;watch;create;update;patch;delete
    40  // +kubebuilder:rbac:groups=apps.kubeblocks.io,resources=componentclassdefinitions/status,verbs=get;update;patch
    41  // +kubebuilder:rbac:groups=apps.kubeblocks.io,resources=componentclassdefinitions/finalizers,verbs=update
    42  
    43  type ComponentClassReconciler struct {
    44  	client.Client
    45  	Scheme   *k8sruntime.Scheme
    46  	Recorder record.EventRecorder
    47  }
    48  
    49  func (r *ComponentClassReconciler) Reconcile(ctx context.Context, req reconcile.Request) (reconcile.Result, error) {
    50  	reqCtx := intctrlutil.RequestCtx{
    51  		Ctx:      ctx,
    52  		Req:      req,
    53  		Log:      log.FromContext(ctx).WithValues("classDefinition", req.NamespacedName),
    54  		Recorder: r.Recorder,
    55  	}
    56  
    57  	classDefinition := &appsv1alpha1.ComponentClassDefinition{}
    58  	if err := r.Client.Get(reqCtx.Ctx, reqCtx.Req.NamespacedName, classDefinition); err != nil {
    59  		return intctrlutil.CheckedRequeueWithError(err, reqCtx.Log, "")
    60  	}
    61  
    62  	res, err := intctrlutil.HandleCRDeletion(reqCtx, r, classDefinition, constant.DBClusterFinalizerName, func() (*ctrl.Result, error) {
    63  		// TODO validate if existing cluster reference classes being deleted
    64  		return nil, nil
    65  	})
    66  	if res != nil {
    67  		return *res, err
    68  	}
    69  
    70  	ml := []client.ListOption{
    71  		client.HasLabels{constant.ResourceConstraintProviderLabelKey},
    72  	}
    73  	constraintsList := &appsv1alpha1.ComponentResourceConstraintList{}
    74  	if err := r.Client.List(reqCtx.Ctx, constraintsList, ml...); err != nil {
    75  		return intctrlutil.CheckedRequeueWithError(err, reqCtx.Log, "")
    76  	}
    77  	constraintsMap := make(map[string]appsv1alpha1.ComponentResourceConstraint)
    78  	for idx := range constraintsList.Items {
    79  		cf := constraintsList.Items[idx]
    80  		if _, ok := cf.GetLabels()[constant.ResourceConstraintProviderLabelKey]; !ok {
    81  			continue
    82  		}
    83  		constraintsMap[cf.GetName()] = cf
    84  	}
    85  
    86  	if classDefinition.Status.ObservedGeneration == classDefinition.Generation {
    87  		return intctrlutil.Reconciled()
    88  	}
    89  
    90  	classes, err := class.ParseComponentClasses(*classDefinition)
    91  	if err != nil {
    92  		return intctrlutil.CheckedRequeueWithError(err, reqCtx.Log, "parse component classes failed")
    93  	}
    94  
    95  	patch := client.MergeFrom(classDefinition.DeepCopy())
    96  	var (
    97  		classList       []appsv1alpha1.ComponentClass
    98  		clusterDefRef   = classDefinition.GetLabels()[constant.ClusterDefLabelKey]
    99  		componentDefRef = classDefinition.GetLabels()[constant.KBAppComponentDefRefLabelKey]
   100  	)
   101  
   102  	var rules []appsv1alpha1.ResourceConstraintRule
   103  	for _, constraint := range constraintsMap {
   104  		rules = append(rules, constraint.FindRules(clusterDefRef, componentDefRef)...)
   105  	}
   106  
   107  	for _, v := range classes {
   108  		match := false
   109  		for _, rule := range rules {
   110  			if rule.ValidateResources(v.ToResourceRequirements().Requests) {
   111  				match = true
   112  				break
   113  			}
   114  		}
   115  		if !match {
   116  			return intctrlutil.CheckedRequeueWithError(nil, reqCtx.Log, fmt.Sprintf("class %s does not conform to any constraints", v.Name))
   117  		}
   118  		classList = append(classList, *v)
   119  	}
   120  
   121  	classDefinition.Status.Classes = classList
   122  	classDefinition.Status.ObservedGeneration = classDefinition.Generation
   123  	if err = r.Client.Status().Patch(ctx, classDefinition, patch); err != nil {
   124  		return intctrlutil.CheckedRequeueWithError(err, reqCtx.Log, "patch component class status failed")
   125  	}
   126  
   127  	return intctrlutil.Reconciled()
   128  }
   129  
   130  // SetupWithManager sets up the controller with the Manager.
   131  func (r *ComponentClassReconciler) SetupWithManager(mgr ctrl.Manager) error {
   132  	return ctrl.NewControllerManagedBy(mgr).For(&appsv1alpha1.ComponentClassDefinition{}).Complete(r)
   133  }