github.com/kubewharf/katalyst-core@v0.5.3/pkg/controller/overcommit/node/matcher/matcher.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 matcher
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"sync"
    23  
    24  	v1 "k8s.io/api/core/v1"
    25  	"k8s.io/apimachinery/pkg/api/errors"
    26  	"k8s.io/apimachinery/pkg/labels"
    27  	"k8s.io/apimachinery/pkg/selection"
    28  	"k8s.io/apimachinery/pkg/util/sets"
    29  	corelisters "k8s.io/client-go/listers/core/v1"
    30  	"k8s.io/client-go/tools/cache"
    31  	"k8s.io/klog/v2"
    32  
    33  	"github.com/kubewharf/katalyst-api/pkg/apis/overcommit/v1alpha1"
    34  	overcommitlisters "github.com/kubewharf/katalyst-api/pkg/client/listers/overcommit/v1alpha1"
    35  	"github.com/kubewharf/katalyst-api/pkg/consts"
    36  )
    37  
    38  const LabelSelectorValIndex = "spec.nodeOvercommitSelectorVal"
    39  
    40  type (
    41  	ConfigToNodes map[string]sets.String
    42  	NodeToConfig  map[string]*v1alpha1.NodeOvercommitConfig
    43  )
    44  
    45  type Matcher interface {
    46  	// Reconcile rematch all configs and nodes
    47  	Reconcile() error
    48  
    49  	// MatchConfig matches nodes for config, return nodeNames whose matched config maybe updated.
    50  	MatchConfig(configName string) ([]string, error)
    51  	// MatchNode matches and sorts configs for node.
    52  	MatchNode(nodeName string) (*v1alpha1.NodeOvercommitConfig, error)
    53  	// ListNodeToConfig list nodes with matched configName
    54  	ListNodeToConfig() map[string]string
    55  	// GetConfig get matched config by nodeName
    56  	GetConfig(nodeName string) *v1alpha1.NodeOvercommitConfig
    57  	// GetNodes get matched nodes by configName
    58  	GetNodes(configName string) []string
    59  	// DelNode delete node in matcher cache
    60  	DelNode(nodeName string)
    61  }
    62  
    63  type NodeMatchEvent struct {
    64  	NodeName   string
    65  	NodeUpdate bool
    66  }
    67  
    68  type DummyMatcher struct{}
    69  
    70  func (dm *DummyMatcher) Reconcile() error {
    71  	return nil
    72  }
    73  
    74  func (dm *DummyMatcher) MatchConfig(_ string) ([]string, error) {
    75  	return []string{}, nil
    76  }
    77  
    78  func (dm *DummyMatcher) MatchNode(_ string) (*v1alpha1.NodeOvercommitConfig, error) {
    79  	return nil, nil
    80  }
    81  
    82  func (dm *DummyMatcher) GetConfig(_ string) *v1alpha1.NodeOvercommitConfig {
    83  	return nil
    84  }
    85  
    86  func (dm *DummyMatcher) DelNode(_ string) {}
    87  
    88  func (dm *DummyMatcher) GetNodeToConfig(_ string) *v1alpha1.NodeOvercommitConfig {
    89  	return nil
    90  }
    91  
    92  func (dm *DummyMatcher) ListNodeToConfig() map[string]string {
    93  	return nil
    94  }
    95  
    96  func (dm *DummyMatcher) GetNodes(_ string) []string {
    97  	return nil
    98  }
    99  
   100  func NewMatcher(nodelister corelisters.NodeLister, noclister overcommitlisters.NodeOvercommitConfigLister, indexer cache.Indexer) *MatcherImpl {
   101  	return &MatcherImpl{
   102  		RWMutex:       sync.RWMutex{},
   103  		nodeLister:    nodelister,
   104  		nocLister:     noclister,
   105  		nocIndexer:    indexer,
   106  		configToNodes: make(map[string]sets.String),
   107  		nodeToConfig:  make(map[string]*v1alpha1.NodeOvercommitConfig),
   108  	}
   109  }
   110  
   111  type MatcherImpl struct {
   112  	ctx context.Context
   113  	sync.RWMutex
   114  
   115  	nodeLister corelisters.NodeLister
   116  	nocLister  overcommitlisters.NodeOvercommitConfigLister
   117  
   118  	nocIndexer    cache.Indexer
   119  	configToNodes ConfigToNodes
   120  	nodeToConfig  NodeToConfig
   121  }
   122  
   123  func (i *MatcherImpl) Reconcile() error {
   124  	err := i.reconcileConfig()
   125  	if err != nil {
   126  		return err
   127  	}
   128  
   129  	return i.reconcileNode()
   130  }
   131  
   132  func (i *MatcherImpl) reconcileConfig() error {
   133  	nodeOvercommitConfigs, err := i.nocLister.List(labels.Everything())
   134  	if err != nil {
   135  		klog.Errorf("list nodeOvercommitConfig fail: %v", err)
   136  		return err
   137  	}
   138  
   139  	i.Lock()
   140  	defer i.Unlock()
   141  	i.configToNodes = make(map[string]sets.String)
   142  	for _, config := range nodeOvercommitConfigs {
   143  		nodeNames, err := i.matchConfigToNodes(config)
   144  		if err != nil {
   145  			return err
   146  		}
   147  
   148  		i.configToNodes[config.Name] = sets.NewString(nodeNames...)
   149  	}
   150  	return nil
   151  }
   152  
   153  func (i *MatcherImpl) reconcileNode() error {
   154  	nodeList, err := i.nodeLister.List(labels.Everything())
   155  	if err != nil {
   156  		klog.Errorf("list node fail: %v", err)
   157  		return err
   158  	}
   159  
   160  	i.Lock()
   161  	defer i.Unlock()
   162  
   163  	for _, node := range nodeList {
   164  		_, err := i.matchNode(node)
   165  		if err != nil {
   166  			klog.Errorf("matchNode %v fail: %v", node.Name, err)
   167  		}
   168  	}
   169  	return nil
   170  }
   171  
   172  func (i *MatcherImpl) MatchConfig(configName string) ([]string, error) {
   173  	var nodeUnionSets sets.String
   174  	nodeNames, err := i.matchConfigNameToNodes(configName)
   175  	if err != nil && errors.IsNotFound(err) {
   176  		i.Lock()
   177  		nodeUnionSets = i.configToNodes[configName]
   178  		delete(i.configToNodes, configName)
   179  		i.Unlock()
   180  		return nodeUnionSets.UnsortedList(), nil
   181  	}
   182  	if err != nil {
   183  		return nil, err
   184  	}
   185  	i.Lock()
   186  	nodeUnionSets = i.configNodeUnion(configName, nodeNames)
   187  	i.configToNodes[configName] = sets.NewString(nodeNames...)
   188  	i.Unlock()
   189  	return nodeUnionSets.UnsortedList(), nil
   190  }
   191  
   192  func (i *MatcherImpl) MatchNode(nodeName string) (*v1alpha1.NodeOvercommitConfig, error) {
   193  	node, err := i.nodeLister.Get(nodeName)
   194  	if err != nil {
   195  		if errors.IsNotFound(err) {
   196  			klog.Errorf("node %v has been deleted", nodeName)
   197  			i.Lock()
   198  			delete(i.nodeToConfig, nodeName)
   199  			i.Unlock()
   200  			return nil, nil
   201  		}
   202  		klog.Errorf("getnode %v fail, err: %v", nodeName, err)
   203  		return nil, err
   204  	}
   205  
   206  	i.Lock()
   207  	defer i.Unlock()
   208  	return i.matchNode(node)
   209  }
   210  
   211  func (i *MatcherImpl) matchNode(node *v1.Node) (*v1alpha1.NodeOvercommitConfig, error) {
   212  	nodeName := node.Name
   213  	if len(node.Labels) == 0 {
   214  		delete(i.nodeToConfig, nodeName)
   215  		return nil, nil
   216  	}
   217  	val, ok := node.Labels[consts.NodeOvercommitSelectorKey]
   218  	if !ok {
   219  		klog.Warningf("node %s has no label %s", nodeName, consts.NodeOvercommitSelectorKey)
   220  		delete(i.nodeToConfig, nodeName)
   221  		return nil, nil
   222  	}
   223  	config, err := GetValidNodeOvercommitConfig(i.nocIndexer, val)
   224  	if err != nil {
   225  		return nil, err
   226  	}
   227  	if config == nil {
   228  		delete(i.nodeToConfig, nodeName)
   229  		return nil, nil
   230  	}
   231  	i.nodeToConfig[nodeName] = config
   232  	return config, nil
   233  }
   234  
   235  func (i *MatcherImpl) DelNode(nodeName string) {
   236  	i.Lock()
   237  	defer i.Unlock()
   238  
   239  	delete(i.nodeToConfig, nodeName)
   240  	for key := range i.configToNodes {
   241  		i.configToNodes[key].Delete(nodeName)
   242  	}
   243  }
   244  
   245  func (i *MatcherImpl) GetConfig(nodeName string) *v1alpha1.NodeOvercommitConfig {
   246  	i.RLock()
   247  	defer i.RUnlock()
   248  	return i.nodeToConfig[nodeName]
   249  }
   250  
   251  func (i *MatcherImpl) ListNodeToConfig() map[string]string {
   252  	ret := make(map[string]string)
   253  	i.RLock()
   254  	defer i.RUnlock()
   255  
   256  	for nodeName, config := range i.nodeToConfig {
   257  		ret[nodeName] = config.Name
   258  	}
   259  	return ret
   260  }
   261  
   262  func (i *MatcherImpl) GetNodes(configName string) []string {
   263  	i.RLock()
   264  	defer i.RUnlock()
   265  	return i.configToNodes[configName].UnsortedList()
   266  }
   267  
   268  func (i *MatcherImpl) matchConfigNameToNodes(configName string) ([]string, error) {
   269  	overcommitConfig, err := i.nocLister.Get(configName)
   270  	if err != nil {
   271  		return nil, err
   272  	}
   273  
   274  	if overcommitConfig == nil {
   275  		return nil, fmt.Errorf("config %s is nil", configName)
   276  	}
   277  
   278  	return i.matchConfigToNodes(overcommitConfig)
   279  }
   280  
   281  func (i *MatcherImpl) matchConfigToNodes(overcommitConfig *v1alpha1.NodeOvercommitConfig) ([]string, error) {
   282  	nodeNames := make([]string, 0)
   283  
   284  	val := overcommitConfig.Spec.NodeOvercommitSelectorVal
   285  	if val == "" {
   286  		return []string{}, nil
   287  	}
   288  
   289  	requirement, err := labels.NewRequirement(consts.NodeOvercommitSelectorKey, selection.Equals, []string{val})
   290  	if err != nil {
   291  		return nil, err
   292  	}
   293  	selector := labels.NewSelector().Add(*requirement)
   294  
   295  	nodeList, err := i.nodeLister.List(selector)
   296  	if err != nil {
   297  		return nil, err
   298  	}
   299  	for _, node := range nodeList {
   300  		nodeNames = append(nodeNames, node.Name)
   301  	}
   302  	return nodeNames, nil
   303  }
   304  
   305  func (i *MatcherImpl) configNodeUnion(configName string, nodeNames []string) sets.String {
   306  	newNodes := sets.NewString(nodeNames...)
   307  
   308  	originalNodes, ok := i.configToNodes[configName]
   309  	if !ok {
   310  		return newNodes
   311  	}
   312  
   313  	return originalNodes.Union(newNodes)
   314  }