Skip to content

Commit

Permalink
feat: update Dynatrace provider to query metrics over a range (#1658)
Browse files Browse the repository at this point in the history
Signed-off-by: Rakshit Gondwal <rakshitgondwal3@gmail.com>
Signed-off-by: Rakshit Gondwal <98955085+rakshitgondwal@users.noreply.github.com>
Co-authored-by: Florian Bacher <florian.bacher@dynatrace.com>
  • Loading branch information
rakshitgondwal and bacherfl committed Jul 18, 2023
1 parent ea73cd9 commit 0f0cddb
Show file tree
Hide file tree
Showing 2 changed files with 115 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,12 @@ type DynatraceData struct {
func (d *KeptnDynatraceProvider) EvaluateQuery(ctx context.Context, metric metricsapi.KeptnMetric, provider metricsapi.KeptnMetricsProvider) (string, []byte, error) {
baseURL := d.normalizeAPIURL(provider.Spec.TargetServer)
query := url.QueryEscape(metric.Spec.Query)
qURL := baseURL + "v2/metrics/query?metricSelector=" + query
var qURL string
if metric.Spec.Range != nil {
qURL = baseURL + "v2/metrics/query?metricSelector=" + query + "&from=now-" + metric.Spec.Range.Interval
} else {
qURL = baseURL + "v2/metrics/query?metricSelector=" + query
}

d.Log.Info("Running query: " + qURL)
ctx, cancel := context.WithTimeout(ctx, 20*time.Second)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,7 @@ func TestEvaluateQuery_CorrectHTTP(t *testing.T) {
require.Equal(t, 1, len(r.Header["Authorization"]))
}))
defer svr.Close()
kdp, obj := setupTest()
kdp, objects := setupTest()
p := metricsapi.KeptnMetricsProvider{
Spec: metricsapi.KeptnMetricsProviderSpec{
SecretKeyRef: v1.SecretKeySelector{
Expand All @@ -164,11 +164,12 @@ func TestEvaluateQuery_CorrectHTTP(t *testing.T) {
TargetServer: svr.URL,
},
}
r, raw, e := kdp.EvaluateQuery(context.TODO(), obj, p)
require.True(t, errors.IsNotFound(e))
require.Equal(t, []byte(nil), raw)
require.Equal(t, "", r)

for _, obj := range objects {
r, raw, e := kdp.EvaluateQuery(context.TODO(), obj, p)
require.True(t, errors.IsNotFound(e))
require.Equal(t, []byte(nil), raw)
require.Equal(t, "", r)
}
}

func TestEvaluateQuery_APIError(t *testing.T) {
Expand All @@ -188,7 +189,7 @@ func TestEvaluateQuery_APIError(t *testing.T) {
secretKey: []byte(secretValue),
},
}
kdp, obj := setupTest(apiToken)
kdp, objects := setupTest(apiToken)
p := metricsapi.KeptnMetricsProvider{
Spec: metricsapi.KeptnMetricsProviderSpec{
SecretKeyRef: v1.SecretKeySelector{
Expand All @@ -200,12 +201,14 @@ func TestEvaluateQuery_APIError(t *testing.T) {
TargetServer: svr.URL,
},
}
r, raw, e := kdp.EvaluateQuery(context.TODO(), obj, p)
require.Equal(t, "", r)
t.Log(string(raw))
require.Equal(t, errorResponse, raw) //we still return the raw answer to help user debug
require.NotNil(t, e)
require.Contains(t, e.Error(), "Token is missing required scope.")
for _, obj := range objects {
r, raw, e := kdp.EvaluateQuery(context.TODO(), obj, p)
require.Equal(t, "", r)
t.Log(string(raw))
require.Equal(t, errorResponse, raw) //we still return the raw answer to help user debug
require.NotNil(t, e)
require.Contains(t, e.Error(), "Token is missing required scope.")
}
}

func TestEvaluateQuery_WrongPayloadHandling(t *testing.T) {
Expand All @@ -225,7 +228,7 @@ func TestEvaluateQuery_WrongPayloadHandling(t *testing.T) {
},
}

kdp, obj := setupTest(apiToken)
kdp, objects := setupTest(apiToken)
p := metricsapi.KeptnMetricsProvider{
Spec: metricsapi.KeptnMetricsProviderSpec{
SecretKeyRef: v1.SecretKeySelector{
Expand All @@ -237,11 +240,13 @@ func TestEvaluateQuery_WrongPayloadHandling(t *testing.T) {
TargetServer: svr.URL,
},
}
r, raw, e := kdp.EvaluateQuery(context.TODO(), obj, p)
require.Equal(t, "", r)
t.Log(string(raw), e)
require.Equal(t, []byte("garbage"), raw) //we still return the raw answer to help user debug
require.NotNil(t, e)
for _, obj := range objects {
r, raw, e := kdp.EvaluateQuery(context.TODO(), obj, p)
require.Equal(t, "", r)
t.Log(string(raw), e)
require.Equal(t, []byte("garbage"), raw) //we still return the raw answer to help user debug
require.NotNil(t, e)
}
}

func TestEvaluateQuery_MissingSecret(t *testing.T) {
Expand All @@ -250,16 +255,18 @@ func TestEvaluateQuery_MissingSecret(t *testing.T) {
require.Nil(t, err)
}))
defer svr.Close()
kdp, obj := setupTest()
kdp, objects := setupTest()

p := metricsapi.KeptnMetricsProvider{
Spec: metricsapi.KeptnMetricsProviderSpec{
TargetServer: svr.URL,
},
}
_, _, e := kdp.EvaluateQuery(context.TODO(), obj, p)
require.NotNil(t, e)
require.ErrorIs(t, e, ErrSecretKeyRefNotDefined)
for _, obj := range objects {
_, _, e := kdp.EvaluateQuery(context.TODO(), obj, p)
require.NotNil(t, e)
require.ErrorIs(t, e, ErrSecretKeyRefNotDefined)
}
}

func TestEvaluateQuery_SecretNotFound(t *testing.T) {
Expand All @@ -268,7 +275,7 @@ func TestEvaluateQuery_SecretNotFound(t *testing.T) {
require.Nil(t, err)
}))
defer svr.Close()
kdp, obj := setupTest()
kdp, objects := setupTest()

p := metricsapi.KeptnMetricsProvider{
Spec: metricsapi.KeptnMetricsProviderSpec{
Expand All @@ -281,9 +288,11 @@ func TestEvaluateQuery_SecretNotFound(t *testing.T) {
TargetServer: svr.URL,
},
}
_, _, e := kdp.EvaluateQuery(context.TODO(), obj, p)
require.NotNil(t, e)
require.True(t, errors.IsNotFound(e))
for _, obj := range objects {
_, _, e := kdp.EvaluateQuery(context.TODO(), obj, p)
require.NotNil(t, e)
require.True(t, errors.IsNotFound(e))
}
}

func TestEvaluateQuery_RefNotExistingKey(t *testing.T) {
Expand All @@ -302,7 +311,7 @@ func TestEvaluateQuery_RefNotExistingKey(t *testing.T) {
secretKey: []byte(secretValue),
},
}
kdp, obj := setupTest(apiToken)
kdp, objects := setupTest(apiToken)

missingKey := "key_not_found"
p := metricsapi.KeptnMetricsProvider{
Expand All @@ -316,10 +325,11 @@ func TestEvaluateQuery_RefNotExistingKey(t *testing.T) {
TargetServer: svr.URL,
},
}

_, _, e := kdp.EvaluateQuery(context.TODO(), obj, p)
require.NotNil(t, e)
require.True(t, strings.Contains(e.Error(), "invalid key "+missingKey))
for _, obj := range objects {
_, _, e := kdp.EvaluateQuery(context.TODO(), obj, p)
require.NotNil(t, e)
require.True(t, strings.Contains(e.Error(), "invalid key "+missingKey))
}
}

