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(metrics-operator): add new provider interface #1943

Merged
merged 9 commits into from
Aug 24, 2023
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,
}
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)
}
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