diff --git a/go.mod b/go.mod index 3e97ad575..a43bf6d3b 100644 --- a/go.mod +++ b/go.mod @@ -5,7 +5,7 @@ go 1.17 require ( github.com/evanphx/json-patch v4.11.0+incompatible github.com/go-echarts/go-echarts/v2 v2.2.4 - github.com/gocrane/api v0.7.1-0.20220906050113-0f331eb419b0 + github.com/gocrane/api v0.8.1-0.20221215043447-1fb56d37bb86 github.com/google/cadvisor v0.39.2 github.com/jaypipes/ghw v0.9.0 github.com/mjibson/go-dsp v0.0.0-20180508042940-11479a337f12 diff --git a/go.sum b/go.sum index 6b3dea665..60c4e0952 100644 --- a/go.sum +++ b/go.sum @@ -348,8 +348,8 @@ github.com/gobwas/pool v0.2.1 h1:xfeeEhW7pwmX8nuLVlqbzVc7udMDrwetjEv+TZIz1og= github.com/gobwas/pool v0.2.1/go.mod h1:q8bcK0KcYlCgd9e7WYLm9LpyS+YeLd8JVDW6WezmKEw= github.com/gobwas/ws v1.1.0-rc.5 h1:QOAag7FoBaBYYHRqzqkhhd8fq5RTubvI4v3Ft/gDVVQ= github.com/gobwas/ws v1.1.0-rc.5/go.mod h1:nzvNcVha5eUziGrbxFCo6qFIojQHjJV5cLYIbezhfL0= -github.com/gocrane/api v0.7.1-0.20220906050113-0f331eb419b0 h1:IIHNT4bDsuBJq9JHHoQhUOrtE5Ec2Ug/Om8s8WQD8ws= -github.com/gocrane/api v0.7.1-0.20220906050113-0f331eb419b0/go.mod h1:GxI+t9AW8+NsHkz2JkPBIJN//9eLUjTZl1ScYAbXMbk= +github.com/gocrane/api v0.8.1-0.20221215043447-1fb56d37bb86 h1:pPNeLZzyQiX5GXphUWEOgX1ozpcydFLlOK7XyGSGdi8= +github.com/gocrane/api v0.8.1-0.20221215043447-1fb56d37bb86/go.mod h1:GxI+t9AW8+NsHkz2JkPBIJN//9eLUjTZl1ScYAbXMbk= github.com/godbus/dbus/v5 v5.0.3/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.0.4 h1:9349emZab16e7zQvpmsbtjc18ykshndd8y2PG3sgJbA= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= diff --git a/pkg/controller/recommendation/recommendation_rule_controller.go b/pkg/controller/recommendation/recommendation_rule_controller.go index cd6bb69c6..79652c8ce 100644 --- a/pkg/controller/recommendation/recommendation_rule_controller.go +++ b/pkg/controller/recommendation/recommendation_rule_controller.go @@ -9,7 +9,6 @@ import ( "time" corev1 "k8s.io/api/core/v1" - v1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/api/equality" "k8s.io/apimachinery/pkg/api/errors" "k8s.io/apimachinery/pkg/api/meta" @@ -80,7 +79,7 @@ func (c *RecommendationRuleController) Reconcile(ctx context.Context, req ctrl.R interval, err := time.ParseDuration(recommendationRule.Spec.RunInterval) if err != nil { - c.Recorder.Event(recommendationRule, v1.EventTypeNormal, "FailedParseRunInterval", err.Error()) + c.Recorder.Event(recommendationRule, corev1.EventTypeWarning, "FailedParseRunInterval", err.Error()) klog.Errorf("Failed to parse RunInterval, recommendationRule %s", klog.KObj(recommendationRule)) return ctrl.Result{}, err } @@ -112,7 +111,7 @@ func (c *RecommendationRuleController) doReconcile(ctx context.Context, recommen identities, err := c.getIdentities(ctx, recommendationRule) if err != nil { - c.Recorder.Event(recommendationRule, corev1.EventTypeNormal, "FailedSelectResource", err.Error()) + c.Recorder.Event(recommendationRule, corev1.EventTypeWarning, "FailedSelectResource", err.Error()) msg := fmt.Sprintf("Failed to get idenitities, RecommendationRule %s error %v", klog.KObj(recommendationRule), err) klog.Errorf(msg) c.UpdateStatus(ctx, recommendationRule, newStatus) @@ -156,7 +155,7 @@ func (c *RecommendationRuleController) doReconcile(ctx context.Context, recommen } err = c.Client.List(ctx, &currRecommendations, opts...) if err != nil { - c.Recorder.Event(recommendationRule, corev1.EventTypeNormal, "FailedSelectResource", err.Error()) + c.Recorder.Event(recommendationRule, corev1.EventTypeWarning, "FailedSelectResource", err.Error()) msg := fmt.Sprintf("Failed to get recomendations, RecommendationRule %s error %v", klog.KObj(recommendationRule), err) klog.Errorf(msg) c.UpdateStatus(ctx, recommendationRule, newStatus) @@ -351,7 +350,6 @@ func (c *RecommendationRuleController) executeMission(ctx context.Context, wg *s defer func() { mission.LastStartTime = &timeNow klog.Infof("Mission message: %s", mission.Message) - wg.Done() }() @@ -365,7 +363,7 @@ func (c *RecommendationRuleController) executeMission(ctx context.Context, wg *s recommendation = c.CreateRecommendationObject(recommendationRule, mission.TargetRef, id, mission.RecommenderRef.Name) } - r, err := c.RecommenderMgr.GetRecommender(mission.RecommenderRef.Name) + r, err := c.RecommenderMgr.GetRecommender(mission.RecommenderRef.Name, *recommendationRule) if err != nil { mission.Message = fmt.Sprintf("get recommender %s failed, %v", mission.RecommenderRef.Name, err) return @@ -439,7 +437,7 @@ func (c *RecommendationRuleController) UpdateStatus(ctx context.Context, recomme }) if err != nil { - c.Recorder.Event(recommendationRule, corev1.EventTypeNormal, "FailedUpdateStatus", err.Error()) + c.Recorder.Event(recommendationRule, corev1.EventTypeWarning, "FailedUpdateStatus", err.Error()) klog.Errorf("Failed to update status, RecommendationRule %s error %v", klog.KObj(recommendationRule), err) return } diff --git a/pkg/recommendation/config/config.go b/pkg/recommendation/config/config.go index eb6a359e0..b5f2018a5 100644 --- a/pkg/recommendation/config/config.go +++ b/pkg/recommendation/config/config.go @@ -7,9 +7,31 @@ import ( klog "k8s.io/klog/v2" "sigs.k8s.io/yaml" + analysisv1alph1 "github.com/gocrane/api/analysis/v1alpha1" "github.com/gocrane/crane/pkg/recommendation/recommender/apis" ) +func MergeRecommenderConfigFromRule(recommender apis.Recommender, recommendationRule analysisv1alph1.RecommendationRule) apis.Recommender { + if recommender.Config == nil { + recommender.Config = map[string]string{} + } + + for _, ruleRecommender := range recommendationRule.Spec.Recommenders { + if recommender.Name == ruleRecommender.Name { + if ruleRecommender.Config == nil { + ruleRecommender.Config = map[string]string{} + } + // merge config in configmap and config in RecommendationRule. + // if conflicted, use the config value in RecommendationRule(has the highest priority). + for configKey, configValue := range ruleRecommender.Config { + recommender.Config[configKey] = configValue + } + } + } + + return recommender +} + func LoadRecommenderConfigFromFile(filePath string) (*apis.RecommenderConfiguration, error) { if filePath == "" { return nil, fmt.Errorf("file path not specified") diff --git a/pkg/recommendation/manager.go b/pkg/recommendation/manager.go index 246894552..7451cdb71 100644 --- a/pkg/recommendation/manager.go +++ b/pkg/recommendation/manager.go @@ -7,6 +7,7 @@ import ( "github.com/fsnotify/fsnotify" "k8s.io/klog/v2" + analysisv1alph1 "github.com/gocrane/api/analysis/v1alpha1" "github.com/gocrane/crane/pkg/oom" "github.com/gocrane/crane/pkg/providers" "github.com/gocrane/crane/pkg/recommendation/config" @@ -21,7 +22,7 @@ import ( type RecommenderManager interface { // GetRecommender return a registered recommender - GetRecommender(recommenderName string) (recommender.Recommender, error) + GetRecommender(recommenderName string, recommendationRule analysisv1alph1.RecommendationRule) (recommender.Recommender, error) } func NewRecommenderManager(recommendationConfiguration string, oomRecorder oom.Recorder, realtimeDataSources map[providers.DataSourceType]providers.RealTime, historyDataSources map[providers.DataSourceType]providers.History) RecommenderManager { @@ -52,7 +53,7 @@ type manager struct { oomRecorder oom.Recorder } -func (m *manager) GetRecommender(recommenderName string) (recommender.Recommender, error) { +func (m *manager) GetRecommender(recommenderName string, recommendationRule analysisv1alph1.RecommendationRule) (recommender.Recommender, error) { m.lock.Lock() defer m.lock.Unlock() @@ -60,13 +61,13 @@ func (m *manager) GetRecommender(recommenderName string) (recommender.Recommende if r.Name == recommenderName { switch recommenderName { case recommender.ReplicasRecommender: - return replicas.NewReplicasRecommender(r) + return replicas.NewReplicasRecommender(r, recommendationRule) case recommender.HPARecommender: - return hpa.NewHPARecommender(r) + return hpa.NewHPARecommender(r, recommendationRule) case recommender.ResourceRecommender: - return resource.NewResourceRecommender(r, m.oomRecorder) + return resource.NewResourceRecommender(r, recommendationRule, m.oomRecorder) case recommender.IdleNodeRecommender: - return idlenode.NewIdleNodeRecommender(r) + return idlenode.NewIdleNodeRecommender(r, recommendationRule) default: return nil, fmt.Errorf("unknown recommender name: %s", recommenderName) } diff --git a/pkg/recommendation/recommender/hpa/registry.go b/pkg/recommendation/recommender/hpa/registry.go index 19f85c43a..3094b7552 100644 --- a/pkg/recommendation/recommender/hpa/registry.go +++ b/pkg/recommendation/recommender/hpa/registry.go @@ -3,6 +3,8 @@ package hpa import ( "strconv" + analysisv1alph1 "github.com/gocrane/api/analysis/v1alpha1" + "github.com/gocrane/crane/pkg/recommendation/config" "github.com/gocrane/crane/pkg/recommendation/recommender" "github.com/gocrane/crane/pkg/recommendation/recommender/apis" "github.com/gocrane/crane/pkg/recommendation/recommender/replicas" @@ -26,7 +28,9 @@ func (rr *HPARecommender) Name() string { } // NewHPARecommender create a new hpa recommender. -func NewHPARecommender(recommender apis.Recommender) (*HPARecommender, error) { +func NewHPARecommender(recommender apis.Recommender, recommendationRule analysisv1alph1.RecommendationRule) (*HPARecommender, error) { + recommender = config.MergeRecommenderConfigFromRule(recommender, recommendationRule) + predictable, exists := recommender.Config["predictable"] if !exists { predictable = "false" @@ -90,7 +94,7 @@ func NewHPARecommender(recommender apis.Recommender) (*HPARecommender, error) { return nil, err } - replicasRecommender, err := replicas.NewReplicasRecommender(recommender) + replicasRecommender, err := replicas.NewReplicasRecommender(recommender, recommendationRule) if err != nil { return nil, err } diff --git a/pkg/recommendation/recommender/idlenode/registry.go b/pkg/recommendation/recommender/idlenode/registry.go index c093d6794..a28729887 100644 --- a/pkg/recommendation/recommender/idlenode/registry.go +++ b/pkg/recommendation/recommender/idlenode/registry.go @@ -3,6 +3,8 @@ package idlenode import ( "strconv" + analysisv1alph1 "github.com/gocrane/api/analysis/v1alpha1" + "github.com/gocrane/crane/pkg/recommendation/config" "github.com/gocrane/crane/pkg/recommendation/recommender" "github.com/gocrane/crane/pkg/recommendation/recommender/apis" "github.com/gocrane/crane/pkg/recommendation/recommender/base" @@ -21,10 +23,8 @@ func (inr *IdleNodeRecommender) Name() string { } // NewIdleNodeRecommender create a new idle node recommender. -func NewIdleNodeRecommender(recommender apis.Recommender) (*IdleNodeRecommender, error) { - if recommender.Config == nil { - recommender.Config = map[string]string{} - } +func NewIdleNodeRecommender(recommender apis.Recommender, recommendationRule analysisv1alph1.RecommendationRule) (*IdleNodeRecommender, error) { + recommender = config.MergeRecommenderConfigFromRule(recommender, recommendationRule) cpuRequestUtilization, exists := recommender.Config["cpu-request-utilization"] if !exists { diff --git a/pkg/recommendation/recommender/replicas/registry.go b/pkg/recommendation/recommender/replicas/registry.go index 674b18612..7b7f4af98 100644 --- a/pkg/recommendation/recommender/replicas/registry.go +++ b/pkg/recommendation/recommender/replicas/registry.go @@ -3,6 +3,8 @@ package replicas import ( "strconv" + analysisv1alph1 "github.com/gocrane/api/analysis/v1alpha1" + "github.com/gocrane/crane/pkg/recommendation/config" "github.com/gocrane/crane/pkg/recommendation/recommender" "github.com/gocrane/crane/pkg/recommendation/recommender/apis" "github.com/gocrane/crane/pkg/recommendation/recommender/base" @@ -27,7 +29,9 @@ func (rr *ReplicasRecommender) Name() string { } // NewReplicasRecommender create a new replicas recommender. -func NewReplicasRecommender(recommender apis.Recommender) (*ReplicasRecommender, error) { +func NewReplicasRecommender(recommender apis.Recommender, recommendationRule analysisv1alph1.RecommendationRule) (*ReplicasRecommender, error) { + recommender = config.MergeRecommenderConfigFromRule(recommender, recommendationRule) + workloadMinReplicas, exists := recommender.Config["workload-min-replicas"] if !exists { workloadMinReplicas = "1" diff --git a/pkg/recommendation/recommender/resource/registry.go b/pkg/recommendation/recommender/resource/registry.go index c75be209a..baecbe866 100644 --- a/pkg/recommendation/recommender/resource/registry.go +++ b/pkg/recommendation/recommender/resource/registry.go @@ -4,7 +4,9 @@ import ( "strconv" "time" + analysisv1alph1 "github.com/gocrane/api/analysis/v1alpha1" "github.com/gocrane/crane/pkg/oom" + "github.com/gocrane/crane/pkg/recommendation/config" "github.com/gocrane/crane/pkg/recommendation/recommender" "github.com/gocrane/crane/pkg/recommendation/recommender/apis" "github.com/gocrane/crane/pkg/recommendation/recommender/base" @@ -37,10 +39,8 @@ func (rr *ResourceRecommender) Name() string { } // NewResourceRecommender create a new resource recommender. -func NewResourceRecommender(recommender apis.Recommender, oomRecorder oom.Recorder) (*ResourceRecommender, error) { - if recommender.Config == nil { - recommender.Config = map[string]string{} - } +func NewResourceRecommender(recommender apis.Recommender, recommendationRule analysisv1alph1.RecommendationRule, oomRecorder oom.Recorder) (*ResourceRecommender, error) { + recommender = config.MergeRecommenderConfigFromRule(recommender, recommendationRule) cpuSampleInterval, exists := recommender.Config["cpu-sample-interval"] if !exists {