func TestEvaluateQuery_HappyPath(t *testing.T) {
Expand All @@ -338,7 +348,46 @@ func TestEvaluateQuery_HappyPath(t *testing.T) {
secretKey: []byte(secretValue),
},
}
kdp, obj := setupTest(apiToken)
kdp, objects := setupTest(apiToken)

p := metricsapi.KeptnMetricsProvider{
Spec: metricsapi.KeptnMetricsProviderSpec{
SecretKeyRef: v1.SecretKeySelector{
LocalObjectReference: v1.LocalObjectReference{
Name: secretName,
},
Key: secretKey,
},
TargetServer: svr.URL,
},
}
for _, obj := range objects {
r, raw, e := kdp.EvaluateQuery(context.TODO(), obj, p)
require.Nil(t, e)
require.Equal(t, []byte(dtpayload), raw)
require.Equal(t, fmt.Sprintf("%f", 50.0), r)
}
}

func TestEvaluateQuery_HappyPathForTimerange(t *testing.T) {
svr := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
_, err := w.Write([]byte(dtpayload))
p := r.URL.Query().Get("from")
require.NotNil(t, p)
require.Nil(t, err)
}))
defer svr.Close()
secretName, secretKey, secretValue := "secretName", "secretKey", "secretValue"
apiToken := &corev1.Secret{
ObjectMeta: metav1.ObjectMeta{
Name: secretName,
Namespace: "",
},
Data: map[string][]byte{
secretKey: []byte(secretValue),
},
}
kdp, obj := setupTestForTimerange(apiToken)

p := metricsapi.KeptnMetricsProvider{
Spec: metricsapi.KeptnMetricsProviderSpec{
Expand All @@ -357,7 +406,32 @@ func TestEvaluateQuery_HappyPath(t *testing.T) {
require.Equal(t, fmt.Sprintf("%f", 50.0), r)
}

func setupTest(objs ...client.Object) (KeptnDynatraceProvider, metricsapi.KeptnMetric) {
func setupTest(objs ...client.Object) (KeptnDynatraceProvider, []metricsapi.KeptnMetric) {

fakeClient := fake.NewClient(objs...)

kdp := KeptnDynatraceProvider{
HttpClient: http.Client{},
Log: ctrl.Log.WithName("testytest"),
K8sClient: fakeClient,
}
objects := []metricsapi.KeptnMetric{
{
Spec: metricsapi.KeptnMetricSpec{
Query: "my-query",
},
},
{
Spec: metricsapi.KeptnMetricSpec{
Query: "my-query",
Range: &metricsapi.RangeSpec{Interval: "5m"},
},
},
}
return kdp, objects
}

func setupTestForTimerange(objs ...client.Object) (KeptnDynatraceProvider, metricsapi.KeptnMetric) {

fakeClient := fake.NewClient(objs...)

Expand All @@ -369,6 +443,7 @@ func setupTest(objs ...client.Object) (KeptnDynatraceProvider, metricsapi.KeptnM
obj := metricsapi.KeptnMetric{
Spec: metricsapi.KeptnMetricSpec{
Query: "my-query",
Range: &metricsapi.RangeSpec{Interval: "5m"},
},
}
return kdp, obj
Expand Down

0 comments on commit 0f0cddb

Please sign in to comment.