github.com/1aal/kubeblocks@v0.0.0-20231107070852-e1c03e598921/controllers/apps/transform_utils.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 "reflect" 24 "time" 25 26 corev1 "k8s.io/api/core/v1" 27 "k8s.io/apimachinery/pkg/runtime" 28 "k8s.io/client-go/tools/record" 29 "sigs.k8s.io/controller-runtime/pkg/client" 30 "sigs.k8s.io/controller-runtime/pkg/client/apiutil" 31 32 appsv1alpha1 "github.com/1aal/kubeblocks/apis/apps/v1alpha1" 33 "github.com/1aal/kubeblocks/pkg/constant" 34 intctrlutil "github.com/1aal/kubeblocks/pkg/controllerutil" 35 ) 36 37 func newRequeueError(after time.Duration, reason string) error { 38 return intctrlutil.NewRequeueError(after, reason) 39 } 40 41 func getGVKName(object client.Object, scheme *runtime.Scheme) (*gvkNObjKey, error) { 42 gvk, err := apiutil.GVKForObject(object, scheme) 43 if err != nil { 44 return nil, err 45 } 46 return &gvkNObjKey{ 47 GroupVersionKind: gvk, 48 ObjectKey: client.ObjectKey{ 49 Namespace: object.GetNamespace(), 50 Name: object.GetName(), 51 }, 52 }, nil 53 } 54 55 func getAppInstanceML(cluster appsv1alpha1.Cluster) client.MatchingLabels { 56 return client.MatchingLabels{ 57 constant.AppInstanceLabelKey: cluster.Name, 58 } 59 } 60 61 // getClusterOwningNamespacedObjects reads namespaced objects owned by our cluster with kinds. 62 func getClusterOwningNamespacedObjects(transCtx *clusterTransformContext, 63 cluster appsv1alpha1.Cluster, 64 labels client.MatchingLabels, 65 kinds []client.ObjectList) (clusterOwningObjects, error) { 66 inNS := client.InNamespace(cluster.Namespace) 67 return getClusterOwningObjectsWithOptions(transCtx, kinds, inNS, labels) 68 } 69 70 // getClusterOwningNonNamespacedObjects reads non-namespaced objects owned by our cluster with kinds. 71 func getClusterOwningNonNamespacedObjects(transCtx *clusterTransformContext, 72 _ appsv1alpha1.Cluster, 73 labels client.MatchingLabels, 74 kinds []client.ObjectList) (clusterOwningObjects, error) { 75 return getClusterOwningObjectsWithOptions(transCtx, kinds, labels) 76 } 77 78 // getClusterOwningObjectsWithOptions reads objects owned by our cluster with kinds and specified options. 79 func getClusterOwningObjectsWithOptions(transCtx *clusterTransformContext, 80 kinds []client.ObjectList, 81 opts ...client.ListOption) (clusterOwningObjects, error) { 82 // list what kinds of object cluster owns 83 objs := make(clusterOwningObjects) 84 for _, list := range kinds { 85 if err := transCtx.Client.List(transCtx.Context, list, opts...); err != nil { 86 return nil, err 87 } 88 // reflect get list.Items 89 items := reflect.ValueOf(list).Elem().FieldByName("Items") 90 l := items.Len() 91 for i := 0; i < l; i++ { 92 // get the underlying object 93 object := items.Index(i).Addr().Interface().(client.Object) 94 name, err := getGVKName(object, rscheme) 95 if err != nil { 96 return nil, err 97 } 98 objs[*name] = object 99 } 100 } 101 return objs, nil 102 } 103 104 // sendWaringEventWithError sends a warning event when occurs error. 105 func sendWarningEventWithError( 106 recorder record.EventRecorder, 107 cluster *appsv1alpha1.Cluster, 108 reason string, 109 err error) { 110 // ignore requeue error 111 if err == nil || intctrlutil.IsRequeueError(err) { 112 return 113 } 114 controllerErr := intctrlutil.UnwrapControllerError(err) 115 if controllerErr != nil { 116 reason = string(controllerErr.Type) 117 } 118 recorder.Event(cluster, corev1.EventTypeWarning, reason, err.Error()) 119 } 120 121 func isResourceRequirementsEqual(a, b corev1.ResourceRequirements) bool { 122 return isResourceEqual(a.Requests, b.Requests) && isResourceEqual(a.Limits, b.Limits) 123 } 124 125 func isResourceEqual(a, b corev1.ResourceList) bool { 126 if len(a) != len(b) { 127 return false 128 } 129 for k, v := range a { 130 if !v.Equal(b[k]) { 131 return false 132 } 133 } 134 return true 135 } 136 137 func isVolumeClaimTemplatesEqual(a, b []appsv1alpha1.ClusterComponentVolumeClaimTemplate) bool { 138 if len(a) != len(b) { 139 return false 140 } 141 142 for i := range a { 143 // first check resource requirements 144 c := a[i].DeepCopy() 145 d := b[i].DeepCopy() 146 if !isResourceRequirementsEqual(c.Spec.Resources, d.Spec.Resources) { 147 return false 148 } 149 150 // then clear resource requirements and check other fields 151 c.Spec.Resources = corev1.ResourceRequirements{} 152 d.Spec.Resources = corev1.ResourceRequirements{} 153 if !reflect.DeepEqual(c, d) { 154 return false 155 } 156 } 157 return true 158 }