Skip to content

Commit

Permalink
feat(metrics-operator): add new provider interface (#1943)
Browse files Browse the repository at this point in the history
Signed-off-by: realanna <anna.reale@dynatrace.com>
Signed-off-by: odubajDT <ondrej.dubaj@dynatrace.com>
Signed-off-by: RealAnna <89971034+RealAnna@users.noreply.github.com>
Co-authored-by: odubajDT <ondrej.dubaj@dynatrace.com>
Co-authored-by: Florian Bacher <florian.bacher@dynatrace.com>
  • Loading branch information
3 people authored Aug 24, 2023
1 parent 6f2d261 commit 66320f8
Show file tree
Hide file tree
Showing 7 changed files with 121 additions and 1 deletion.
7 changes: 7 additions & 0 deletions metrics-operator/controllers/common/providers/common.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,10 @@ const DynatraceProviderType = "dynatrace"
const DynatraceDQLProviderType = "dql"
const PrometheusProviderType = "prometheus"
const DataDogProviderType = "datadog"

var SupportedProviders = []string{
DynatraceProviderType,
DynatraceDQLProviderType,
PrometheusProviderType,
DataDogProviderType,
}
12 changes: 11 additions & 1 deletion metrics-operator/controllers/common/providers/datadog/datadog.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ type KeptnDataDogProvider struct {
K8sClient client.Client
}

func (d *KeptnDataDogProvider) FetchAnalysisValue(ctx context.Context, query string, spec metricsapi.AnalysisSpec, provider *metricsapi.KeptnMetricsProvider) (string, []byte, error) {
ctx, cancel := context.WithTimeout(ctx, 20*time.Second)
defer cancel()
return d.query(ctx, query, *provider, spec.From.Unix(), spec.To.Unix())
}

// EvaluateQuery fetches the SLI values from datadog provider
func (d *KeptnDataDogProvider) EvaluateQuery(ctx context.Context, metric metricsapi.KeptnMetric, provider metricsapi.KeptnMetricsProvider) (string, []byte, error) {
ctx, cancel := context.WithTimeout(ctx, 20*time.Second)
Expand All @@ -35,7 +41,11 @@ func (d *KeptnDataDogProvider) EvaluateQuery(ctx context.Context, metric metrics
if err != nil {
return "", nil, err
}
qURL := provider.Spec.TargetServer + "/api/v1/query?from=" + strconv.Itoa(int(fromTime)) + "&to=" + strconv.Itoa(int(toTime)) + "&query=" + url.QueryEscape(metric.Spec.Query)
return d.query(ctx, metric.Spec.Query, provider, fromTime, toTime)
}

func (d *KeptnDataDogProvider) query(ctx context.Context, query string, provider metricsapi.KeptnMetricsProvider, fromTime int64, toTime int64) (string, []byte, error) {
qURL := provider.Spec.TargetServer + "/api/v1/query?from=" + strconv.Itoa(int(fromTime)) + "&to=" + strconv.Itoa(int(toTime)) + "&query=" + url.QueryEscape(query)
apiKeyVal, appKeyVal, err := getDDSecret(ctx, provider, d.K8sClient)
if err != nil {
return "", nil, err
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,13 @@ type DynatraceData struct {
Values []*float64 `json:"values"`
}

func (d *KeptnDynatraceProvider) FetchAnalysisValue(ctx context.Context, query string, spec metricsapi.AnalysisSpec, provider *metricsapi.KeptnMetricsProvider) (string, []byte, error) {
baseURL := d.normalizeAPIURL(provider.Spec.TargetServer)
escapedQ := urlEncodeQuery(query)
qURL := baseURL + "v2/metrics/query?metricSelector=" + escapedQ + "&from=" + spec.From.String() + "&to=" + spec.To.String()
return d.runQuery(ctx, qURL, *provider)
}

// EvaluateQuery fetches the SLI values from dynatrace provider
func (d *KeptnDynatraceProvider) EvaluateQuery(ctx context.Context, metric metricsapi.KeptnMetric, provider metricsapi.KeptnMetricsProvider) (string, []byte, error) {
baseURL := d.normalizeAPIURL(provider.Spec.TargetServer)
Expand All @@ -52,6 +59,10 @@ func (d *KeptnDynatraceProvider) EvaluateQuery(ctx context.Context, metric metri
qURL = urlEncodeQuery(qURL)
qURL = baseURL + "v2/metrics/query?" + qURL

return d.runQuery(ctx, qURL, provider)
}

func (d *KeptnDynatraceProvider) runQuery(ctx context.Context, qURL string, provider metricsapi.KeptnMetricsProvider) (string, []byte, error) {
d.Log.Info("Running query: " + qURL)
ctx, cancel := context.WithTimeout(ctx, 20*time.Second)
defer cancel()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,11 @@ type keptnDynatraceDQLProvider struct {
clock clock.Clock
}

func (d *keptnDynatraceDQLProvider) FetchAnalysisValue(ctx context.Context, query string, spec metricsapi.AnalysisSpec, provider *metricsapi.KeptnMetricsProvider) (string, []byte, error) {
//TODO implement me
panic("implement me")
}

type DynatraceDQLHandler struct {
RequestToken string `json:"requestToken"`
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,40 @@ type KeptnPrometheusProvider struct {
HttpClient http.Client
}

func (r *KeptnPrometheusProvider) FetchAnalysisValue(ctx context.Context, query string, spec metricsapi.AnalysisSpec, provider *metricsapi.KeptnMetricsProvider) (string, []byte, error) {
ctx, cancel := context.WithTimeout(ctx, 20*time.Second)
defer cancel()

client, err := promapi.NewClient(promapi.Config{Address: provider.Spec.TargetServer, Client: &r.HttpClient})
if err != nil {
return "", nil, err
}
api := prometheus.NewAPI(client)
r.Log.Info(fmt.Sprintf(
"Running query: /api/v1/query_range?query=%s&start=%d&end=%d",
query,
spec.From.Unix(), spec.To.Unix(),
))
queryRange := prometheus.Range{
Start: spec.From.Time,
End: spec.To.Time,
}
result, warnings, err := api.QueryRange(
ctx,
query,
queryRange,
[]prometheus.Option{}...,
)

if err != nil {
return "", nil, err
}
if len(warnings) != 0 {
r.Log.Info("Prometheus API returned warnings: " + warnings[0])
}
return getResultForMatrix(result)
}

// EvaluateQuery fetches the SLI values from prometheus provider
func (r *KeptnPrometheusProvider) EvaluateQuery(ctx context.Context, metric metricsapi.KeptnMetric, provider metricsapi.KeptnMetricsProvider) (string, []byte, error) {
ctx, cancel := context.WithTimeout(ctx, 20*time.Second)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@ import (
"net/http"
"net/http/httptest"
"testing"
"time"

metricsapi "github.com/keptn/lifecycle-toolkit/metrics-operator/api/v1alpha3"
"github.com/prometheus/common/model"
"github.com/stretchr/testify/require"
v1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
ctrl "sigs.k8s.io/controller-runtime"
)

Expand Down Expand Up @@ -314,3 +316,53 @@ func Test_resultsForMatrix(t *testing.T) {
})
}
}

func TestFetchAnalysisValue(t *testing.T) {

svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, err := w.Write([]byte(promPayloadWithRangeAndStep))
require.Nil(t, err)
}))
defer svr.Close()

// Create a mock KeptnMetricsProvider
mockProvider := &metricsapi.KeptnMetricsProvider{
Spec: metricsapi.KeptnMetricsProviderSpec{
SecretKeyRef: v1.SecretKeySelector{
LocalObjectReference: v1.LocalObjectReference{
Name: "myapitoken",
},
Key: "mykey",
},
TargetServer: svr.URL,
},
}

// Create your KeptnPrometheusProvider instance
provider := KeptnPrometheusProvider{
HttpClient: http.Client{},
Log: ctrl.Log.WithName("testytest"),
}

// Prepare the analysis spec
now := time.Now()
analysisSpec := metricsapi.AnalysisSpec{
Timeframe: metricsapi.Timeframe{
From: metav1.Time{
Time: now.Add(-time.Hour),
},
To: metav1.Time{
Time: now,
}},
}

// Prepare the expected result
expectedResult := "1"

// Call the function
result, _, err := provider.FetchAnalysisValue(context.Background(), "your_query_string_here", analysisSpec, mockProvider)

// Assertions
require.NoError(t, err)
require.Equal(t, expectedResult, result)
}
1 change: 1 addition & 0 deletions metrics-operator/controllers/common/providers/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
// KeptnSLIProvider is the interface that describes the operations that an SLI provider must implement
type KeptnSLIProvider interface {
EvaluateQuery(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, []byte, error)
}

// NewProvider is a factory method that chooses the right implementation of KeptnSLIProvider
Expand Down

0 comments on commit 66320f8

Please sign in to comment.