Skip to content

Commit

Permalink
fix: change datadog api library to the official one
Browse files Browse the repository at this point in the history
  • Loading branch information
mmolnar committed Jul 1, 2024
1 parent 947f566 commit 77c7b08
Show file tree
Hide file tree
Showing 9 changed files with 157 additions and 112 deletions.
36 changes: 20 additions & 16 deletions api/metrics/datadog/datadog.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,46 +6,50 @@ import (
"encoding/hex"
"encoding/json"
"fmt"
_nethttp "net/http"
"sync"
"time"

"github.com/zorkian/go-datadog-api"
"github.com/DataDog/datadog-api-client-go/v2/api/datadog"
"github.com/DataDog/datadog-api-client-go/v2/api/datadogV1"

"statusbay/api/httpresponse"
"statusbay/cache"

log "github.com/sirupsen/logrus"
)

// ClientDescriber is a interface for using function in DataDog package
type ClientDescriber interface {
QueryMetrics(from int64, to int64, query string) ([]datadog.Series, error)
type MetricsApiDescriber interface {
QueryMetrics(ctx context.Context, from int64, to int64, query string) (datadogV1.MetricsQueryResponse, *_nethttp.Response, error)
}

// Datadog is responsible for communicate with datadog and cache storage save/cleanup
type Datadog struct {
client ClientDescriber
apiClient *datadog.APIClient
metricsApi MetricsApiDescriber
cache *cache.CacheManager
cacheExpiration time.Duration
mu *sync.RWMutex
logger *log.Entry
}

// NewDatadogManager creates a new NewDatadog
func NewDatadogManager(cache *cache.CacheManager, cacheExpiration time.Duration, apiKey, appKey string, client ClientDescriber) *Datadog {
func NewDatadogManager(cache *cache.CacheManager, cacheExpiration time.Duration, apiKey, appKey string) *Datadog {
log.Info("initializing Datadog client")
configuration := datadog.NewConfiguration()
configuration.AddDefaultHeader("DD-API-KEY", apiKey)
configuration.AddDefaultHeader("DD-APPLICATION-KEY", appKey)
apiClient := datadog.NewAPIClient(configuration)
metricsApi := datadogV1.NewMetricsApi(apiClient)

if client == nil {
log.Info("initializing Datadog client")
client = datadog.NewClient(apiKey, appKey)
}
return &Datadog{
client: client,
apiClient: apiClient,
metricsApi: metricsApi,
cache: cache,
cacheExpiration: cacheExpiration,
mu: &sync.RWMutex{},
logger: log.WithField("metric_engine", "datadog"),
}

}

// Serve will start listening metric request
Expand Down Expand Up @@ -86,7 +90,7 @@ func (dd *Datadog) GetMetric(query string, from, to time.Time) ([]httpresponse.M
}).Error("cache Unmarshal failed")
}

metrics, err := dd.client.QueryMetrics(from.Unix(), to.Unix(), query)
metrics, _, err := dd.metricsApi.QueryMetrics(context.Background(), from.Unix(), to.Unix(), query)

