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 }