github.com/gocrane/crane@v0.11.0/pkg/recommendation/manager.go (about)

     1  package recommendation
     2  
     3  import (
     4  	"fmt"
     5  	"sync"
     6  
     7  	"github.com/fsnotify/fsnotify"
     8  	"k8s.io/klog/v2"
     9  
    10  	analysisv1alph1 "github.com/gocrane/api/analysis/v1alpha1"
    11  	"github.com/gocrane/crane/pkg/recommendation/config"
    12  	"github.com/gocrane/crane/pkg/recommendation/framework"
    13  	"github.com/gocrane/crane/pkg/recommendation/recommender"
    14  	"github.com/gocrane/crane/pkg/recommendation/recommender/apis"
    15  	_ "github.com/gocrane/crane/pkg/recommendation/recommender/hpa"
    16  	_ "github.com/gocrane/crane/pkg/recommendation/recommender/idlenode"
    17  	_ "github.com/gocrane/crane/pkg/recommendation/recommender/replicas"
    18  	_ "github.com/gocrane/crane/pkg/recommendation/recommender/resource"
    19  	_ "github.com/gocrane/crane/pkg/recommendation/recommender/service"
    20  	_ "github.com/gocrane/crane/pkg/recommendation/recommender/volume"
    21  )
    22  
    23  type RecommenderManager interface {
    24  	// GetRecommender return a registered recommender
    25  	GetRecommender(recommenderName string) (recommender.Recommender, error)
    26  	// GetRecommenderWithRule return a registered recommender, its config merged with recommendationRule
    27  	GetRecommenderWithRule(recommenderName string, recommendationRule analysisv1alph1.RecommendationRule) (recommender.Recommender, error)
    28  }
    29  
    30  func NewRecommenderManager(recommendationConfiguration string) RecommenderManager {
    31  	m := &manager{
    32  		recommendationConfiguration: recommendationConfiguration,
    33  	}
    34  
    35  	m.loadConfigFile() // nolint:errcheck
    36  
    37  	go m.watchConfigFile()
    38  
    39  	return m
    40  }
    41  
    42  type ResourceSpec struct {
    43  	CPU    string
    44  	Memory string
    45  }
    46  
    47  type ResourceSpecs []ResourceSpec
    48  
    49  type manager struct {
    50  	recommendationConfiguration string
    51  
    52  	lock               sync.Mutex
    53  	recommenderConfigs map[string]apis.Recommender
    54  }
    55  
    56  func (m *manager) GetRecommender(recommenderName string) (recommender.Recommender, error) {
    57  	return m.GetRecommenderWithRule(recommenderName, analysisv1alph1.RecommendationRule{})
    58  }
    59  
    60  func (m *manager) GetRecommenderWithRule(recommenderName string, recommendationRule analysisv1alph1.RecommendationRule) (recommender.Recommender, error) {
    61  	m.lock.Lock()
    62  	defer m.lock.Unlock()
    63  
    64  	if recommenderConfig, ok := m.recommenderConfigs[recommenderName]; ok {
    65  		return recommender.GetRecommenderProvider(recommenderName, recommenderConfig, recommendationRule)
    66  	}
    67  
    68  	return nil, fmt.Errorf("unknown recommender name: %s", recommenderName)
    69  }
    70  
    71  func (m *manager) watchConfigFile() {
    72  	watcher, err := fsnotify.NewWatcher()
    73  	if err != nil {
    74  		klog.Error(err)
    75  		return
    76  	}
    77  	defer watcher.Close()
    78  
    79  	err = watcher.Add(m.recommendationConfiguration)
    80  	if err != nil {
    81  		klog.ErrorS(err, "Failed to watch", "file", m.recommendationConfiguration)
    82  		return
    83  	}
    84  	klog.Infof("Start watching %s for update.", m.recommendationConfiguration)
    85  
    86  	for {
    87  		select {
    88  		case event, ok := <-watcher.Events:
    89  			klog.Infof("Watched an event: %v", event)
    90  			if !ok {
    91  				return
    92  			}
    93  			if event.Op&fsnotify.Remove == fsnotify.Remove {
    94  				err = watcher.Add(m.recommendationConfiguration)
    95  				if err != nil {
    96  					klog.ErrorS(err, "Failed to watch.", "file", m.recommendationConfiguration)
    97  					continue
    98  				}
    99  				klog.Infof("Config file %s removed. Reload it.", event.Name)
   100  				if err = m.loadConfigFile(); err != nil {
   101  					klog.ErrorS(err, "Failed to load config set file.")
   102  				}
   103  			} else if event.Op&fsnotify.Write == fsnotify.Write || event.Op&fsnotify.Create == fsnotify.Create {
   104  				klog.Infof("Config file %s modified. Reload it.", event.Name)
   105  				if err = m.loadConfigFile(); err != nil {
   106  					klog.ErrorS(err, "Failed to load config set file.")
   107  				}
   108  			}
   109  		case err, ok := <-watcher.Errors:
   110  			if !ok {
   111  				return
   112  			}
   113  			klog.Error(err)
   114  		}
   115  	}
   116  }
   117  
   118  func (m *manager) loadConfigFile() error {
   119  	m.lock.Lock()
   120  	defer m.lock.Unlock()
   121  
   122  	recommenderConfigs, err := config.GetRecommendersFromConfiguration(m.recommendationConfiguration)
   123  	if err != nil {
   124  		klog.ErrorS(err, "Failed to load recommendation config file", "file", m.recommendationConfiguration)
   125  		return err
   126  	}
   127  	m.recommenderConfigs = recommenderConfigs
   128  	klog.Info("Recommendation Config updated.")
   129  	return nil
   130  }
   131  
   132  func Run(ctx *framework.RecommendationContext, recommender recommender.Recommender) error {
   133  	klog.Infof("%s: start to run recommender %q.", ctx.String(), recommender.Name())
   134  
   135  	// 1. Filter phase
   136  	err := recommender.Filter(ctx)
   137  	if err != nil {
   138  		klog.Errorf("%s: recommender %q failed at filter phase: %v", ctx.String(), recommender.Name(), err)
   139  		return err
   140  	}
   141  
   142  	// 2. PrePrepare phase
   143  	err = recommender.CheckDataProviders(ctx)
   144  	if err != nil {
   145  		klog.Errorf("%s: recommender %q failed at prepare check data provider phase: %v", ctx.String(), recommender.Name(), err)
   146  		return err
   147  	}
   148  
   149  	// 3. Prepare phase
   150  	err = recommender.CollectData(ctx)
   151  	if err != nil {
   152  		klog.Errorf("%s: recommender %q failed at prepare collect data phase: %v", ctx.String(), recommender.Name(), err)
   153  		return err
   154  	}
   155  
   156  	// 4. PostPrepare phase
   157  	err = recommender.PostProcessing(ctx)
   158  	if err != nil {
   159  		klog.Errorf("%s: recommender %q failed at prepare data post processing phase: %v", ctx.String(), recommender.Name(), err)
   160  		return err
   161  	}
   162  
   163  	// 5. PreRecommend phase
   164  	err = recommender.PreRecommend(ctx)
   165  	if err != nil {
   166  		klog.Errorf("%s: recommender %q failed at pre commend phase: %v", ctx.String(), recommender.Name(), err)
   167  		return err
   168  	}
   169  
   170  	// 6. Recommend phase
   171  	err = recommender.Recommend(ctx)
   172  	if err != nil {
   173  		klog.Errorf("%s: recommender %q failed at recommend phase: %v", ctx.String(), recommender.Name(), err)
   174  		return err
   175  	}
   176  
   177  	// 7. PostRecommend phase, add policy
   178  	err = recommender.Policy(ctx)
   179  	if err != nil {
   180  		klog.Errorf("%s: recommender %q failed at recommend policy phase: %v", ctx.String(), recommender.Name(), err)
   181  		return err
   182  	}
   183  
   184  	// 8. Observe phase
   185  	err = recommender.Observe(ctx)
   186  	if err != nil {
   187  		klog.Errorf("%s: recommender %q failed at observe phase: %v", ctx.String(), recommender.Name(), err)
   188  		return err
   189  	}
   190  
   191  	klog.Infof("%s: finish to run recommender %q.", ctx.String(), recommender.Name())
   192  	return nil
   193  }