github.com/kubewharf/katalyst-core@v0.5.3/pkg/controller/kcc/kcc.go (about)

     1  /*
     2  Copyright 2022 The Katalyst Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package kcc
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"time"
    23  
    24  	v1 "k8s.io/api/core/v1"
    25  	apiequality "k8s.io/apimachinery/pkg/api/equality"
    26  	apierrors "k8s.io/apimachinery/pkg/api/errors"
    27  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    28  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    29  	"k8s.io/apimachinery/pkg/labels"
    30  	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
    31  	"k8s.io/apimachinery/pkg/util/sets"
    32  	"k8s.io/apimachinery/pkg/util/wait"
    33  	"k8s.io/client-go/tools/cache"
    34  	"k8s.io/client-go/util/retry"
    35  	"k8s.io/client-go/util/workqueue"
    36  	"k8s.io/klog/v2"
    37  	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
    38  
    39  	configapis "github.com/kubewharf/katalyst-api/pkg/apis/config/v1alpha1"
    40  	configinformers "github.com/kubewharf/katalyst-api/pkg/client/informers/externalversions/config/v1alpha1"
    41  	"github.com/kubewharf/katalyst-api/pkg/client/listers/config/v1alpha1"
    42  	kcclient "github.com/kubewharf/katalyst-core/pkg/client"
    43  	"github.com/kubewharf/katalyst-core/pkg/client/control"
    44  	"github.com/kubewharf/katalyst-core/pkg/config/controller"
    45  	"github.com/kubewharf/katalyst-core/pkg/config/generic"
    46  	"github.com/kubewharf/katalyst-core/pkg/consts"
    47  	kcctarget "github.com/kubewharf/katalyst-core/pkg/controller/kcc/target"
    48  	kccutil "github.com/kubewharf/katalyst-core/pkg/controller/kcc/util"
    49  	"github.com/kubewharf/katalyst-core/pkg/metrics"
    50  	"github.com/kubewharf/katalyst-core/pkg/util"
    51  	"github.com/kubewharf/katalyst-core/pkg/util/native"
    52  )
    53  
    54  const (
    55  	kccControllerName = "kcc"
    56  )
    57  
    58  const (
    59  	kccWorkerCount = 1
    60  )
    61  
    62  const (
    63  	kccConditionTypeValidReasonNormal                     = "Normal"
    64  	kccConditionTypeValidReasonPrioritySelectorKeyInvalid = "PrioritySelectorKeyInvalid"
    65  	kccConditionTypeValidReasonTargetTypeOverlap          = "TargetTypeOverlap"
    66  	kccConditionTypeValidReasonTargetTypeNotAllowed       = "TargetTypeNotAllowed"
    67  	kccConditionTypeValidReasonTargetTypeNotExist         = "TargetTypeNotExist"
    68  	kccConditionTypeValidReasonTerminating                = "Terminating"
    69  )
    70  
    71  type KatalystCustomConfigController struct {
    72  	ctx       context.Context
    73  	dryRun    bool
    74  	kccConfig *controller.KCCConfig
    75  
    76  	client              *kcclient.GenericClientSet
    77  	kccControl          control.KCCControl
    78  	unstructuredControl control.UnstructuredControl
    79  
    80  	// katalystCustomConfigLister can list/get KatalystCustomConfig from the shared informer's store
    81  	katalystCustomConfigLister v1alpha1.KatalystCustomConfigLister
    82  	// katalystCustomConfigSyncQueue queue for KatalystCustomConfig
    83  	katalystCustomConfigSyncQueue workqueue.RateLimitingInterface
    84  
    85  	syncedFunc []cache.InformerSynced
    86  
    87  	// targetHandler store gvr kcc and gvr
    88  	targetHandler *kcctarget.KatalystCustomConfigTargetHandler
    89  
    90  	// metricsEmitter for emit metrics
    91  	metricsEmitter metrics.MetricEmitter
    92  }
    93  
    94  func NewKatalystCustomConfigController(
    95  	ctx context.Context,
    96  	genericConf *generic.GenericConfiguration,
    97  	_ *controller.GenericControllerConfiguration,
    98  	kccConfig *controller.KCCConfig,
    99  	client *kcclient.GenericClientSet,
   100  	katalystCustomConfigInformer configinformers.KatalystCustomConfigInformer,
   101  	metricsEmitter metrics.MetricEmitter,
   102  	targetHandler *kcctarget.KatalystCustomConfigTargetHandler,
   103  ) (*KatalystCustomConfigController, error) {
   104  	k := &KatalystCustomConfigController{
   105  		ctx:                           ctx,
   106  		client:                        client,
   107  		dryRun:                        genericConf.DryRun,
   108  		kccConfig:                     kccConfig,
   109  		katalystCustomConfigLister:    katalystCustomConfigInformer.Lister(),
   110  		targetHandler:                 targetHandler,
   111  		katalystCustomConfigSyncQueue: workqueue.NewNamedRateLimitingQueue(workqueue.DefaultControllerRateLimiter(), kccControllerName),
   112  		syncedFunc: []cache.InformerSynced{
   113  			katalystCustomConfigInformer.Informer().HasSynced,
   114  			targetHandler.HasSynced,
   115  		},
   116  	}
   117  
   118  	katalystCustomConfigInformer.Informer().AddEventHandler(cache.ResourceEventHandlerFuncs{
   119  		AddFunc:    k.addKatalystCustomConfigEventHandle,
   120  		UpdateFunc: k.updateKatalystCustomConfigEventHandle,
   121  	})
   122  
   123  	if metricsEmitter == nil {
   124  		k.metricsEmitter = metrics.DummyMetrics{}
   125  	} else {
   126  		k.metricsEmitter = metricsEmitter.WithTags(kccControllerName)
   127  	}
   128  
   129  	k.kccControl = control.DummyKCCControl{}
   130  	k.unstructuredControl = control.DummyUnstructuredControl{}
   131  	if !k.dryRun {
   132  		k.kccControl = control.NewRealKCCControl(client.InternalClient)
   133  		k.unstructuredControl = control.NewRealUnstructuredControl(client.DynamicClient)
   134  	}
   135  
   136  	// register kcc-target informer handler
   137  	targetHandler.RegisterTargetHandler(kccControllerName, k.katalystCustomConfigTargetHandler)
   138  	return k, nil
   139  }
   140  
   141  func (k *KatalystCustomConfigController) Run() {
   142  	defer utilruntime.HandleCrash()
   143  	defer k.katalystCustomConfigSyncQueue.ShutDown()
   144  
   145  	defer klog.Infof("shutting down %s controller", kccControllerName)
   146  
   147  	if !cache.WaitForCacheSync(k.ctx.Done(), k.syncedFunc...) {
   148  		utilruntime.HandleError(fmt.Errorf("unable to sync caches for %s controller", kccControllerName))
   149  		return
   150  	}
   151  	klog.Infof("caches are synced for %s controller", kccControllerName)
   152  	klog.Infof("start %d workers for %s controller", kccWorkerCount, kccControllerName)
   153  
   154  	for i := 0; i < kccWorkerCount; i++ {
   155  		go wait.Until(k.worker, time.Second, k.ctx.Done())
   156  	}
   157  
   158  	<-k.ctx.Done()
   159  }
   160  
   161  func (k *KatalystCustomConfigController) addKatalystCustomConfigEventHandle(obj interface{}) {
   162  	t, ok := obj.(*configapis.KatalystCustomConfig)
   163  	if !ok {
   164  		klog.Errorf("[kcc] cannot convert obj to *KatalystCustomConfig: %v", obj)
   165  		return
   166  	}
   167  
   168  	klog.V(4).Infof("[kcc] notice addition of KatalystCustomConfig %s", native.GenerateUniqObjectNameKey(t))
   169  	k.enqueueKatalystCustomConfig(t)
   170  }
   171  
   172  func (k *KatalystCustomConfigController) updateKatalystCustomConfigEventHandle(_, new interface{}) {
   173  	t, ok := new.(*configapis.KatalystCustomConfig)
   174  	if !ok {
   175  		klog.Errorf("[kcc] cannot convert obj to *KatalystCustomConfig: %v", new)
   176  		return
   177  	}
   178  
   179  	klog.V(4).Infof("[kcc] notice update of KatalystCustomConfig %s", native.GenerateUniqObjectNameKey(t))
   180  	k.enqueueKatalystCustomConfig(t)
   181  }
   182  
   183  func (k *KatalystCustomConfigController) enqueueKatalystCustomConfig(kcc *configapis.KatalystCustomConfig) {
   184  	if kcc == nil {
   185  		klog.Warning("[kcc] trying to enqueue a nil kcc")
   186  		return
   187  	}
   188  
   189  	key, err := cache.DeletionHandlingMetaNamespaceKeyFunc(kcc)
   190  	if err != nil {
   191  		utilruntime.HandleError(fmt.Errorf("couldn't get key for object %#v: %v", kcc, err))
   192  		return
   193  	}
   194  
   195  	k.katalystCustomConfigSyncQueue.Add(key)
   196  
   197  	// if this kcc has same gvr with others, we also enqueue them to reconcile
   198  	if kccKeys := k.targetHandler.GetKCCKeyListByGVR(kcc.Spec.TargetType); len(kccKeys) > 1 {
   199  		klog.Infof("[kcc] kcc %s whose target type is overlap with keys: %s", native.GenerateUniqObjectNameKey(kcc), kccKeys)
   200  		for _, otherKey := range kccKeys {
   201  			if key == otherKey {
   202  				continue
   203  			}
   204  			k.katalystCustomConfigSyncQueue.Add(otherKey)
   205  		}
   206  	}
   207  }
   208  
   209  func (k *KatalystCustomConfigController) worker() {
   210  	for k.processNextKatalystCustomConfigWorkItem() {
   211  	}
   212  }
   213  
   214  func (k *KatalystCustomConfigController) processNextKatalystCustomConfigWorkItem() bool {
   215  	key, quit := k.katalystCustomConfigSyncQueue.Get()
   216  	if quit {
   217  		return false
   218  	}
   219  	defer k.katalystCustomConfigSyncQueue.Done(key)
   220  
   221  	err := k.syncKatalystCustomConfig(key.(string))
   222  	if err == nil {
   223  		k.katalystCustomConfigSyncQueue.Forget(key)
   224  		return true
   225  	}
   226  
   227  	utilruntime.HandleError(fmt.Errorf("sync kcc %q failed with %v", key, err))
   228  	k.katalystCustomConfigSyncQueue.AddRateLimited(key)
   229  
   230  	return true
   231  }
   232  
   233  func (k *KatalystCustomConfigController) syncKatalystCustomConfig(key string) error {
   234  	klog.V(4).Infof("[kcc] processing kcc %s", key)
   235  	kcc, err := k.getKCCByKey(key)
   236  	if apierrors.IsNotFound(err) {
   237  		klog.Warningf("[kcc] kcc %s is not found", key)
   238  		return nil
   239  	} else if err != nil {
   240  		klog.Errorf("[kcc] kcc %s get error: %v", key, err)
   241  		return err
   242  	}
   243  
   244  	// handle kcc deletion
   245  	if kcc.DeletionTimestamp != nil {
   246  		err := k.handleKCCFinalizer(kcc)
   247  		if err != nil {
   248  			return err
   249  		}
   250  		return nil
   251  	}
   252  
   253  	// make sure kcc obj has finalizer to prevent it from being deleted by mistake
   254  	kcc, err = k.ensureKCCFinalizer(kcc)
   255  	if err != nil {
   256  		return err
   257  	}
   258  
   259  	// get related kcc keys of gvr
   260  	kccKeys := k.targetHandler.GetKCCKeyListByGVR(kcc.Spec.TargetType)
   261  	if len(kccKeys) == 0 {
   262  		if !k.kccConfig.ValidAPIGroupSet.Has(kcc.Spec.TargetType.Group) {
   263  			// kcc with not allowed target type, of which api group is not allowed
   264  			return k.updateKCCStatusCondition(kcc, configapis.KatalystCustomConfigConditionTypeValid, v1.ConditionFalse,
   265  				kccConditionTypeValidReasonTargetTypeNotAllowed, fmt.Sprintf("api group %s of target type %s is not in valid api group set %v",
   266  					kcc.Spec.TargetType.Group, kcc.Spec.TargetType, k.kccConfig.ValidAPIGroupSet))
   267  		}
   268  
   269  		// kcc target type is not exist, we will re-enqueue after 30s as
   270  		// crd of the gvr may not have been created yet. Because we not list/watch crd add/update event, we
   271  		// reconcile it periodically to check it whether the gvr is created
   272  		k.katalystCustomConfigSyncQueue.AddAfter(key, 30*time.Second)
   273  
   274  		return k.updateKCCStatusCondition(kcc, configapis.KatalystCustomConfigConditionTypeValid, v1.ConditionFalse,
   275  			kccConditionTypeValidReasonTargetTypeNotExist, fmt.Sprintf("crd of target type %s is not created", kcc.Spec.TargetType))
   276  	} else if len(kccKeys) > 1 {
   277  		// kcc with overlap target type
   278  		// we will check other kcc whether is alive
   279  		aliveKeys := make([]string, 0, len(kccKeys))
   280  		for _, otherKey := range kccKeys {
   281  			if otherKey == key {
   282  				continue
   283  			}
   284  
   285  			otherKCC, err := k.getKCCByKey(otherKey)
   286  			if err != nil && !apierrors.IsNotFound(err) {
   287  				return err
   288  			}
   289  
   290  			if err == nil && otherKCC.GetDeletionTimestamp() == nil {
   291  				aliveKeys = append(aliveKeys, otherKey)
   292  			}
   293  		}
   294  
   295  		if len(aliveKeys) > 0 {
   296  			klog.Errorf("[kcc] kcc %s is overlap with other key: %s", native.GenerateUniqObjectNameKey(kcc), aliveKeys)
   297  			return k.updateKCCStatusCondition(kcc, configapis.KatalystCustomConfigConditionTypeValid, v1.ConditionFalse,
   298  				kccConditionTypeValidReasonTargetTypeOverlap, fmt.Sprintf("it is overlap with other kcc %v", aliveKeys))
   299  		}
   300  	}
   301  
   302  	// check whether kcc node selector allowed key list is valid
   303  	msg, ok := checkNodeLabelSelectorAllowedKeyList(kcc)
   304  	if !ok {
   305  		return k.updateKCCStatusCondition(kcc, configapis.KatalystCustomConfigConditionTypeValid, v1.ConditionFalse,
   306  			kccConditionTypeValidReasonPrioritySelectorKeyInvalid, msg)
   307  	}
   308  
   309  	targetAccessor, ok := k.targetHandler.GetTargetAccessorByGVR(kcc.Spec.TargetType)
   310  	if !ok {
   311  		return fmt.Errorf("cannot get accessor by gvr %s", kcc.Spec.TargetType.String())
   312  	}
   313  
   314  	// get all related targets
   315  	targets, err := targetAccessor.List(labels.Everything())
   316  	if err != nil {
   317  		return err
   318  	}
   319  
   320  	// filter all deleting targets
   321  	targets = native.FilterOutDeletingUnstructured(targets)
   322  
   323  	// collect all invalid configs
   324  	invalidConfigList, err := k.collectInvalidConfigs(targets)
   325  	if err != nil {
   326  		return err
   327  	}
   328  
   329  	oldKCC := kcc.DeepCopy()
   330  	kcc.Status.InvalidTargetConfigList = invalidConfigList
   331  	kcc.Status.ObservedGeneration = kcc.Generation
   332  	setKatalystCustomConfigConditions(kcc, configapis.KatalystCustomConfigConditionTypeValid, v1.ConditionTrue,
   333  		kccConditionTypeValidReasonNormal, "")
   334  	if !apiequality.Semantic.DeepEqual(oldKCC.Status, kcc.Status) {
   335  		_, err = k.kccControl.UpdateKCCStatus(k.ctx, kcc, metav1.UpdateOptions{})
   336  		if err != nil {
   337  			return err
   338  		}
   339  	}
   340  
   341  	return nil
   342  }
   343  
   344  func (k *KatalystCustomConfigController) getKCCByKey(key string) (*configapis.KatalystCustomConfig, error) {
   345  	namespace, name, err := cache.SplitMetaNamespaceKey(key)
   346  	if err != nil {
   347  		klog.Errorf("[kcc] failed to split namespace and name from key %s", key)
   348  		return nil, err
   349  	}
   350  
   351  	return k.client.InternalClient.ConfigV1alpha1().KatalystCustomConfigs(namespace).Get(k.ctx, name, metav1.GetOptions{
   352  		ResourceVersion: "0",
   353  	})
   354  }
   355  
   356  // katalystCustomConfigTargetHandler process object of kcc target type from targetAccessor, and
   357  // KatalystCustomConfigTargetAccessor will call this handler when some update event on target is added.
   358  func (k *KatalystCustomConfigController) katalystCustomConfigTargetHandler(gvr metav1.GroupVersionResource, target *unstructured.Unstructured) error {
   359  	for _, syncFunc := range k.syncedFunc {
   360  		if !syncFunc() {
   361  			return fmt.Errorf("[kcc] informer has not synced")
   362  		}
   363  	}
   364  
   365  	klog.V(4).Infof("[kcc] gvr: %s, target: %s updated", gvr.String(), native.GenerateUniqObjectNameKey(target))
   366  	if target.GetDeletionTimestamp() != nil {
   367  		err := k.handleKCCTargetFinalizer(gvr, target)
   368  		if err != nil {
   369  			return err
   370  		}
   371  		return nil
   372  	}
   373  
   374  	target, err := kccutil.EnsureKCCTargetFinalizer(k.ctx, k.unstructuredControl,
   375  		consts.KatalystCustomConfigTargetFinalizerKCC, gvr, target)
   376  	if err != nil {
   377  		return err
   378  	}
   379  
   380  	// kcc target updated trigger its kcc to reconcile
   381  	if util.ToKCCTargetResource(target).IsUpdated() {
   382  		kccKeys := k.targetHandler.GetKCCKeyListByGVR(gvr)
   383  		for _, key := range kccKeys {
   384  			k.katalystCustomConfigSyncQueue.Add(key)
   385  		}
   386  		return nil
   387  	}
   388  
   389  	return nil
   390  }
   391  
   392  // handleKCCTargetFinalizer enqueue all related kcc to reconcile when a kcc target was deleted
   393  func (k *KatalystCustomConfigController) handleKCCTargetFinalizer(gvr metav1.GroupVersionResource, target *unstructured.Unstructured) error {
   394  	if !controllerutil.ContainsFinalizer(target, consts.KatalystCustomConfigTargetFinalizerKCC) {
   395  		return nil
   396  	}
   397  
   398  	klog.Infof("[kcc] handling gvr: %s kcc target %s finalizer", gvr.String(), native.GenerateUniqObjectNameKey(target))
   399  	kccKeys := k.targetHandler.GetKCCKeyListByGVR(gvr)
   400  	for _, key := range kccKeys {
   401  		k.katalystCustomConfigSyncQueue.Add(key)
   402  	}
   403  
   404  	err := kccutil.RemoveKCCTargetFinalizer(k.ctx, k.unstructuredControl, consts.KatalystCustomConfigTargetFinalizerKCC, gvr, target)
   405  	if err != nil {
   406  		return err
   407  	}
   408  
   409  	klog.Infof("[kcc] success remove gvr: %s kcc target %s finalizer", gvr.String(), native.GenerateUniqObjectNameKey(target))
   410  	return nil
   411  }
   412  
   413  func (k *KatalystCustomConfigController) collectInvalidConfigs(list []*unstructured.Unstructured) ([]string, error) {
   414  	invalidConfigs := sets.String{}
   415  	for _, o := range list {
   416  		if !util.ToKCCTargetResource(o).CheckValid() {
   417  			invalidConfigs.Insert(native.GenerateUniqObjectNameKey(o))
   418  		}
   419  	}
   420  
   421  	return invalidConfigs.List(), nil
   422  }
   423  
   424  func (k *KatalystCustomConfigController) updateKCCStatusCondition(kcc *configapis.KatalystCustomConfig,
   425  	conditionType configapis.KatalystCustomConfigConditionType, status v1.ConditionStatus, reason, message string,
   426  ) error {
   427  	updated := setKatalystCustomConfigConditions(kcc, conditionType, status, reason, message)
   428  	if updated || kcc.Status.ObservedGeneration != kcc.Generation {
   429  		kcc.Status.ObservedGeneration = kcc.Generation
   430  		_, err := k.kccControl.UpdateKCCStatus(k.ctx, kcc, metav1.UpdateOptions{})
   431  		if err != nil {
   432  			return err
   433  		}
   434  	}
   435  
   436  	return nil
   437  }
   438  
   439  // handleKCCFinalizer checks if there still exist CRs for the given kcc
   440  // if true, protect kcc CR from deleting before its configuration CRs been deleted.
   441  func (k *KatalystCustomConfigController) handleKCCFinalizer(kcc *configapis.KatalystCustomConfig) error {
   442  	if !controllerutil.ContainsFinalizer(kcc, consts.KatalystCustomConfigFinalizerKCC) {
   443  		return nil
   444  	}
   445  
   446  	accessor, ok := k.targetHandler.GetTargetAccessorByGVR(kcc.Spec.TargetType)
   447  	if ok {
   448  		// only if accessor of gvr exists, we will check its object whether exists
   449  		list, err := accessor.List(labels.Everything())
   450  		if err != nil {
   451  			return err
   452  		}
   453  
   454  		if len(list) > 0 {
   455  			residueObjNames := sets.String{}
   456  			for _, o := range list {
   457  				residueObjNames.Insert(native.GenerateUniqObjectNameKey(o))
   458  			}
   459  
   460  			return k.updateKCCStatusCondition(kcc, configapis.KatalystCustomConfigConditionTypeValid, v1.ConditionFalse,
   461  				kccConditionTypeValidReasonTerminating, fmt.Sprintf("residue configs: %s", residueObjNames.List()))
   462  		}
   463  	}
   464  
   465  	err := k.removeKCCFinalizer(kcc)
   466  	if err != nil {
   467  		return err
   468  	}
   469  	return nil
   470  }
   471  
   472  func (k *KatalystCustomConfigController) ensureKCCFinalizer(kcc *configapis.KatalystCustomConfig) (*configapis.KatalystCustomConfig, error) {
   473  	err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
   474  		var err, getErr error
   475  		if controllerutil.ContainsFinalizer(kcc, consts.KatalystCustomConfigFinalizerKCC) {
   476  			return nil
   477  		}
   478  
   479  		controllerutil.AddFinalizer(kcc, consts.KatalystCustomConfigFinalizerKCC)
   480  		newKCC, err := k.kccControl.UpdateKCC(k.ctx, kcc, metav1.UpdateOptions{})
   481  		if apierrors.IsConflict(err) {
   482  			newKCC, getErr = k.client.InternalClient.ConfigV1alpha1().KatalystCustomConfigs(kcc.Namespace).Get(k.ctx, kcc.Name, metav1.GetOptions{ResourceVersion: "0"})
   483  			if err != nil {
   484  				return getErr
   485  			}
   486  		}
   487  
   488  		kcc = newKCC
   489  		return err
   490  	})
   491  	if err != nil {
   492  		return nil, err
   493  	}
   494  
   495  	return kcc, nil
   496  }
   497  
   498  func (k *KatalystCustomConfigController) removeKCCFinalizer(kcc *configapis.KatalystCustomConfig) error {
   499  	err := retry.RetryOnConflict(retry.DefaultBackoff, func() error {
   500  		var err, getErr error
   501  		if !controllerutil.ContainsFinalizer(kcc, consts.KatalystCustomConfigFinalizerKCC) {
   502  			return nil
   503  		}
   504  
   505  		controllerutil.RemoveFinalizer(kcc, consts.KatalystCustomConfigFinalizerKCC)
   506  		newKCC, err := k.kccControl.UpdateKCC(k.ctx, kcc, metav1.UpdateOptions{})
   507  		if apierrors.IsConflict(err) {
   508  			newKCC, getErr = k.client.InternalClient.ConfigV1alpha1().KatalystCustomConfigs(kcc.Namespace).Get(k.ctx, kcc.Name, metav1.GetOptions{ResourceVersion: "0"})
   509  			if err != nil {
   510  				return getErr
   511  			}
   512  		}
   513  
   514  		kcc = newKCC
   515  		return err
   516  	})
   517  	if err != nil {
   518  		return err
   519  	}
   520  
   521  	return nil
   522  }
   523  
   524  // checkNodeLabelSelectorAllowedKeyList checks if the priority of NodeLabelSelectorAllowedKeyList is duplicated
   525  func checkNodeLabelSelectorAllowedKeyList(kcc *configapis.KatalystCustomConfig) (string, bool) {
   526  	duplicatedPrioritySet := sets.NewInt32()
   527  	priorityKeyListMap := map[int32]bool{}
   528  	for _, priorityAllowedKeyList := range kcc.Spec.NodeLabelSelectorAllowedKeyList {
   529  		if priorityKeyListMap[priorityAllowedKeyList.Priority] {
   530  			duplicatedPrioritySet.Insert(priorityAllowedKeyList.Priority)
   531  			continue
   532  		}
   533  		priorityKeyListMap[priorityAllowedKeyList.Priority] = true
   534  	}
   535  
   536  	if len(duplicatedPrioritySet) > 0 {
   537  		return fmt.Sprintf("duplicated priority: %v", duplicatedPrioritySet.List()), false
   538  	}
   539  
   540  	return "", true
   541  }
   542  
   543  // setKatalystCustomConfigConditions is used to set conditions for kcc
   544  func setKatalystCustomConfigConditions(
   545  	kcc *configapis.KatalystCustomConfig,
   546  	conditionType configapis.KatalystCustomConfigConditionType,
   547  	conditionStatus v1.ConditionStatus,
   548  	reason, message string,
   549  ) bool {
   550  	var conditionIndex int
   551  	conditions := kcc.Status.Conditions
   552  	for conditionIndex = 0; conditionIndex < len(conditions); conditionIndex++ {
   553  		if conditions[conditionIndex].Type == conditionType {
   554  			break
   555  		}
   556  	}
   557  
   558  	if conditionIndex == len(conditions) {
   559  		conditions = append(conditions, configapis.KatalystCustomConfigCondition{
   560  			Type: conditionType,
   561  		})
   562  	}
   563  
   564  	condition := &conditions[conditionIndex]
   565  	if condition.Status != conditionStatus || condition.Message != message ||
   566  		condition.Reason != reason {
   567  		condition.LastTransitionTime = metav1.NewTime(time.Now())
   568  		condition.Status = conditionStatus
   569  		condition.Reason = reason
   570  		condition.Message = message
   571  		kcc.Status.Conditions = conditions
   572  		return true
   573  	}
   574  
   575  	return false
   576  }