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

     1  package framework
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"fmt"
     7  	"sync"
     8  
     9  	jsonpatch "github.com/evanphx/json-patch"
    10  	appsv1 "k8s.io/api/apps/v1"
    11  	autoscalingapiv1 "k8s.io/api/autoscaling/v1"
    12  	autoscalingv2 "k8s.io/api/autoscaling/v2beta2"
    13  	corev1 "k8s.io/api/core/v1"
    14  	"k8s.io/apimachinery/pkg/api/meta"
    15  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    16  	"k8s.io/client-go/scale"
    17  	"k8s.io/klog/v2"
    18  	"sigs.k8s.io/controller-runtime/pkg/client"
    19  
    20  	"github.com/gocrane/api/analysis/v1alpha1"
    21  	autoscalingapi "github.com/gocrane/api/autoscaling/v1alpha1"
    22  
    23  	"github.com/gocrane/crane/pkg/common"
    24  	"github.com/gocrane/crane/pkg/metricnaming"
    25  	"github.com/gocrane/crane/pkg/oom"
    26  	"github.com/gocrane/crane/pkg/prediction/config"
    27  	predictormgr "github.com/gocrane/crane/pkg/predictor"
    28  	"github.com/gocrane/crane/pkg/providers"
    29  	"github.com/gocrane/crane/pkg/utils"
    30  )
    31  
    32  type RecommendationContext struct {
    33  	Context context.Context
    34  	// The kubernetes resource object reference of recommendation flow.
    35  	Identity ObjectIdentity
    36  	// Target Object
    37  	Object client.Object
    38  	// Protecting inputValues
    39  	inputValuesMutex sync.RWMutex
    40  	// Time series data from data source.
    41  	inputValues map[string][]*common.TimeSeries
    42  	// Result series from prediction
    43  	ResultValues []*common.TimeSeries
    44  	// DataProviders contains data source of your recommendation flow.
    45  	DataProviders map[providers.DataSourceType]providers.History
    46  	// Recommendation store result of recommendation flow.
    47  	Recommendation *v1alpha1.Recommendation
    48  	// When cancel channel accept signal indicates that the context has been canceled. The recommendation should stop executing as soon as possible.
    49  	// CancelCh <-chan struct{}
    50  	// RecommendationRule for the context
    51  	RecommendationRule *v1alpha1.RecommendationRule
    52  	// metrics namer for datasource provider
    53  	MetricNamer metricnaming.MetricNamer
    54  	// Algorithm Config
    55  	AlgorithmConfig *config.Config
    56  	// Manager of predict algorithm
    57  	PredictorMgr predictormgr.Manager
    58  	// Pod template
    59  	PodTemplate corev1.PodTemplateSpec
    60  	// Client
    61  	Client client.Client
    62  	// RestMapper
    63  	RestMapper meta.RESTMapper
    64  	// ScalesGetter
    65  	ScaleClient scale.ScalesGetter
    66  	// oom.Recorder
    67  	OOMRecorder oom.Recorder
    68  	// Scale
    69  	Scale *autoscalingapiv1.Scale
    70  	// Pods in recommendation
    71  	Pods []corev1.Pod
    72  	// HPA Object
    73  	HPA *autoscalingv2.HorizontalPodAutoscaler
    74  	// HPA Object
    75  	EHPA *autoscalingapi.EffectiveHorizontalPodAutoscaler
    76  }
    77  
    78  func NewRecommendationContext(context context.Context, identity ObjectIdentity, recommendationRule *v1alpha1.RecommendationRule, predictorMgr predictormgr.Manager, dataProviders map[providers.DataSourceType]providers.History, recommendation *v1alpha1.Recommendation, client client.Client, scaleClient scale.ScalesGetter, oomRecorder oom.Recorder) RecommendationContext {
    79  	return RecommendationContext{
    80  		Context:            context,
    81  		Identity:           identity,
    82  		Object:             &identity.Object,
    83  		inputValues:        make(map[string][]*common.TimeSeries),
    84  		PredictorMgr:       predictorMgr,
    85  		DataProviders:      dataProviders,
    86  		RecommendationRule: recommendationRule,
    87  		Recommendation:     recommendation,
    88  		Client:             client,
    89  		RestMapper:         client.RESTMapper(),
    90  		ScaleClient:        scaleClient,
    91  		OOMRecorder:        oomRecorder,
    92  		//CancelCh:       context.Done(),
    93  	}
    94  }
    95  
    96  func NewRecommendationContextForObserve(recommendation *v1alpha1.Recommendation, restMapper meta.RESTMapper, scaleClient scale.ScalesGetter) RecommendationContext {
    97  	return RecommendationContext{
    98  		inputValues:    make(map[string][]*common.TimeSeries),
    99  		Recommendation: recommendation,
   100  		RestMapper:     restMapper,
   101  		ScaleClient:    scaleClient,
   102  	}
   103  }
   104  
   105  func (ctx *RecommendationContext) AddInputValue(key string, timeSeries []*common.TimeSeries) {
   106  	ctx.inputValuesMutex.Lock()
   107  	defer ctx.inputValuesMutex.Unlock()
   108  	ctx.inputValues[key] = timeSeries
   109  }
   110  
   111  func (ctx *RecommendationContext) InputValue(key string) []*common.TimeSeries {
   112  	ctx.inputValuesMutex.RLock()
   113  	defer ctx.inputValuesMutex.RUnlock()
   114  	return ctx.inputValues[key]
   115  }
   116  
   117  func (ctx *RecommendationContext) String() string {
   118  	return fmt.Sprintf("RecommendationRule(%s) Target(%s/%s)", ctx.RecommendationRule.Name, ctx.Object.GetNamespace(), ctx.Object.GetName())
   119  }
   120  
   121  type ObjectIdentity struct {
   122  	Namespace  string
   123  	APIVersion string
   124  	Kind       string
   125  	Name       string
   126  	Labels     map[string]string
   127  	Object     unstructured.Unstructured
   128  }
   129  
   130  func (id ObjectIdentity) GetObjectReference() corev1.ObjectReference {
   131  	return corev1.ObjectReference{Kind: id.Kind, APIVersion: id.APIVersion, Namespace: id.Namespace, Name: id.Name}
   132  }
   133  
   134  //func (ctx *RecommendationContext) Canceled() bool {
   135  //	select {
   136  //	case <-ctx.CancelCh:
   137  //		return true
   138  //	default:
   139  //		return false
   140  //	}
   141  //}
   142  
   143  func ObjectConversion(object interface{}, target interface{}) error {
   144  	bytes, err := json.Marshal(object)
   145  	if err != nil {
   146  		return err
   147  	}
   148  
   149  	return json.Unmarshal(bytes, target)
   150  }
   151  
   152  func RetrievePodTemplate(ctx *RecommendationContext) error {
   153  	unstructed := ctx.Object.(*unstructured.Unstructured)
   154  
   155  	// fill PodTemplate
   156  	podTemplateObject, found, err := unstructured.NestedMap(unstructed.Object, "spec", "template")
   157  	if !found || err != nil {
   158  		return fmt.Errorf("get template from unstructed object %s failed. ", klog.KObj(unstructed))
   159  	}
   160  
   161  	return ObjectConversion(podTemplateObject, &ctx.PodTemplate)
   162  }
   163  
   164  func RetrieveScale(ctx *RecommendationContext) error {
   165  	targetRef := autoscalingv2.CrossVersionObjectReference{
   166  		APIVersion: ctx.Recommendation.Spec.TargetRef.APIVersion,
   167  		Kind:       ctx.Recommendation.Spec.TargetRef.Kind,
   168  		Name:       ctx.Recommendation.Spec.TargetRef.Name,
   169  	}
   170  
   171  	if ctx.Recommendation.Spec.TargetRef.Kind != "DaemonSet" {
   172  		scale, _, err := utils.GetScale(context.TODO(), ctx.RestMapper, ctx.ScaleClient, ctx.Recommendation.Spec.TargetRef.Namespace, targetRef)
   173  		if err != nil {
   174  			return err
   175  		}
   176  		ctx.Scale = scale
   177  	}
   178  	return nil
   179  }
   180  
   181  func RetrievePods(ctx *RecommendationContext) error {
   182  	if ctx.Recommendation.Spec.TargetRef.Kind == "Node" {
   183  		pods, err := utils.GetNodePods(ctx.Client, ctx.Recommendation.Spec.TargetRef.Name)
   184  		ctx.Pods = pods
   185  		return err
   186  	} else if ctx.Recommendation.Spec.TargetRef.Kind == "DaemonSet" {
   187  		var daemonSet appsv1.DaemonSet
   188  		if err := ObjectConversion(ctx.Object, &daemonSet); err != nil {
   189  			return err
   190  		}
   191  		pods, err := utils.GetDaemonSetPods(ctx.Client, ctx.Recommendation.Spec.TargetRef.Namespace, ctx.Recommendation.Spec.TargetRef.Name)
   192  		ctx.Pods = pods
   193  		return err
   194  	} else if ctx.Recommendation.Spec.TargetRef.Kind == "Service" {
   195  		var svc corev1.Service
   196  		if err := ObjectConversion(ctx.Object, &svc); err != nil {
   197  			return err
   198  		}
   199  		pods, err := utils.GetServicePods(ctx.Client, &svc)
   200  		ctx.Pods = pods
   201  		return err
   202  	} else {
   203  		pods, err := utils.GetPodsFromScale(ctx.Client, ctx.Scale)
   204  		ctx.Pods = pods
   205  		return err
   206  	}
   207  }
   208  
   209  func ConvertToRecommendationInfos(src interface{}, target interface{}) ([]byte, []byte, error) {
   210  	oldBytes, err := json.Marshal(src)
   211  	if err != nil {
   212  		return nil, nil, fmt.Errorf("encode error %s. ", err)
   213  	}
   214  
   215  	newBytes, err := json.Marshal(target)
   216  	if err != nil {
   217  		return nil, nil, fmt.Errorf("encode error %s. ", err)
   218  	}
   219  
   220  	newPatch, err := jsonpatch.CreateMergePatch(oldBytes, newBytes)
   221  	if err != nil {
   222  		return nil, nil, fmt.Errorf("create merge patch error %s. ", err)
   223  	}
   224  	oldPatch, err := jsonpatch.CreateMergePatch(newBytes, oldBytes)
   225  	if err != nil {
   226  		return nil, nil, fmt.Errorf("create merge patch error %s. ", err)
   227  	}
   228  
   229  	return newPatch, oldPatch, err
   230  }