github.com/kubewharf/katalyst-core@v0.5.3/pkg/controller/kcc/kcct.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  	"k8s.io/apimachinery/pkg/selection"
    31  	utilruntime "k8s.io/apimachinery/pkg/util/runtime"
    32  	"k8s.io/apimachinery/pkg/util/sets"
    33  	"k8s.io/apimachinery/pkg/util/wait"
    34  	"k8s.io/client-go/tools/cache"
    35  	"k8s.io/klog/v2"
    36  	"sigs.k8s.io/controller-runtime/pkg/controller/controllerutil"
    37  
    38  	configapis "github.com/kubewharf/katalyst-api/pkg/apis/config/v1alpha1"
    39  	configinformers "github.com/kubewharf/katalyst-api/pkg/client/informers/externalversions/config/v1alpha1"
    40  	"github.com/kubewharf/katalyst-api/pkg/client/listers/config/v1alpha1"
    41  	kcclient "github.com/kubewharf/katalyst-core/pkg/client"
    42  	"github.com/kubewharf/katalyst-core/pkg/client/control"
    43  	"github.com/kubewharf/katalyst-core/pkg/config/controller"
    44  	"github.com/kubewharf/katalyst-core/pkg/config/generic"
    45  	"github.com/kubewharf/katalyst-core/pkg/consts"
    46  	kcctarget "github.com/kubewharf/katalyst-core/pkg/controller/kcc/target"
    47  	kccutil "github.com/kubewharf/katalyst-core/pkg/controller/kcc/util"
    48  	"github.com/kubewharf/katalyst-core/pkg/metrics"
    49  	"github.com/kubewharf/katalyst-core/pkg/util"
    50  	"github.com/kubewharf/katalyst-core/pkg/util/native"
    51  )
    52  
    53  const (
    54  	kccTargetControllerName = "kcct"
    55  )
    56  
    57  const (
    58  	kccTargetConditionReasonNormal                    = "Normal"
    59  	kccTargetConditionReasonHashFailed                = "HashFailed"
    60  	kccTargetConditionReasonMatchMoreOrLessThanOneKCC = "MatchMoreOrLessThanOneKCC"
    61  	kccTargetConditionReasonValidateFailed            = "ValidateFailed"
    62  )
    63  
    64  type KatalystCustomConfigTargetController struct {
    65  	ctx       context.Context
    66  	dryRun    bool
    67  	kccConfig *controller.KCCConfig
    68  
    69  	client              *kcclient.GenericClientSet
    70  	kccControl          control.KCCControl
    71  	unstructuredControl control.UnstructuredControl
    72  
    73  	// katalystCustomConfigLister can list/get KatalystCustomConfig from the shared informer's store
    74  	katalystCustomConfigLister v1alpha1.KatalystCustomConfigLister
    75  
    76  	syncedFunc []cache.InformerSynced
    77  
    78  	// targetHandler store gvr kcc and gvr
    79  	targetHandler *kcctarget.KatalystCustomConfigTargetHandler
    80  
    81  	// metricsEmitter for emit metrics
    82  	metricsEmitter metrics.MetricEmitter
    83  }
    84  
    85  func NewKatalystCustomConfigTargetController(
    86  	ctx context.Context,
    87  	genericConf *generic.GenericConfiguration,
    88  	_ *controller.GenericControllerConfiguration,
    89  	kccConfig *controller.KCCConfig,
    90  	client *kcclient.GenericClientSet,
    91  	katalystCustomConfigInformer configinformers.KatalystCustomConfigInformer,
    92  	metricsEmitter metrics.MetricEmitter,
    93  	targetHandler *kcctarget.KatalystCustomConfigTargetHandler,
    94  ) (*KatalystCustomConfigTargetController, error) {
    95  	k := &KatalystCustomConfigTargetController{
    96  		ctx:                        ctx,
    97  		client:                     client,
    98  		dryRun:                     genericConf.DryRun,
    99  		kccConfig:                  kccConfig,
   100  		katalystCustomConfigLister: katalystCustomConfigInformer.Lister(),
   101  		targetHandler:              targetHandler,
   102  		syncedFunc: []cache.InformerSynced{
   103  			katalystCustomConfigInformer.Informer().HasSynced,
   104  			targetHandler.HasSynced,
   105  		},
   106  	}
   107  
   108  	if metricsEmitter == nil {
   109  		k.metricsEmitter = metrics.DummyMetrics{}
   110  	} else {
   111  		k.metricsEmitter = metricsEmitter.WithTags(kccTargetControllerName)
   112  	}
   113  
   114  	k.kccControl = control.DummyKCCControl{}
   115  	k.unstructuredControl = control.DummyUnstructuredControl{}
   116  	if !k.dryRun {
   117  		k.kccControl = control.NewRealKCCControl(client.InternalClient)
   118  		k.unstructuredControl = control.NewRealUnstructuredControl(client.DynamicClient)
   119  	}
   120  
   121  	// register kcc-target informer handler
   122  	targetHandler.RegisterTargetHandler(kccTargetControllerName, k.katalystCustomConfigTargetHandler)
   123  	return k, nil
   124  }
   125  
   126  // Run don't need to trigger reconcile logic.
   127  func (k *KatalystCustomConfigTargetController) Run() {
   128  	defer utilruntime.HandleCrash()
   129  
   130  	defer klog.Infof("shutting down %s controller", kccTargetControllerName)
   131  
   132  	if !cache.WaitForCacheSync(k.ctx.Done(), k.syncedFunc...) {
   133  		utilruntime.HandleError(fmt.Errorf("unable to sync caches for %s controller", kccTargetControllerName))
   134  		return
   135  	}
   136  	klog.Infof("caches are synced for %s controller", kccTargetControllerName)
   137  
   138  	go wait.Until(k.clearExpiredKCCTarget, 30*time.Second, k.ctx.Done())
   139  
   140  	<-k.ctx.Done()
   141  }
   142  
   143  // katalystCustomConfigTargetHandler process object of kcc target type from targetAccessor, and
   144  // KatalystCustomConfigTargetAccessor will call this handler when some update event on target is added.
   145  func (k *KatalystCustomConfigTargetController) katalystCustomConfigTargetHandler(gvr metav1.GroupVersionResource, target *unstructured.Unstructured) error {
   146  	for _, syncFunc := range k.syncedFunc {
   147  		if !syncFunc() {
   148  			return fmt.Errorf("[kcct] informer has not synced")
   149  		}
   150  	}
   151  
   152  	klog.V(4).Infof("gvr: %s, target: %s updated", gvr.String(), native.GenerateUniqObjectNameKey(target))
   153  
   154  	if target.GetDeletionTimestamp() != nil {
   155  		err := k.handleKCCTargetFinalizer(gvr, target)
   156  		if err != nil {
   157  			return err
   158  		}
   159  		return nil
   160  	}
   161  
   162  	target, err := kccutil.EnsureKCCTargetFinalizer(k.ctx, k.unstructuredControl,
   163  		consts.KatalystCustomConfigTargetFinalizerKCCT, gvr, target)
   164  	if err != nil {
   165  		return err
   166  	}
   167  
   168  	// add kcc target config process logic:
   169  	//	1. clear expired valid target config
   170  	//	2. generate hash of current target config and update it to annotation
   171  	//	3. todo: control revision history if needed
   172  	//	4. update target config annotation and status if needed
   173  
   174  	targetResource := util.ToKCCTargetResource(target)
   175  	if isExpired := targetResource.CheckExpired(time.Now()); isExpired {
   176  		// delete expired kcc target
   177  		err := k.unstructuredControl.DeleteUnstructured(k.ctx, gvr, target, metav1.DeleteOptions{})
   178  		if err != nil {
   179  			return err
   180  		}
   181  		return nil
   182  	}
   183  
   184  	var (
   185  		isValid               bool
   186  		message, hash, reason string
   187  		overlapTargets        []util.KCCTargetResource
   188  	)
   189  
   190  	reason = kccTargetConditionReasonNormal
   191  	kccKeys := k.targetHandler.GetKCCKeyListByGVR(gvr)
   192  	if len(kccKeys) != 1 {
   193  		isValid = false
   194  		reason = kccTargetConditionReasonMatchMoreOrLessThanOneKCC
   195  		message = fmt.Sprintf("more or less than one kcc %v match same gvr %s", kccKeys, gvr.String())
   196  	} else {
   197  		key := kccKeys[0]
   198  		namespace, name, err := cache.SplitMetaNamespaceKey(key)
   199  		if err != nil {
   200  			klog.Errorf("failed to split namespace and name from key %s", key)
   201  			return err
   202  		}
   203  
   204  		kcc, err := k.katalystCustomConfigLister.KatalystCustomConfigs(namespace).Get(name)
   205  		if apierrors.IsNotFound(err) {
   206  			klog.Warningf("kcc %s is not found", key)
   207  			return nil
   208  		} else if err != nil {
   209  			klog.Errorf("kcc %s get error: %v", key, err)
   210  			return err
   211  		}
   212  
   213  		isValid, message, overlapTargets, err = k.validateTargetResourceGenericSpec(kcc, targetResource)
   214  		if err != nil {
   215  			return err
   216  		}
   217  
   218  		if !isValid {
   219  			reason = kccTargetConditionReasonValidateFailed
   220  		}
   221  	}
   222  
   223  	if isValid {
   224  		// update target resource hash only when config is valid
   225  		hash, err = targetResource.GenerateConfigHash()
   226  		if err != nil {
   227  			// if generate config hash failed set target resource invalid
   228  			isValid = false
   229  			message = fmt.Sprintf("generate config hash failed: %s", err)
   230  			reason = kccTargetConditionReasonHashFailed
   231  		}
   232  
   233  		// set kcc target hash
   234  		if targetResource.GetHash() != hash {
   235  			targetResource.SetHash(hash)
   236  			target, err = k.unstructuredControl.UpdateUnstructured(k.ctx, gvr, target, metav1.UpdateOptions{})
   237  			if err != nil {
   238  				return err
   239  			}
   240  
   241  			targetResource = util.ToKCCTargetResource(target)
   242  		}
   243  	}
   244  
   245  	// update kcc target status
   246  	oldKCCTargetResource := targetResource.DeepCopy()
   247  	updateTargetResourceStatus(targetResource, isValid, message, reason)
   248  	if !apiequality.Semantic.DeepEqual(oldKCCTargetResource, targetResource) {
   249  		klog.V(4).Infof("gvr: %s, target: %s need update status", gvr.String(), native.GenerateUniqObjectNameKey(target))
   250  		_, err = k.unstructuredControl.UpdateUnstructuredStatus(k.ctx, gvr, target, metav1.UpdateOptions{})
   251  		if err != nil {
   252  			return err
   253  		}
   254  
   255  		if len(overlapTargets) > 0 {
   256  			// if kcc target status changed, it needs trigger overlap kcc targets to reconcile
   257  			err := k.enqueueTargets(gvr, overlapTargets)
   258  			if err != nil {
   259  				return err
   260  			}
   261  		} else if !oldKCCTargetResource.CheckValid() && targetResource.CheckValid() {
   262  			// if old kcc is overlap and change to valid now, it needs trigger all other invalid kcc targets to reconcile
   263  			targets, err := k.listAllKCCTargetResource(gvr)
   264  			if err != nil {
   265  				return err
   266  			}
   267  
   268  			var invalidTargets []util.KCCTargetResource
   269  			for _, target := range targets {
   270  				if targetResource.GetName() == target.GetName() && targetResource.GetNamespace() == target.GetNamespace() {
   271  					continue
   272  				}
   273  
   274  				if !target.CheckValid() {
   275  					invalidTargets = append(invalidTargets, target)
   276  				}
   277  			}
   278  
   279  			err = k.enqueueTargets(gvr, invalidTargets)
   280  			if err != nil {
   281  				return err
   282  			}
   283  		}
   284  	}
   285  
   286  	return nil
   287  }
   288  
   289  func (k *KatalystCustomConfigTargetController) clearExpiredKCCTarget() {
   290  	k.targetHandler.RangeGVRTargetAccessor(func(gvr metav1.GroupVersionResource, accessor kcctarget.KatalystCustomConfigTargetAccessor) bool {
   291  		configTargets, err := accessor.List(labels.Everything())
   292  		if err != nil {
   293  			return false
   294  		}
   295  
   296  		for _, target := range configTargets {
   297  			// expired target config will be re-enqueue periodically to make sure it is cleared
   298  			if util.ToKCCTargetResource(target).CheckExpired(time.Now()) {
   299  				accessor.Enqueue(kccTargetControllerName, target)
   300  			}
   301  		}
   302  		return true
   303  	})
   304  }
   305  
   306  func (k *KatalystCustomConfigTargetController) enqueueTargets(gvr metav1.GroupVersionResource, targets []util.KCCTargetResource) error {
   307  	accessor, ok := k.targetHandler.GetTargetAccessorByGVR(gvr)
   308  	if !ok {
   309  		return fmt.Errorf("target accessor %s not found", gvr)
   310  	}
   311  
   312  	for _, t := range targets {
   313  		if t.GetDeletionTimestamp() != nil {
   314  			continue
   315  		}
   316  
   317  		accessor.Enqueue(kccTargetControllerName, t.Unstructured)
   318  	}
   319  
   320  	return nil
   321  }
   322  
   323  // handleKCCTargetFinalizer enqueue all kcc target to reconcile when a target was deleted
   324  func (k *KatalystCustomConfigTargetController) handleKCCTargetFinalizer(gvr metav1.GroupVersionResource, target *unstructured.Unstructured) error {
   325  	if !controllerutil.ContainsFinalizer(target, consts.KatalystCustomConfigTargetFinalizerKCCT) {
   326  		return nil
   327  	}
   328  
   329  	klog.Infof("handling gvr: %s kcc target %s finalizer", gvr.String(), native.GenerateUniqObjectNameKey(target))
   330  	targets, err := k.listAllKCCTargetResource(gvr)
   331  	if err != nil {
   332  		return err
   333  	}
   334  
   335  	// if kcc target deleted, it needs trigger other target to reconcile
   336  	err = k.enqueueTargets(gvr, targets)
   337  	if err != nil {
   338  		return err
   339  	}
   340  
   341  	err = kccutil.RemoveKCCTargetFinalizer(k.ctx, k.unstructuredControl, consts.KatalystCustomConfigTargetFinalizerKCCT, gvr, target)
   342  	if err != nil {
   343  		return err
   344  	}
   345  
   346  	klog.Infof("success remove gvr: %s kcc target %s finalizer", gvr.String(), native.GenerateUniqObjectNameKey(target))
   347  	return nil
   348  }
   349  
   350  // validateTargetResourceGenericSpec validate target resource generic spec as follows rule:
   351  // 1. can not set both labelSelector and nodeNames config at the same time
   352  // 2. if nodeNames is not set, lastDuration must not be set either
   353  // 3. labelSelector config must only contain kcc' labelSelectorKey in priority allowed key list
   354  // 4. labelSelector config cannot overlap with other labelSelector config in same priority
   355  // 5. nodeNames config must set lastDuration to make sure it will be auto cleared
   356  // 6. nodeNames config cannot overlap with other nodeNames config
   357  // 7. it is not allowed two global config (without either labelSelector or nodeNames) overlap
   358  func (k *KatalystCustomConfigTargetController) validateTargetResourceGenericSpec(kcc *configapis.KatalystCustomConfig, targetResource util.KCCTargetResource) (bool, string, []util.KCCTargetResource, error) {
   359  	labelSelector := targetResource.GetLabelSelector()
   360  	nodeNames := targetResource.GetNodeNames()
   361  	if len(labelSelector) != 0 && len(nodeNames) != 0 {
   362  		return false, "both labelSelector and nodeNames has been set", nil, nil
   363  	} else if len(labelSelector) != 0 {
   364  		return k.validateTargetResourceLabelSelector(kcc, targetResource)
   365  	} else if len(nodeNames) != 0 {
   366  		return k.validateTargetResourceNodeNames(kcc, targetResource)
   367  	} else {
   368  		return k.validateTargetResourceGlobal(kcc, targetResource)
   369  	}
   370  }
   371  
   372  func (k *KatalystCustomConfigTargetController) validateTargetResourceLabelSelector(kcc *configapis.KatalystCustomConfig, targetResource util.KCCTargetResource) (bool, string, []util.KCCTargetResource, error) {
   373  	priorityAllowedKeyListMap := getPriorityAllowedKeyListMap(kcc)
   374  	if len(priorityAllowedKeyListMap) == 0 {
   375  		return false, fmt.Sprintf("kcc %s no support label selector", native.GenerateUniqObjectNameKey(kcc)), nil, nil
   376  	}
   377  
   378  	valid, msg, err := validateLabelSelectorMatchWithKCCDefinition(priorityAllowedKeyListMap, targetResource)
   379  	if err != nil {
   380  		return false, "", nil, nil
   381  	} else if !valid {
   382  		return false, msg, nil, nil
   383  	}
   384  
   385  	kccTargetResources, err := k.listAllKCCTargetResource(kcc.Spec.TargetType)
   386  	if err != nil {
   387  		return false, "", nil, err
   388  	}
   389  
   390  	return validateLabelSelectorOverlapped(priorityAllowedKeyListMap, targetResource, kccTargetResources)
   391  }
   392  
   393  func getPriorityAllowedKeyListMap(kcc *configapis.KatalystCustomConfig) map[int32]sets.String {
   394  	priorityAllowedKeyListMap := make(map[int32]sets.String)
   395  	for _, allowedKey := range kcc.Spec.NodeLabelSelectorAllowedKeyList {
   396  		priorityAllowedKeyListMap[allowedKey.Priority] = sets.NewString(allowedKey.KeyList...)
   397  	}
   398  	return priorityAllowedKeyListMap
   399  }
   400  
   401  // validateLabelSelectorMatchWithKCCDefinition make sures that labelSelector config must only contain key in kcc' allowed key list
   402  func validateLabelSelectorMatchWithKCCDefinition(priorityAllowedKeyListMap map[int32]sets.String, targetResource util.KCCTargetResource) (bool, string, error) {
   403  	if targetResource.GetLastDuration() != nil {
   404  		return false, "both labelSelector and lastDuration has been set", nil
   405  	}
   406  
   407  	labelSelector := targetResource.GetLabelSelector()
   408  	selector, err := labels.Parse(labelSelector)
   409  	if err != nil {
   410  		return false, fmt.Sprintf("labelSelector parse failed: %s", err), nil
   411  	}
   412  
   413  	priority := targetResource.GetPriority()
   414  	allowedKeyList, ok := priorityAllowedKeyListMap[priority]
   415  	if !ok {
   416  		return false, fmt.Sprintf("priority %d not supported", priority), nil
   417  	}
   418  
   419  	reqs, selectable := selector.Requirements()
   420  	if !selectable {
   421  		return false, fmt.Sprintf("labelSelector cannot selectable"), nil
   422  	}
   423  
   424  	inValidLabelKeys := sets.String{}
   425  	for _, r := range reqs {
   426  		key := r.Key()
   427  		if !allowedKeyList.Has(key) {
   428  			inValidLabelKeys.Insert(key)
   429  		}
   430  	}
   431  
   432  	if len(inValidLabelKeys) > 0 {
   433  		return false, fmt.Sprintf("labelSelector with invalid key %v (%s)", inValidLabelKeys.List(), allowedKeyList.List()), nil
   434  	}
   435  
   436  	return true, "", nil
   437  }
   438  
   439  // validateLabelSelectorOverlapped make sures that labelSelector config cannot overlap with other labelSelector config
   440  func validateLabelSelectorOverlapped(priorityAllowedKeyListMap map[int32]sets.String, targetResource util.KCCTargetResource,
   441  	otherResources []util.KCCTargetResource,
   442  ) (bool, string, []util.KCCTargetResource, error) {
   443  	labelSelector := targetResource.GetLabelSelector()
   444  	selector, err := labels.Parse(labelSelector)
   445  	if err != nil {
   446  		return false, fmt.Sprintf("labelSelector parse failed: %s", err), nil, nil
   447  	}
   448  
   449  	priority := targetResource.GetPriority()
   450  	allowedKeyList, ok := priorityAllowedKeyListMap[priority]
   451  	if !ok {
   452  		return false, fmt.Sprintf("priority %d not supported", priority), nil, nil
   453  	}
   454  
   455  	var overlapTargets []util.KCCTargetResource
   456  	overlapResources := sets.String{}
   457  	for _, res := range otherResources {
   458  		if (res.GetNamespace() == targetResource.GetNamespace() && res.GetName() == targetResource.GetName()) ||
   459  			len(res.GetLabelSelector()) == 0 {
   460  			continue
   461  		}
   462  
   463  		otherSelector, err := labels.Parse(res.GetLabelSelector())
   464  		if err != nil {
   465  			continue
   466  		}
   467  
   468  		otherPriority := res.GetPriority()
   469  		if otherPriority != priority {
   470  			continue
   471  		}
   472  
   473  		overlap := checkLabelSelectorOverlap(selector, otherSelector, allowedKeyList.List())
   474  		if overlap {
   475  			overlapTargets = append(overlapTargets, res)
   476  			overlapResources.Insert(native.GenerateUniqObjectNameKey(res))
   477  		}
   478  	}
   479  
   480  	if len(overlapResources) > 0 {
   481  		return false, fmt.Sprintf("labelSelector overlay with others: %v", overlapResources.List()), overlapTargets, nil
   482  	}
   483  
   484  	return true, "", nil, nil
   485  }
   486  
   487  func (k *KatalystCustomConfigTargetController) validateTargetResourceNodeNames(kcc *configapis.KatalystCustomConfig, targetResource util.KCCTargetResource) (bool, string, []util.KCCTargetResource, error) {
   488  	if targetResource.GetLastDuration() == nil {
   489  		return false, "nodeNames has been set but lastDuration no set", nil, nil
   490  	}
   491  
   492  	kccTargetResources, err := k.listAllKCCTargetResource(kcc.Spec.TargetType)
   493  	if err != nil {
   494  		return false, "", nil, err
   495  	}
   496  
   497  	return validateTargetResourceNodeNamesOverlapped(targetResource, kccTargetResources)
   498  }
   499  
   500  // validateLabelSelectorOverlapped make sures that nodeNames config cannot overlap with other labelSelector config
   501  func validateTargetResourceNodeNamesOverlapped(targetResource util.KCCTargetResource, otherResources []util.KCCTargetResource) (bool, string, []util.KCCTargetResource, error) {
   502  	nodeNames := sets.NewString(targetResource.GetNodeNames()...)
   503  
   504  	var overlapTargets []util.KCCTargetResource
   505  	overlapResources := sets.String{}
   506  	for _, res := range otherResources {
   507  		if (res.GetNamespace() == targetResource.GetNamespace() && res.GetName() == targetResource.GetName()) ||
   508  			len(res.GetNodeNames()) == 0 {
   509  			continue
   510  		}
   511  
   512  		otherNodeNames := sets.NewString(res.GetNodeNames()...)
   513  		if nodeNames.Intersection(otherNodeNames).Len() > 0 {
   514  			overlapResources.Insert(native.GenerateUniqObjectNameKey(res))
   515  			overlapTargets = append(overlapTargets, res)
   516  		}
   517  	}
   518  
   519  	if len(overlapResources) > 0 {
   520  		return false, fmt.Sprintf("nodeNames overlay with others: %v", overlapResources.List()), overlapTargets, nil
   521  	}
   522  
   523  	return true, "", nil, nil
   524  }
   525  
   526  func (k *KatalystCustomConfigTargetController) validateTargetResourceGlobal(kcc *configapis.KatalystCustomConfig, targetResource util.KCCTargetResource) (bool, string, []util.KCCTargetResource, error) {
   527  	if targetResource.GetLastDuration() != nil {
   528  		return false, "lastDuration has been set for global config", nil, nil
   529  	}
   530  
   531  	kccTargetResources, err := k.listAllKCCTargetResource(kcc.Spec.TargetType)
   532  	if err != nil {
   533  		return false, "", nil, err
   534  	}
   535  
   536  	return validateTargetResourceGlobalOverlapped(targetResource, kccTargetResources)
   537  }
   538  
   539  // validateLabelSelectorOverlapped make sures that only one global configurations is created.
   540  func validateTargetResourceGlobalOverlapped(targetResource util.KCCTargetResource, otherResources []util.KCCTargetResource) (bool, string, []util.KCCTargetResource, error) {
   541  	var overlapTargets []util.KCCTargetResource
   542  	overlapTargetNames := sets.String{}
   543  	for _, res := range otherResources {
   544  		if (res.GetNamespace() == targetResource.GetNamespace() && res.GetName() == targetResource.GetName()) ||
   545  			(len(res.GetNodeNames()) > 0 || len(res.GetLabelSelector()) > 0) {
   546  			continue
   547  		}
   548  
   549  		overlapTargetNames.Insert(native.GenerateUniqObjectNameKey(res))
   550  		overlapTargets = append(overlapTargets, res)
   551  	}
   552  
   553  	if len(overlapTargetNames) > 0 {
   554  		return false, fmt.Sprintf("global config %s overlay with others: %v",
   555  			native.GenerateUniqObjectNameKey(targetResource), overlapTargetNames.List()), overlapTargets, nil
   556  	}
   557  
   558  	return true, "", nil, nil
   559  }
   560  
   561  func (k *KatalystCustomConfigTargetController) listAllKCCTargetResource(gvr metav1.GroupVersionResource) ([]util.KCCTargetResource, error) {
   562  	accessor, ok := k.targetHandler.GetTargetAccessorByGVR(gvr)
   563  	if !ok {
   564  		return nil, fmt.Errorf("target accessor %s not found", gvr)
   565  	}
   566  
   567  	list, err := accessor.List(labels.Everything())
   568  	if err != nil {
   569  		return nil, err
   570  	}
   571  
   572  	resources := make([]util.KCCTargetResource, 0, len(list))
   573  	for _, obj := range list {
   574  		if obj.GetDeletionTimestamp() != nil {
   575  			continue
   576  		}
   577  		resources = append(resources, util.ToKCCTargetResource(obj))
   578  	}
   579  
   580  	return resources, nil
   581  }
   582  
   583  func updateTargetResourceStatus(targetResource util.KCCTargetResource, isValid bool, msg, reason string) {
   584  	status := targetResource.GetGenericStatus()
   585  	status.ObservedGeneration = targetResource.GetGeneration()
   586  	if !isValid {
   587  		kccutil.UpdateKCCTGenericConditions(&status, configapis.ConfigConditionTypeValid, v1.ConditionFalse, reason, msg)
   588  	} else {
   589  		kccutil.UpdateKCCTGenericConditions(&status, configapis.ConfigConditionTypeValid, v1.ConditionTrue, reason, "")
   590  	}
   591  
   592  	targetResource.SetGenericStatus(status)
   593  }
   594  
   595  // checkLabelSelectorOverlap checks whether the labelSelector overlap with other labelSelector by the keyList
   596  func checkLabelSelectorOverlap(selector labels.Selector, otherSelector labels.Selector,
   597  	keyList []string,
   598  ) bool {
   599  	for _, key := range keyList {
   600  		equalLabelSet, inEqualLabelSet, _ := getMatchLabelSet(selector, key)
   601  		otherEqualLabelSet, otherInEqualLabelSet, _ := getMatchLabelSet(otherSelector, key)
   602  		if (equalLabelSet.Len() > 0 && otherEqualLabelSet.Len() > 0 && equalLabelSet.Intersection(otherEqualLabelSet).Len() > 0) ||
   603  			(equalLabelSet.Len() == 0 && otherEqualLabelSet.Len() == 0 && (inEqualLabelSet.Len() > 0 || otherInEqualLabelSet.Len() > 0)) ||
   604  			(inEqualLabelSet.Len() > 0 && !inEqualLabelSet.Intersection(otherEqualLabelSet).Equal(otherEqualLabelSet)) ||
   605  			(otherInEqualLabelSet.Len() > 0 && !otherInEqualLabelSet.Intersection(equalLabelSet).Equal(equalLabelSet)) ||
   606  			(equalLabelSet.Len() > 0 && otherEqualLabelSet.Len() == 0 && otherInEqualLabelSet.Len() == 0) ||
   607  			(otherEqualLabelSet.Len() > 0 && equalLabelSet.Len() == 0 && inEqualLabelSet.Len() == 0) {
   608  			continue
   609  		} else {
   610  			return false
   611  		}
   612  	}
   613  
   614  	return true
   615  }
   616  
   617  func getMatchLabelSet(selector labels.Selector, key string) (sets.String, sets.String, error) {
   618  	reqs, selectable := selector.Requirements()
   619  	if !selectable {
   620  		return nil, nil, fmt.Errorf("labelSelector cannot selectable")
   621  	}
   622  
   623  	equalLabelSet := sets.String{}
   624  	inEqualLabelSet := sets.String{}
   625  	for _, r := range reqs {
   626  		if r.Key() != key {
   627  			continue
   628  		}
   629  		switch r.Operator() {
   630  		case selection.Equals, selection.DoubleEquals, selection.In:
   631  			equalLabelSet = equalLabelSet.Union(r.Values())
   632  		case selection.NotEquals, selection.NotIn:
   633  			inEqualLabelSet = inEqualLabelSet.Union(r.Values())
   634  		default:
   635  			return nil, nil, fmt.Errorf("labelSelector operator %s not supported", r.Operator())
   636  		}
   637  	}
   638  	return equalLabelSet, inEqualLabelSet, nil
   639  }