Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: aggregation functions support for metrics controller #1802

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
ba83818
feat: aggregation functions support for metrics controller
rakshitgondwal Jul 28, 2023
efe6a7f
Merge branch 'main' into feat/aggregation-func-for-metrics-controller
rakshitgondwal Aug 3, 2023
001c533
fix errors
rakshitgondwal Aug 3, 2023
9a460ac
Merge branch 'main' into feat/aggregation-func-for-metrics-controller
rakshitgondwal Aug 9, 2023
52f8010
update aggFuncs to not use stats pkg
rakshitgondwal Aug 9, 2023
a743790
Merge branch 'main' into feat/aggregation-func-for-metrics-controller
rakshitgondwal Aug 13, 2023
728860f
add EvaluateQueryForStep in KeptnSLIProvider interface
rakshitgondwal Aug 22, 2023
18e3164
Merge branch 'main' into feat/aggregation-func-for-metrics-controller
rakshitgondwal Aug 22, 2023
89b2f00
Merge branch 'main' into feat/aggregation-func-for-metrics-controller
rakshitgondwal Aug 24, 2023
a101015
refractor code
rakshitgondwal Aug 24, 2023
c87ace3
add test for aggregateValues
rakshitgondwal Aug 24, 2023
5a715e9
Merge branch 'main' into feat/aggregation-func-for-metrics-controller
rakshitgondwal Aug 24, 2023
f16cd3c
add more tests for aggregateValues
rakshitgondwal Aug 24, 2023
16f6588
fix after review
rakshitgondwal Aug 25, 2023
a1401a0
fix lint errors
rakshitgondwal Aug 25, 2023
e833eac
Merge branch 'main' into feat/aggregation-func-for-metrics-controller
rakshitgondwal Aug 25, 2023
157be77
Merge branch 'main' into feat/aggregation-func-for-metrics-controller
rakshitgondwal Sep 5, 2023
30ee210
fix errors
rakshitgondwal Sep 5, 2023
028d998
add EvaluateQueryForStep to KeptnSLIProviderMock
rakshitgondwal Sep 5, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 75 additions & 0 deletions metrics-operator/controllers/common/aggregation/aggregation.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
package aggregation

import (
"math"
"sort"
)

func CalculateMax(values []float64) float64 {
if len(values) == 0 {
return 0.0
}

max := values[0]
for _, value := range values {
if value > max {
max = value
}
}
return max
}

func CalculateMin(values []float64) float64 {
if len(values) == 0 {
return 0.0
}

min := values[0]
for _, value := range values {
if value < min {
min = value
}
}
return min
}

func CalculateMedian(values []float64) float64 {
if len(values) == 0 {
return 0.0
}

// Sort the values
sortedValues := make([]float64, len(values))
copy(sortedValues, values)
sort.Float64s(sortedValues)

// Calculate the median
middle := len(sortedValues) / 2
if len(sortedValues)%2 == 0 {
return (sortedValues[middle-1] + sortedValues[middle]) / 2
}
return sortedValues[middle]
}

func CalculateAverage(values []float64) float64 {
sum := 0.0

for _, value := range values {
sum += value
}
if len(values) > 0 {
return sum / float64(len(values))
}

return 0.0
}

