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 }