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  }