github.com/kubewharf/katalyst-core@v0.5.3/pkg/util/native/informer_dynamic.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 native
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"time"
    23  
    24  	"k8s.io/apimachinery/pkg/runtime/schema"
    25  	"k8s.io/apimachinery/pkg/util/errors"
    26  	"k8s.io/apimachinery/pkg/util/wait"
    27  	"k8s.io/client-go/dynamic/dynamicinformer"
    28  	"k8s.io/client-go/informers"
    29  	"k8s.io/client-go/util/retry"
    30  	"k8s.io/klog/v2"
    31  	"sigs.k8s.io/custom-metrics-apiserver/pkg/dynamicmapper"
    32  )
    33  
    34  // DynamicInformer keeps the informer-related contents for each workload
    35  type DynamicInformer struct {
    36  	GVK      schema.GroupVersionKind
    37  	GVR      schema.GroupVersionResource
    38  	Informer informers.GenericInformer
    39  }
    40  
    41  type DynamicResourcesManager struct {
    42  	// dynamicGVRs and dynamicInformers initialize only once
    43  	dynamicGVRs      map[string]schema.GroupVersionResource
    44  	dynamicInformers map[string]DynamicInformer
    45  
    46  	mapper                 *dynamicmapper.RegeneratingDiscoveryRESTMapper
    47  	dynamicInformerFactory dynamicinformer.DynamicSharedInformerFactory
    48  }
    49  
    50  // NewDynamicResourcesManager initializes a dynamic resources manger to manage dynamic informers
    51  func NewDynamicResourcesManager(
    52  	dynamicResources []string,
    53  	mapper *dynamicmapper.RegeneratingDiscoveryRESTMapper,
    54  	dynamicInformerFactory dynamicinformer.DynamicSharedInformerFactory,
    55  ) (*DynamicResourcesManager, error) {
    56  	m := &DynamicResourcesManager{
    57  		dynamicInformers:       make(map[string]DynamicInformer),
    58  		mapper:                 mapper,
    59  		dynamicInformerFactory: dynamicInformerFactory,
    60  	}
    61  
    62  	dynamicGVRs, err := getDynamicResourcesGVRMap(dynamicResources)
    63  	if err != nil {
    64  		return nil, fmt.Errorf("new dynamic resource manager failed: %v", err)
    65  	}
    66  	m.dynamicGVRs = dynamicGVRs
    67  
    68  	m.initDynamicInformers()
    69  	return m, nil
    70  }
    71  
    72  // Run start mapper to refresh  starts a goroutine to check if it has new gvr support available,
    73  // and if so, panics to restart to make sure all caches are correct
    74  func (m *DynamicResourcesManager) Run(ctx context.Context) {
    75  	// run RegeneratingDiscoveryRESTMapper to refresh gvr to gvk map
    76  	m.mapper.RunUntil(ctx.Done())
    77  
    78  	// try to check whether new resources need to support, and if so,
    79  	// panics to restart
    80  	go wait.UntilWithContext(ctx, func(ctx context.Context) {
    81  		for resource, gvr := range m.dynamicGVRs {
    82  			gvk, err := m.mapper.KindFor(gvr)
    83  			if err != nil {
    84  				klog.Errorf("find for %v failed, err %v", gvr.String(), err)
    85  				continue
    86  			}
    87  
    88  			// check whether current gvk is equal to old one
    89  			if informer, ok := m.dynamicInformers[resource]; ok && gvk == informer.GVK {
    90  				continue
    91  			}
    92  
    93  			panic(fmt.Sprintf("gvk %s for gvr %s found, try to restart", gvk.String(), gvr.String()))
    94  		}
    95  	}, 1*time.Minute)
    96  }
    97  
    98  // GetDynamicInformers gets current dynamic informers
    99  func (m *DynamicResourcesManager) GetDynamicInformers() map[string]DynamicInformer {
   100  	return m.dynamicInformers
   101  }
   102  
   103  // initDynamicInformers initializes dynamic informers map
   104  func (m *DynamicResourcesManager) initDynamicInformers() {
   105  	_ = wait.ExponentialBackoff(retry.DefaultRetry, func() (bool, error) {
   106  		err := m.syncDynamicInformers()
   107  		if err == nil {
   108  			return true, nil
   109  		}
   110  
   111  		klog.Errorf("sync DynamicInformers failed: %v, try to refresh mapper", err)
   112  
   113  		err = m.mapper.RegenerateMappings()
   114  		if err != nil {
   115  			klog.Errorf("regenerate DiscoveryRESTMapper failed: %v", err)
   116  		}
   117  		return false, nil
   118  	})
   119  }
   120  
   121  // syncDynamicInformers sync dynamic informers by current dynamic resources
   122  func (m *DynamicResourcesManager) syncDynamicInformers() error {
   123  	var errList []error
   124  	for resource, gvr := range m.dynamicGVRs {
   125  		if _, ok := m.dynamicInformers[resource]; ok {
   126  			continue
   127  		}
   128  
   129  		gvk, err := m.mapper.KindFor(gvr)
   130  		if err != nil {
   131  			errList = append(errList, fmt.Errorf("find for %v failed, err %v", gvr.String(), err))
   132  			continue
   133  		}
   134  
   135  		m.dynamicInformers[resource] = DynamicInformer{
   136  			GVK:      gvk,
   137  			GVR:      gvr,
   138  			Informer: m.dynamicInformerFactory.ForResource(gvr),
   139  		}
   140  	}
   141  	if len(errList) > 0 {
   142  		return errors.NewAggregate(errList)
   143  	}
   144  
   145  	return nil
   146  }
   147  
   148  func getDynamicResourcesGVRMap(dynamicResources []string) (map[string]schema.GroupVersionResource, error) {
   149  	dynamicGVRs := make(map[string]schema.GroupVersionResource, len(dynamicResources))
   150  	var errList []error
   151  	for _, resource := range dynamicResources {
   152  		if _, ok := dynamicGVRs[resource]; ok {
   153  			continue
   154  		}
   155  
   156  		gvr, _ := schema.ParseResourceArg(resource)
   157  		if gvr == nil {
   158  			return nil, fmt.Errorf("ParseResourceArg resource %v failed", resource)
   159  		}
   160  
   161  		dynamicGVRs[resource] = *gvr
   162  	}
   163  	if len(errList) > 0 {
   164  		return nil, errors.NewAggregate(errList)
   165  	}
   166  
   167  	return dynamicGVRs, nil
   168  }