func CalculatePercentile(values sort.Float64Slice, perc float64) float64 {
if len(values) == 0 {
return 0.0
}
// Calculate the index for the requested percentile
i := math.Ceil(float64(len(values)) * perc / 100)

return values[int(i-1)]
}

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import (
//go:generate moq -pkg fake -skip-ensure -out ./fake/provider_mock.go . KeptnSLIProvider
type KeptnSLIProvider interface {
EvaluateQuery(ctx context.Context, metric metricsapi.KeptnMetric, provider metricsapi.KeptnMetricsProvider) (string, []byte, error)
EvaluateQueryForStep(ctx context.Context, metric metricsapi.KeptnMetric, provider metricsapi.KeptnMetricsProvider) ([]string, []byte, error)
FetchAnalysisValue(ctx context.Context, query string, spec metricsapi.AnalysisSpec, provider *metricsapi.KeptnMetricsProvider) (string, error)
}

Expand Down
74 changes: 72 additions & 2 deletions metrics-operator/controllers/metrics/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,14 @@ package metrics

import (
"context"
"fmt"
"sort"
"strconv"
"time"

"github.com/go-logr/logr"
metricsapi "github.com/keptn/lifecycle-toolkit/metrics-operator/api/v1alpha3"
"github.com/keptn/lifecycle-toolkit/metrics-operator/controllers/common/aggregation"
"github.com/keptn/lifecycle-toolkit/metrics-operator/controllers/common/providers"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
Expand Down Expand Up @@ -94,9 +98,9 @@ func (r *KeptnMetricReconciler) Reconcile(ctx context.Context, req ctrl.Request)
r.Log.Error(err2, "Failed to get the correct Metric Provider")
return ctrl.Result{Requeue: false}, err2
}

reconcile := ctrl.Result{Requeue: true, RequeueAfter: 10 * time.Second}
value, rawValue, err := provider.EvaluateQuery(ctx, *metric, *metricProvider)

value, rawValue, err := r.getResults(ctx, metric, provider, metricProvider)
if err != nil {
r.Log.Error(err, "Failed to evaluate the query", "Response from provider was:", (string)(rawValue))
metric.Status.ErrMsg = err.Error()
Expand All @@ -118,6 +122,33 @@ func (r *KeptnMetricReconciler) Reconcile(ctx context.Context, req ctrl.Request)
return reconcile, err
}

func (r *KeptnMetricReconciler) getResults(ctx context.Context, metric *metricsapi.KeptnMetric, provider providers.KeptnSLIProvider, metricProvider *metricsapi.KeptnMetricsProvider) (string, []byte, error) {
if metric.Spec.Range != nil && metric.Spec.Range.Step != "" {
return r.getStepQueryResults(ctx, metric, provider, metricProvider)
}
return r.getQueryResults(ctx, metric, provider, metricProvider)
}
func (r *KeptnMetricReconciler) getQueryResults(ctx context.Context, metric *metricsapi.KeptnMetric, provider providers.KeptnSLIProvider, metricProvider *metricsapi.KeptnMetricsProvider) (string, []byte, error) {
value, rawValue, err := provider.EvaluateQuery(ctx, *metric, *metricProvider)
if err != nil {
r.Log.Error(err, "Failed to evaluate the query", "Response from provider was:", (string)(rawValue))
return "", cupSize(rawValue), err
}
return value, cupSize(rawValue), nil
}
func (r *KeptnMetricReconciler) getStepQueryResults(ctx context.Context, metric *metricsapi.KeptnMetric, provider providers.KeptnSLIProvider, metricProvider *metricsapi.KeptnMetricsProvider) (string, []byte, error) {
value, rawValue, err := provider.EvaluateQueryForStep(ctx, *metric, *metricProvider)
if err != nil {
r.Log.Error(err, "Failed to evaluate the query", "Response from provider was:", (string)(rawValue))
return "", cupSize(rawValue), err
}
aggValue, err := aggregateValues(value, metric.Spec.Range.Aggregation)
if err != nil {
return "", nil, err
}
return aggValue, cupSize(rawValue), nil
}

func cupSize(value []byte) []byte {
if len(value) == 0 {
return []byte{}
Expand All @@ -142,3 +173,42 @@ func (r *KeptnMetricReconciler) fetchProvider(ctx context.Context, namespacedMet
}
return provider, nil
}

rakshitgondwal marked this conversation as resolved.
Show resolved Hide resolved
func aggregateValues(stringSlice []string, aggFunc string) (string, error) {
floatSlice, err := stringSliceToFloatSlice(stringSlice)
if err != nil {
return "", err
}
var aggValue float64
switch aggFunc {
case "max":
aggValue = aggregation.CalculateMax(floatSlice)
case "min":
aggValue = aggregation.CalculateMin(floatSlice)
case "median":
aggValue = aggregation.CalculateMedian(floatSlice)
case "avg":
aggValue = aggregation.CalculateAverage(floatSlice)
case "p90":
aggValue = aggregation.CalculatePercentile(sort.Float64Slice(floatSlice), 90)
case "p95":
aggValue = aggregation.CalculatePercentile(sort.Float64Slice(floatSlice), 95)
case "p99":
aggValue = aggregation.CalculatePercentile(sort.Float64Slice(floatSlice), 99)
}
return fmt.Sprintf("%v", aggValue), nil
}

func stringSliceToFloatSlice(strSlice []string) ([]float64, error) {
floatSlice := make([]float64, len(strSlice))

for i, str := range strSlice {
floatValue, err := strconv.ParseFloat(str, 64)
if err != nil {
return nil, err
}
floatSlice[i] = floatValue
}

return floatSlice, nil
}
Loading
Loading