github.com/kubewharf/katalyst-core@v0.5.3/pkg/metaserver/agent/cnr/cnr.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 cnr
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"sync"
    23  	"time"
    24  
    25  	apiequality "k8s.io/apimachinery/pkg/api/equality"
    26  	v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    27  	"k8s.io/klog/v2"
    28  
    29  	nodev1alpha1 "github.com/kubewharf/katalyst-api/pkg/apis/node/v1alpha1"
    30  	"github.com/kubewharf/katalyst-api/pkg/client/clientset/versioned/typed/node/v1alpha1"
    31  	"github.com/kubewharf/katalyst-core/pkg/config/agent/global"
    32  	"github.com/kubewharf/katalyst-core/pkg/config/agent/metaserver"
    33  )
    34  
    35  // CNRFetcher is used to get CNR information.
    36  type CNRFetcher interface {
    37  	// GetCNR returns those latest custom node resources metadata.
    38  	GetCNR(ctx context.Context) (*nodev1alpha1.CustomNodeResource, error)
    39  
    40  	// RegisterNotifier registers a notifier to be notified when CNR is updated.
    41  	RegisterNotifier(name string, notifier CNRNotifier) error
    42  
    43  	// UnregisterNotifier unregisters a notifier.
    44  	UnregisterNotifier(name string) error
    45  }
    46  
    47  // CNRNotifier is used to notify CNR update.
    48  type CNRNotifier interface {
    49  	// OnCNRUpdate is called when CNR is updated.
    50  	OnCNRUpdate(cnr *nodev1alpha1.CustomNodeResource)
    51  
    52  	// OnCNRStatusUpdate is called when CNR status is updated.
    53  	OnCNRStatusUpdate(cnr *nodev1alpha1.CustomNodeResource)
    54  }
    55  
    56  type cachedCNRFetcher struct {
    57  	sync.Mutex
    58  
    59  	baseConf *global.BaseConfiguration
    60  	cnrConf  *metaserver.CNRConfiguration
    61  	client   v1alpha1.CustomNodeResourceInterface
    62  
    63  	cnr          *nodev1alpha1.CustomNodeResource
    64  	lastSyncTime time.Time
    65  	notifiers    map[string]CNRNotifier
    66  }
    67  
    68  func NewCachedCNRFetcher(baseConf *global.BaseConfiguration, cnrConf *metaserver.CNRConfiguration, client v1alpha1.CustomNodeResourceInterface) CNRFetcher {
    69  	return &cachedCNRFetcher{
    70  		baseConf:  baseConf,
    71  		cnrConf:   cnrConf,
    72  		client:    client,
    73  		notifiers: make(map[string]CNRNotifier),
    74  	}
    75  }
    76  
    77  // GetCNR returns latest CNR from cache, if the cache is expired, it will sync CNR from remote,
    78  // and if the sync fails, it will not update the cache and return the last success cached CNR.
    79  func (c *cachedCNRFetcher) GetCNR(ctx context.Context) (*nodev1alpha1.CustomNodeResource, error) {
    80  	c.Lock()
    81  	defer c.Unlock()
    82  
    83  	now := time.Now()
    84  	if c.lastSyncTime.Add(c.cnrConf.CNRCacheTTL).Before(now) {
    85  		c.syncCNR(ctx)
    86  		c.lastSyncTime = now
    87  	}
    88  
    89  	if c.cnr != nil {
    90  		return c.cnr, nil
    91  	}
    92  
    93  	return nil, fmt.Errorf("cannot get cnr from cache and remote")
    94  }
    95  
    96  func (c *cachedCNRFetcher) syncCNR(ctx context.Context) {
    97  	klog.Infof("[cnr] sync cnr from remote")
    98  	cnr, err := c.client.Get(ctx, c.baseConf.NodeName, v1.GetOptions{ResourceVersion: "0"})
    99  	if err != nil {
   100  		klog.Errorf("syncCNR failed: %v", err)
   101  		return
   102  	}
   103  
   104  	c.cnr = cnr
   105  
   106  	if apiequality.Semantic.DeepEqual(cnr.Spec, c.cnr.Spec) ||
   107  		apiequality.Semantic.DeepEqual(cnr.ObjectMeta, c.cnr.ObjectMeta) {
   108  		// notify all notifiers
   109  		for _, notifier := range c.notifiers {
   110  			notifier.OnCNRUpdate(cnr)
   111  		}
   112  	}
   113  
   114  	if apiequality.Semantic.DeepEqual(cnr.Status, c.cnr.Status) {
   115  		// notify all notifiers
   116  		for _, notifier := range c.notifiers {
   117  			notifier.OnCNRStatusUpdate(cnr)
   118  		}
   119  	}
   120  }
   121  
   122  // RegisterNotifier registers a notifier to the fetcher, it returns error if the notifier is already registered,
   123  // so that the notifier can be registered only once or unregistered it before registering again.
   124  func (c *cachedCNRFetcher) RegisterNotifier(name string, notifier CNRNotifier) error {
   125  	c.Lock()
   126  	defer c.Unlock()
   127  
   128  	if _, ok := c.notifiers[name]; ok {
   129  		return fmt.Errorf("notifier %s already registered", name)
   130  	}
   131  
   132  	c.notifiers[name] = notifier
   133  	return nil
   134  }
   135  
   136  // UnregisterNotifier unregisters a notifier from the fetcher.
   137  func (c *cachedCNRFetcher) UnregisterNotifier(name string) error {
   138  	c.Lock()
   139  	defer c.Unlock()
   140  
   141  	if _, ok := c.notifiers[name]; !ok {
   142  		return fmt.Errorf("notifier %s not found", name)
   143  	}
   144  
   145  	delete(c.notifiers, name)
   146  	return nil
   147  }