From 4dc68eb8b08fd05eb77edaa24518f3b71e88bc83 Mon Sep 17 00:00:00 2001 From: lbbniu Date: Mon, 8 May 2023 18:37:30 +0800 Subject: [PATCH] feat: Find the Orphan Service Plugin in k8s cluster --- deploy/craned/deployment.yaml | 4 + pkg/prometheus-adapter/config_fetcher.go | 8 +- pkg/recommendation/framework/context.go | 11 ++- pkg/recommendation/manager.go | 1 + pkg/recommendation/recommender/const.go | 3 + .../recommender/service/filter.go | 48 ++++++++++ .../recommender/service/observe.go | 10 +++ .../recommender/service/prepare.go | 87 +++++++++++++++++++ .../recommender/service/recommend.go | 78 +++++++++++++++++ .../recommender/service/registry.go | 67 ++++++++++++++ .../recommender/volumes/observe.go | 2 +- .../recommender/volumes/prepare.go | 8 +- .../recommender/volumes/recommend.go | 6 +- .../recommender/volumes/registry.go | 2 +- pkg/utils/expression_prom_default.go | 16 ++++ pkg/utils/pod.go | 37 +++++++- pkg/utils/prediction.go | 14 +-- 17 files changed, 374 insertions(+), 28 deletions(-) create mode 100644 pkg/recommendation/recommender/service/filter.go create mode 100644 pkg/recommendation/recommender/service/observe.go create mode 100644 pkg/recommendation/recommender/service/prepare.go create mode 100644 pkg/recommendation/recommender/service/recommend.go create mode 100644 pkg/recommendation/recommender/service/registry.go diff --git a/deploy/craned/deployment.yaml b/deploy/craned/deployment.yaml index f7913bbc3..603f68cc6 100644 --- a/deploy/craned/deployment.yaml +++ b/deploy/craned/deployment.yaml @@ -123,6 +123,10 @@ data: acceptedResources: - kind: PersistentVolume apiVersion: v1 + - name: Service + acceptedResources: + - kind: Service + apiVersion: v1 --- apiVersion: v1 kind: ConfigMap diff --git a/pkg/prometheus-adapter/config_fetcher.go b/pkg/prometheus-adapter/config_fetcher.go index d459b3f95..96fe64608 100644 --- a/pkg/prometheus-adapter/config_fetcher.go +++ b/pkg/prometheus-adapter/config_fetcher.go @@ -42,16 +42,12 @@ func (pc *PrometheusAdapterConfigFetcher) Reconcile(ctx context.Context, req ctr klog.V(4).Infof("Got prometheus adapter configmap %s", req.NamespacedName) //get configmap content - cm := &corev1.ConfigMap{} - err := pc.Client.Get(ctx, req.NamespacedName, cm) + var cm corev1.ConfigMap + err := pc.Client.Get(ctx, req.NamespacedName, &cm) if err != nil { return ctrl.Result{}, client.IgnoreNotFound(err) } - if cm == nil { - return ctrl.Result{}, fmt.Errorf("get configmap %s/%s failed", req.NamespacedName.Namespace, req.NamespacedName.Name) - } - cfg, err := config.FromYAML([]byte(cm.Data[pc.AdapterConfigMapKey])) if err != nil { klog.Errorf("Got metricsDiscoveryConfig failed[%s] %v", pc.AdapterConfigMapName, err) diff --git a/pkg/recommendation/framework/context.go b/pkg/recommendation/framework/context.go index ab6b600c4..1a8c05abf 100644 --- a/pkg/recommendation/framework/context.go +++ b/pkg/recommendation/framework/context.go @@ -199,13 +199,20 @@ func RetrievePods(ctx *RecommendationContext) error { return err } else if ctx.Recommendation.Spec.TargetRef.Kind == "DaemonSet" { var daemonSet appsv1.DaemonSet - err := ObjectConversion(ctx.Object, &daemonSet) - if err != nil { + if err := ObjectConversion(ctx.Object, &daemonSet); err != nil { return err } pods, err := utils.GetDaemonSetPods(ctx.Client, ctx.Recommendation.Spec.TargetRef.Namespace, ctx.Recommendation.Spec.TargetRef.Name) ctx.Pods = pods return err + } else if ctx.Recommendation.Spec.TargetRef.Kind == "Service" { + var svc corev1.Service + if err := ObjectConversion(ctx.Object, &svc); err != nil { + return err + } + pods, err := utils.GetServicePods(ctx.Client, &svc) + ctx.Pods = pods + return err } else { pods, err := utils.GetPodsFromScale(ctx.Client, ctx.Scale) ctx.Pods = pods diff --git a/pkg/recommendation/manager.go b/pkg/recommendation/manager.go index 2bcb5be8a..2bcf01107 100644 --- a/pkg/recommendation/manager.go +++ b/pkg/recommendation/manager.go @@ -16,6 +16,7 @@ import ( _ "github.com/gocrane/crane/pkg/recommendation/recommender/idlenode" _ "github.com/gocrane/crane/pkg/recommendation/recommender/replicas" _ "github.com/gocrane/crane/pkg/recommendation/recommender/resource" + _ "github.com/gocrane/crane/pkg/recommendation/recommender/service" ) type RecommenderManager interface { diff --git a/pkg/recommendation/recommender/const.go b/pkg/recommendation/recommender/const.go index 180e27404..98c17ad4f 100644 --- a/pkg/recommendation/recommender/const.go +++ b/pkg/recommendation/recommender/const.go @@ -15,4 +15,7 @@ const ( // VolumesRecommender name VolumesRecommender string = "Volumes" + + // ServiceRecommender name + ServiceRecommender string = "Service" ) diff --git a/pkg/recommendation/recommender/service/filter.go b/pkg/recommendation/recommender/service/filter.go new file mode 100644 index 000000000..098d58c93 --- /dev/null +++ b/pkg/recommendation/recommender/service/filter.go @@ -0,0 +1,48 @@ +package service + +import ( + "fmt" + + corev1 "k8s.io/api/core/v1" + "sigs.k8s.io/controller-runtime/pkg/client" + + "github.com/gocrane/crane/pkg/recommendation/framework" +) + +// Filter out k8s resources that are not supported by the recommender. +func (s *ServiceRecommender) Filter(ctx *framework.RecommendationContext) error { + var err error + + // filter resource that not match objectIdentity + if err = s.BaseRecommender.Filter(ctx); err != nil { + return err + } + var svc corev1.Service + if err = framework.ObjectConversion(ctx.Object, &svc); err != nil { + return err + } + + if svc.Spec.Type != corev1.ServiceTypeLoadBalancer { + return fmt.Errorf("service: %v type: %s is not a LoadBalancer", ctx.Object.GetName(), svc.Spec.Type) + } + + // filter Endpoints not empty + var ep corev1.Endpoints + if err = ctx.Client.Get(ctx.Context, client.ObjectKeyFromObject(ctx.Object), &ep); client.IgnoreNotFound(err) != nil { + return err + } + for _, ss := range ep.Subsets { + if len(ss.Addresses) != 0 { + return fmt.Errorf("service: %v addresses: %v not empty", ctx.Object.GetName(), ss.Addresses) + } + if len(ss.NotReadyAddresses) != 0 { + return fmt.Errorf("service: %v NotReadyAddresses: %v not empty", ctx.Object.GetName(), ss.NotReadyAddresses) + } + } + + if err = framework.RetrievePods(ctx); err != nil { + return err + } + + return nil +} diff --git a/pkg/recommendation/recommender/service/observe.go b/pkg/recommendation/recommender/service/observe.go new file mode 100644 index 000000000..6ecf46643 --- /dev/null +++ b/pkg/recommendation/recommender/service/observe.go @@ -0,0 +1,10 @@ +package service + +import ( + "github.com/gocrane/crane/pkg/recommendation/framework" +) + +// Observe enhance the observability. +func (s *ServiceRecommender) Observe(ctx *framework.RecommendationContext) error { + return nil +} diff --git a/pkg/recommendation/recommender/service/prepare.go b/pkg/recommendation/recommender/service/prepare.go new file mode 100644 index 000000000..49832fc56 --- /dev/null +++ b/pkg/recommendation/recommender/service/prepare.go @@ -0,0 +1,87 @@ +package service + +import ( + "fmt" + "time" + + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/labels" + "k8s.io/klog/v2" + + "github.com/gocrane/crane/pkg/metricnaming" + "github.com/gocrane/crane/pkg/providers" + "github.com/gocrane/crane/pkg/recommendation/framework" + "github.com/gocrane/crane/pkg/utils" +) + +const callerFormat = "ServiceRecommender-%s-%s" + +// CheckDataProviders in PrePrepare phase, will create data source provider via your recommendation config. +func (s *ServiceRecommender) CheckDataProviders(ctx *framework.RecommendationContext) error { + if err := s.BaseRecommender.CheckDataProviders(ctx); err != nil { + return err + } + + return nil +} + +func (s *ServiceRecommender) CollectData(ctx *framework.RecommendationContext) error { + if len(ctx.Pods) == 0 { + return nil + } + + var workloadRef *metav1.OwnerReference + for _, pod := range ctx.Pods { + workloadRef = utils.GetPodOwnerReference(ctx.Context, ctx.Client, &pod) + if workloadRef != nil { + break + } + } + if workloadRef == nil { + return fmt.Errorf("could not find all pod OwnerReferences for Service %s selector", ctx.Object.GetName()) + } + podName := utils.GetPodNameReg(workloadRef.Name, workloadRef.Kind) + + labelSelector := labels.SelectorFromSet(ctx.Identity.Labels) + caller := fmt.Sprintf(callerFormat, klog.KObj(ctx.Recommendation), ctx.Recommendation.UID) + timeNow := time.Now() + metricNamer := metricnaming.ResourceToGeneralMetricNamer(utils.GetWorkloadNetReceiveBytesExpression(podName), corev1.ResourceServices, labelSelector, caller) + if err := metricNamer.Validate(); err != nil { + return err + } + ctx.MetricNamer = metricNamer + + // get pod net receive bytes + klog.Infof("%s: %s NetReceiveBytes %s", ctx.String(), s.Name(), ctx.MetricNamer.BuildUniqueKey()) + tsList, err := ctx.DataProviders[providers.PrometheusDataSource].QueryTimeSeries(ctx.MetricNamer, timeNow.Add(-time.Hour*24*7), timeNow, time.Minute) + if err != nil { + return fmt.Errorf("%s query pod net receive bytes historic metrics failed: %v ", s.Name(), err) + } + if len(tsList) != 1 { + return fmt.Errorf("%s query pod net receive bytes historic metrics data is unexpected, List length is %d ", s.Name(), len(tsList)) + } + ctx.AddInputValue(netReceiveBytesKey, tsList) + + metricNamer = metricnaming.ResourceToGeneralMetricNamer(utils.GetWorkloadNetTransferBytesExpression(podName), corev1.ResourceServices, labelSelector, caller) + if err = metricNamer.Validate(); err != nil { + return err + } + + // get pod net transfer bytes + klog.Infof("%s: %s NetTransferBytes %s", ctx.String(), s.Name(), ctx.MetricNamer.BuildUniqueKey()) + tsList, err = ctx.DataProviders[providers.PrometheusDataSource].QueryTimeSeries(ctx.MetricNamer, timeNow.Add(-time.Hour*24*7), timeNow, time.Minute) + if err != nil { + return fmt.Errorf("%s query pod net transfer bytes historic metrics failed: %v ", s.Name(), err) + } + if len(tsList) != 1 { + return fmt.Errorf("%s query pod net transfer bytes historic metrics data is unexpected, List length is %d ", s.Name(), len(tsList)) + } + ctx.AddInputValue(netTransferBytesKey, tsList) + + return nil +} + +func (s *ServiceRecommender) PostProcessing(ctx *framework.RecommendationContext) error { + return nil +} diff --git a/pkg/recommendation/recommender/service/recommend.go b/pkg/recommendation/recommender/service/recommend.go new file mode 100644 index 000000000..9de404eee --- /dev/null +++ b/pkg/recommendation/recommender/service/recommend.go @@ -0,0 +1,78 @@ +package service + +import ( + "fmt" + "github.com/montanaflynn/stats" + + "github.com/gocrane/crane/pkg/common" + "github.com/gocrane/crane/pkg/recommendation/framework" +) + +func (s *ServiceRecommender) PreRecommend(ctx *framework.RecommendationContext) error { + return nil +} + +func (s *ServiceRecommender) Recommend(ctx *framework.RecommendationContext) error { + if len(ctx.Pods) == 0 { + ctx.Recommendation.Status.Action = "Delete" + ctx.Recommendation.Status.Description = "It is a Orphan Service, Pod count is 0" + return nil + } + + // check if pod net receive bytes lt config value + if netReceiveBytes := s.getMaxValue(s.netReceiveBytes, ctx.InputValue(netReceiveBytesKey)); netReceiveBytes > s.netReceiveBytes { + return fmt.Errorf("Service %s is not a Orphan Service, because the config value is %f, but the net receive bytes is %f ", ctx.Object.GetName(), s.netReceiveBytes, netReceiveBytes) + } + + // check if pod net transfer bytes lt config value + if netTransferBytes := s.getMaxValue(s.netTransferBytes, ctx.InputValue(netTransferBytesKey)); netTransferBytes > s.netTransferBytes { + return fmt.Errorf("Service %s is not a Orphan Service, because the config value is %f, but the net transfer bytes is %f ", ctx.Object.GetName(), s.netTransferBytes, netTransferBytes) + } + + // check if pod net receive percentile lt config value + if netReceivePercentile := s.getPercentile(s.netTransferBytes, ctx.InputValue(netReceiveBytesKey)); netReceivePercentile > s.netReceivePercentile { + return fmt.Errorf("Service %s is not a Orphan Service, because the config value is %f, but the net receive percentile is %f ", ctx.Object.GetName(), s.netReceivePercentile, netReceivePercentile) + } + + // check if pod net transfer percentile lt config value + if netTransferPercentile := s.getPercentile(s.netTransferBytes, ctx.InputValue(netTransferBytesKey)); netTransferPercentile > s.netTransferPercentile { + return fmt.Errorf("Service %s is not a Orphan Service, because the config value is %f, but the net transfer percentile is %f ", ctx.Object.GetName(), s.netTransferPercentile, netTransferPercentile) + } + + ctx.Recommendation.Status.Action = "Delete" + ctx.Recommendation.Status.Description = "It is a Orphan Service, Pod net bytes low" + return nil +} + +// Policy add some logic for result of recommend phase. +func (s *ServiceRecommender) Policy(ctx *framework.RecommendationContext) error { + return nil +} + +func (s *ServiceRecommender) getMaxValue(configValue float64, ts []*common.TimeSeries) float64 { + if configValue == 0 { + return configValue + } + var maxValue float64 + for _, ss := range ts[0].Samples { + if ss.Value > maxValue { + maxValue = ss.Value + } + } + return maxValue +} + +func (s *ServiceRecommender) getPercentile(configValue float64, ts []*common.TimeSeries) float64 { + if configValue == 0 { + return configValue + } + var input stats.Float64Data + for _, ss := range ts[0].Samples { + input = append(input, ss.Value) + } + percentile, err := stats.Percentile(input, configValue) + if err != nil { + return configValue + } + return percentile +} diff --git a/pkg/recommendation/recommender/service/registry.go b/pkg/recommendation/recommender/service/registry.go new file mode 100644 index 000000000..216f001dc --- /dev/null +++ b/pkg/recommendation/recommender/service/registry.go @@ -0,0 +1,67 @@ +package service + +import ( + 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" +) + +const ( + netReceiveBytesKey = "net-receive-bytes" + netTransferBytesKey = "net-transfer-bytes" + netReceivePercentileKey = "net-receive-percentile" + netTransferPercentileKey = "net-transfer-percentile" +) + +var _ recommender.Recommender = &ServiceRecommender{} + +type ServiceRecommender struct { + base.BaseRecommender + netReceiveBytes float64 + netTransferBytes float64 + netReceivePercentile float64 + netTransferPercentile float64 +} + +func init() { + recommender.RegisterRecommenderProvider(recommender.ServiceRecommender, NewServiceRecommender) +} + +func (s *ServiceRecommender) Name() string { + return recommender.ServiceRecommender +} + +// NewServiceRecommender create a new service recommender. +func NewServiceRecommender(recommender apis.Recommender, recommendationRule analysisv1alph1.RecommendationRule) (recommender.Recommender, error) { + recommender = config.MergeRecommenderConfigFromRule(recommender, recommendationRule) + + netReceiveBytes, err := recommender.GetConfigFloat(netReceiveBytesKey, 0) + if err != nil { + return nil, err + } + + netTransferBytes, err := recommender.GetConfigFloat(netTransferBytesKey, 0) + if err != nil { + return nil, err + } + + netReceivePercentile, err := recommender.GetConfigFloat(netReceivePercentileKey, 0) + if err != nil { + return nil, err + } + + netTransferPercentile, err := recommender.GetConfigFloat(netTransferPercentileKey, 0) + if err != nil { + return nil, err + } + + return &ServiceRecommender{ + *base.NewBaseRecommender(recommender), + netReceiveBytes, + netTransferBytes, + netReceivePercentile, + netTransferPercentile, + }, nil +} diff --git a/pkg/recommendation/recommender/volumes/observe.go b/pkg/recommendation/recommender/volumes/observe.go index ab48d714a..9de0777ac 100644 --- a/pkg/recommendation/recommender/volumes/observe.go +++ b/pkg/recommendation/recommender/volumes/observe.go @@ -5,6 +5,6 @@ import ( ) // Observe enhance the observability. -func (s *VolumesRecommender) Observe(ctx *framework.RecommendationContext) error { +func (vr *VolumesRecommender) Observe(ctx *framework.RecommendationContext) error { return nil } diff --git a/pkg/recommendation/recommender/volumes/prepare.go b/pkg/recommendation/recommender/volumes/prepare.go index dc7227123..b434c7af4 100644 --- a/pkg/recommendation/recommender/volumes/prepare.go +++ b/pkg/recommendation/recommender/volumes/prepare.go @@ -5,18 +5,18 @@ import ( ) // CheckDataProviders in PrePrepare phase, will create data source provider via your recommendation config. -func (rr *VolumesRecommender) CheckDataProviders(ctx *framework.RecommendationContext) error { - if err := rr.BaseRecommender.CheckDataProviders(ctx); err != nil { +func (vr *VolumesRecommender) CheckDataProviders(ctx *framework.RecommendationContext) error { + if err := vr.BaseRecommender.CheckDataProviders(ctx); err != nil { return err } return nil } -func (rr *VolumesRecommender) CollectData(ctx *framework.RecommendationContext) error { +func (vr *VolumesRecommender) CollectData(ctx *framework.RecommendationContext) error { return nil } -func (rr *VolumesRecommender) PostProcessing(ctx *framework.RecommendationContext) error { +func (vr *VolumesRecommender) PostProcessing(ctx *framework.RecommendationContext) error { return nil } diff --git a/pkg/recommendation/recommender/volumes/recommend.go b/pkg/recommendation/recommender/volumes/recommend.go index 5463bf983..d1740b018 100644 --- a/pkg/recommendation/recommender/volumes/recommend.go +++ b/pkg/recommendation/recommender/volumes/recommend.go @@ -4,17 +4,17 @@ import ( "github.com/gocrane/crane/pkg/recommendation/framework" ) -func (s *VolumesRecommender) PreRecommend(ctx *framework.RecommendationContext) error { +func (vr *VolumesRecommender) PreRecommend(ctx *framework.RecommendationContext) error { return nil } -func (s *VolumesRecommender) Recommend(ctx *framework.RecommendationContext) error { +func (vr *VolumesRecommender) Recommend(ctx *framework.RecommendationContext) error { ctx.Recommendation.Status.Action = "Delete" ctx.Recommendation.Status.Description = "It is an Orphan Volumes" return nil } // Policy add some logic for result of recommend phase. -func (s *VolumesRecommender) Policy(ctx *framework.RecommendationContext) error { +func (vr *VolumesRecommender) Policy(ctx *framework.RecommendationContext) error { return nil } diff --git a/pkg/recommendation/recommender/volumes/registry.go b/pkg/recommendation/recommender/volumes/registry.go index be242a8d6..eceefd7a4 100644 --- a/pkg/recommendation/recommender/volumes/registry.go +++ b/pkg/recommendation/recommender/volumes/registry.go @@ -18,7 +18,7 @@ func init() { recommender.RegisterRecommenderProvider(recommender.VolumesRecommender, NewVolumesRecommender) } -func (s *VolumesRecommender) Name() string { +func (vr *VolumesRecommender) Name() string { return recommender.VolumesRecommender } diff --git a/pkg/utils/expression_prom_default.go b/pkg/utils/expression_prom_default.go index e7f30bfec..53ce95c21 100644 --- a/pkg/utils/expression_prom_default.go +++ b/pkg/utils/expression_prom_default.go @@ -37,16 +37,24 @@ const ( ContainerMemUsageExprTemplate = `container_memory_working_set_bytes{container!="POD",namespace="%s",pod=~"%s",container="%s"}` CustomerExprTemplate = `sum(%s{%s})` + + // Container network cumulative count of bytes received + queryFmtNetReceiveBytes = `sum(increase(container_network_receive_bytes_total{pod=~"%s"}[1h])) by (namespace)` + // Container network cumulative count of bytes transmitted + queryFmtNetTransferBytes = `sum(increase(container_network_transmit_bytes_total{pod=~"%s"}[1h])) by (namespace)` ) const ( PostRegMatchesPodDeployment = `[a-z0-9]+-[a-z0-9]{5}$` PostRegMatchesPodReplicaset = `[a-z0-9]+$` + PostRegMatchesPodDaemonSet = `[a-z0-9]{5}$` PostRegMatchesPodStatefulset = `[0-9]+$` ) func GetPodNameReg(resourceName string, resourceType string) string { switch resourceType { + case "DaemonSet": + return fmt.Sprintf("^%s-%s", resourceName, PostRegMatchesPodDaemonSet) case "ReplicaSet": return fmt.Sprintf("^%s-%s", resourceName, PostRegMatchesPodReplicaset) case "Deployment": @@ -108,3 +116,11 @@ func GetNodeCpuUsageUtilizationExpression(nodeName string) string { func GetNodeMemUsageUtilizationExpression(nodeName string) string { return fmt.Sprintf(NodeMemUsageUtilizationExprTemplate, nodeName, nodeName) } + +func GetWorkloadNetReceiveBytesExpression(podName string) string { + return fmt.Sprintf(queryFmtNetReceiveBytes, podName) +} + +func GetWorkloadNetTransferBytesExpression(podName string) string { + return fmt.Sprintf(queryFmtNetTransferBytes, podName) +} diff --git a/pkg/utils/pod.go b/pkg/utils/pod.go index 505ec3c9d..c51ae8c17 100644 --- a/pkg/utils/pod.go +++ b/pkg/utils/pod.go @@ -327,7 +327,7 @@ func GetOrphanVolumes(kubeClient client.Client) ([]string, error) { } // Check if each volume is being used by any pods - orphanVolumesName := []string{} + var orphanVolumesName []string for _, volume := range volumes.Items { if isOrphanVolume(&volume, pods) { orphanVolumesName = append(orphanVolumesName, volume.Spec.ClaimRef.Name) @@ -349,6 +349,41 @@ func isOrphanVolume(volume *corev1.PersistentVolume, pods *corev1.PodList) bool return true } +func GetServicePods(kubeClient client.Client, svc *corev1.Service) ([]corev1.Pod, error) { + opts := []client.ListOption{ + client.InNamespace(svc.Namespace), + client.MatchingLabels(svc.Spec.Selector), + } + + podList := &corev1.PodList{} + err := kubeClient.List(context.TODO(), podList, opts...) + if err != nil { + return nil, err + } + + return podList.Items, nil +} + +func GetPodOwnerReference(ctx context.Context, kubeClient client.Client, pod *v1.Pod) *metav1.OwnerReference { + for _, ownerRef := range pod.OwnerReferences { + if ownerRef.Kind == "ReplicaSet" { + // ReplicaSet + var rs appsv1.ReplicaSet + err := kubeClient.Get(ctx, client.ObjectKey{Namespace: pod.Namespace, Name: ownerRef.Name}, &rs) + if err != nil { + return nil + } + for _, ownRef := range rs.OwnerReferences { + return &ownRef + } + } else { + return &ownerRef + } + } + + return nil +} + func IsPodTerminated(pod *corev1.Pod) bool { return pod.Status.Phase == corev1.PodSucceeded || pod.Status.Phase == corev1.PodFailed } diff --git a/pkg/utils/prediction.go b/pkg/utils/prediction.go index 8d84ca456..618342f10 100644 --- a/pkg/utils/prediction.go +++ b/pkg/utils/prediction.go @@ -48,21 +48,15 @@ func QueryPredictedValuesOnce(recommendation *v1alpha1.Recommendation, predictor func GetReadyPredictionMetric(metric string, resourceIdentifier string, prediction *predictionapi.TimeSeriesPrediction) (*predictionapi.MetricTimeSeries, error) { for _, metricStatus := range prediction.Status.PredictionMetrics { if metricStatus.ResourceIdentifier == resourceIdentifier && len(metricStatus.Prediction) == 1 { - var targetMetricStatus *predictionapi.PredictionMetricStatus - targetMetricStatus = &metricStatus - if targetMetricStatus == nil { - return nil, fmt.Errorf("TimeSeries is empty, metric name %s resourceIdentifier %s", metric, resourceIdentifier) - } - - if !targetMetricStatus.Ready { + if !metricStatus.Ready { return nil, fmt.Errorf("TimeSeries is not ready, metric name %s resourceIdentifier %s", metric, resourceIdentifier) } - if len(targetMetricStatus.Prediction) != 1 { - return nil, fmt.Errorf("TimeSeries data length is unexpected: %d, metric name %s resourceIdentifier %s", len(targetMetricStatus.Prediction), metric, resourceIdentifier) + if len(metricStatus.Prediction) != 1 { + return nil, fmt.Errorf("TimeSeries data length is unexpected: %d, metric name %s resourceIdentifier %s", len(metricStatus.Prediction), metric, resourceIdentifier) } - return targetMetricStatus.Prediction[0], nil + return metricStatus.Prediction[0], nil } }