if err != nil {
dd.logger.WithError(err).WithFields(log.Fields{
Expand All @@ -98,11 +102,11 @@ func (dd *Datadog) GetMetric(query string, from, to time.Time) ([]httpresponse.M
}

var response []httpresponse.MetricsQuery
for _, metric := range metrics {
for _, metric := range metrics.Series {
metricData := httpresponse.MetricsQuery{}
metricData.Metric = metric.GetDisplayName()
metricData.Metric = *metric.DisplayName
var points []httpresponse.DataPoint
for _, point := range metric.Points {
for _, point := range metric.Pointlist {
points = append(points, [2]float64{*point[0], *point[1]})
}
metricData.Points = points
Expand Down
7 changes: 4 additions & 3 deletions api/metrics/datadog/datadog_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,21 @@ package datadog

import (
"context"
testutil "statusbay/api/metrics/datadog/testutils"
testutils "statusbay/api/metrics/datadog/testutils"
"statusbay/cache"
"sync"
"testing"
"time"
)

func MockDatadog(cacheExpiration, cacheCleanupInterval time.Duration) *Datadog {
ddMockClient := testutil.NewMockDatadog()
metricsApi := testutils.NewMockMockMetricsApi()
cache := &cache.CacheManager{
Client: &cache.NoOpCache{},
}

dd := NewDatadogManager(cache, cacheExpiration, "", "", ddMockClient)
dd := NewDatadogManager(cache, cacheExpiration, "", "")
dd.metricsApi = metricsApi

return dd
}
Expand Down
71 changes: 52 additions & 19 deletions api/metrics/datadog/testutils/client.go
Original file line number Diff line number Diff line change
@@ -1,39 +1,72 @@
package testutil

import (
"context"
"encoding/json"
"fmt"
"io/ioutil"
_nethttp "net/http"
"os"
"path/filepath"
"runtime"
"strings"

"github.com/zorkian/go-datadog-api"
"github.com/DataDog/datadog-api-client-go/v2/api/datadogV1"
)

func MockQueryMetrics(from int64, to int64, query string) ([]datadog.Series, error) {
type MockMetricsApi struct {
}

func NewMockMockMetricsApi() *MockMetricsApi {
return &MockMetricsApi{}
}

func (m *MockMetricsApi) QueryMetrics(ctx context.Context, from int64, to int64, query string) (datadogV1.MetricsQueryResponse, *_nethttp.Response, error) {
var (
resp datadogV1.MetricsQueryResponse
httpResp *_nethttp.Response
)
_, filename, _, _ := runtime.Caller(0)
currentFolderPath := filepath.Dir(filename)
reader, err := ioutil.ReadFile(fmt.Sprintf("%s/mock/%s.json", currentFolderPath, query))

// Read directory contents
mockQueryDir := filepath.Join(filepath.Dir(filename), "mock")
files, err := os.ReadDir(mockQueryDir)
if err != nil {
return nil, err
return resp, httpResp, err
}
var data []datadog.Series
err = json.Unmarshal(reader, &data)
if err != nil {
return nil, err

// Create a list of accessible queries
mockQueryList := make(map[string]string, len(files))
for _, file := range files {
if !file.IsDir() {
mockQueryName := strings.TrimSuffix(file.Name(), filepath.Ext(file.Name()))
mockQueryFilePath := filepath.Join(mockQueryDir, file.Name())
mockQueryList[mockQueryName] = mockQueryFilePath
}
}
return data, nil
}

type MockDatadog struct {
}
// Check if the query parameter is among the accessible queries
mockQueryFilePath, found := mockQueryList[query]
if !found {
return resp, httpResp, fmt.Errorf("Mock file not found for query: %s", query)
}

func NewMockDatadog() *MockDatadog {
return &MockDatadog{}
}
// Rest of the code...

func (m *MockDatadog) QueryMetrics(from int64, to int64, query string) ([]datadog.Series, error) {
return MockQueryMetrics(from, to, query)
reader, err := os.ReadFile(mockQueryFilePath)
if err != nil {
return resp, httpResp, err
}

err = json.Unmarshal(reader, &resp)
if err != nil {
fmt.Printf("Error reading mock response:\n %s\n", err)
return resp, httpResp, err
}

err = json.Unmarshal(reader, &resp)
if err != nil {
fmt.Printf("Error reading mock response:\n %s\n", err)
return resp, httpResp, err
}
return resp, httpResp, nil
}
74 changes: 38 additions & 36 deletions api/metrics/datadog/testutils/mock/multiple-metric.json
Original file line number Diff line number Diff line change
@@ -1,38 +1,40 @@
[
{
"metric": "foo.metric.response.4xx",
"display_name": "foo.metric.response.4xx",
"pointlist": [
[
1557941880000,
1
{
"series": [
{
"metric": "foo.metric.response.4xx",
"display_name": "foo.metric.response.4xx",
"pointlist": [
[
1557941880000,
1
],
[
1557941910000,
2
],
[
1557942010000,
2
]
],
[
1557941910000,
2
"scope": "service:foo-production",
"expression": "sum:sw.foo.metric.response.4xx{service:foo-production}"
},
{
"metric": "foo.metric.response.5xx",
"display_name": "foo.metric.response.5xx",
"pointlist": [
[
1557941880000,
1
],
[
1557941910000,
2
]
],
[
1557942010000,
2
]
],
"scope": "service:foo-production",
"expression": "sum:sw.foo.metric.response.4xx{service:foo-production}"
},
{
"metric": "foo.metric.response.5xx",
"display_name": "foo.metric.response.5xx",
"pointlist": [
[
1557941880000,
1
],
[
1557941910000,
2
]
],
"scope": "service:foo-production",
"expression": "sum:sw.foo.metric.response.5xx{service:foo-production}"
}
]
"scope": "service:foo-production",
"expression": "sum:sw.foo.metric.response.5xx{service:foo-production}"
}
]
}
36 changes: 19 additions & 17 deletions api/metrics/datadog/testutils/mock/single-metric.json
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
[
{
"metric": "foo.metric.response.2xx",
"display_name": "foo.metric.response.2xx",
"pointlist": [
[
1557941880000,
1
{
"series": [
{
"metric": "foo.metric.response.2xx",
"display_name": "foo.metric.response.2xx",
"pointlist": [
[
1557941880000,
1
],
[
1557941910000,
2
]
],
[
1557941910000,
2
]
],
"scope": "service:foo-production",
"expression": "sum:sw.foo.metric.response.2xx{service:foo-production}"
}
]
"scope": "service:foo-production",
"expression": "sum:sw.foo.metric.response.2xx{service:foo-production}"
}
]
}
2 changes: 1 addition & 1 deletion api/metrics/structs.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ func Load(metricsProviders *config.MetricsProvider, cache *cache.CacheManager) m
return providers
}
if metricsProviders.DataDog != nil {
providers["datadog"] = datadog.NewDatadogManager(cache, metricsProviders.DataDog.CacheExpiration, metricsProviders.DataDog.APIKey, metricsProviders.DataDog.AppKey, nil)
providers["datadog"] = datadog.NewDatadogManager(cache, metricsProviders.DataDog.CacheExpiration, metricsProviders.DataDog.APIKey, metricsProviders.DataDog.AppKey)
}

if metricsProviders.Prometheus != nil {
Expand Down
28 changes: 14 additions & 14 deletions config/api.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,39 +11,39 @@ import (

// MetricsProvider struct
type MetricsProvider struct {
DataDog *DatadogConfig `yaml:"datadog"`
Prometheus *PrometheusConfig `yaml:"prometheus"`
DataDog *DatadogConfig `yaml:"datadog" env:", prefix=DATADOG_"`
Prometheus *PrometheusConfig `yaml:"prometheus" env:", prefix=PROMETHEUS_"`
}

// AlertProvider struct
type AlertProvider struct {
Statuscake *Statuscake `yaml:"statuscake"`
Pingdom *Pingdom `yaml:"pingdom"`
Statuscake *Statuscake `yaml:"statuscake" env:", prefix=STATUSCAKE_"`
Pingdom *Pingdom `yaml:"pingdom" env:", prefix=PINGDOM_"`
}

// DatadogConfig configuration
type DatadogConfig struct {
APIKey string `yaml:"api_key"`
AppKey string `yaml:"app_key"`
APIKey string `yaml:"api_key" env:"API_KEY, overwrite"`
AppKey string `yaml:"app_key" env:"APP_KEY, overwrite"`
CacheExpiration time.Duration `yaml:"cache_expiration"`
}

// PrometheusConfig configuration
type PrometheusConfig struct {
Address string `yaml:"address"`
Address string `yaml:"address" env:"ADDRESS, overwrite"`
}

// Pingdom configuration
type Pingdom struct {
Endpoint string `yaml:"endpoint"`
Token string `yaml:"token"`
Endpoint string `yaml:"endpoint" env:"ENDPOINT, overwrite"`
Token string `yaml:"token" env:"TOKEN, overwrite"`
}

// Statuscake configuration
type Statuscake struct {
Endpoint string `yaml:"endpoint"`
Username string `yaml:"username"`
APIKey string `yaml:"api_key"`
Endpoint string `yaml:"endpoint" env:"ENDPOINT, overwrite"`
Username string `yaml:"username" env:"USERNAME, overwrite"`
APIKey string `yaml:"api_key" env:"API_KEY, overwrite"`
}

// KubernetesMarksEvents is the struct representing the events StatusBay marks
Expand All @@ -62,8 +62,8 @@ type API struct {
Log LogConfig `yaml:"log"`
MySQL *state.MySQLConfig `yaml:"mysql" env:", prefix=MYSQL_"`
Redis *cache.RedisConfig `yaml:"redis"`
MetricsProvider *MetricsProvider `yaml:"metrics"`
AlertProvider *AlertProvider `yaml:"alerts"`
MetricsProvider *MetricsProvider `yaml:"metrics" env:", prefix=METRICS_"`
AlertProvider *AlertProvider `yaml:"alerts" env:", prefix=ALERTS_"`
Telemetry MetricsConfig `yaml:"telemetry"`
}

Expand Down
Loading

0 comments on commit 77c7b08

Please sign in to comment.