Skip to content

Commit 1c16822

Browse files
tbavelierlevan-m
andauthored
[CECO-1860][DatadogGenericResource] Add monitor support (#1635)
* refactor based on #1602 * fix finalizer test following refactor * remove dead code following refactor * move id conversion functions to utils.go from notebooks.go * move synthetics status update function to synthetics.go from utils.go (and associated test) * follow-up to 7b2552e: moving away from specific notebook id conversion to generic * nit: fix update error message * add monitor to supported generic resource controller * move id conversion functions to utils.go from notebooks.go * move synthetics status update function to synthetics.go from utils.go (and associated test) * follow-up to 7b2552e: moving away from specific notebook id conversion to generic * nit: fix update error message * lint --------- Co-authored-by: levan-m <116471169+levan-m@users.noreply.github.com>
1 parent 53c0d8c commit 1c16822

File tree

12 files changed

+128
-25
lines changed

12 files changed

+128
-25
lines changed

api/datadoghq/v1alpha1/datadoggenericresource_types.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ type SupportedResourcesType string
1313

1414
// When adding a new type, make sure to update the kubebuilder validation enum marker
1515
const (
16+
Monitor SupportedResourcesType = "monitor"
1617
Notebook SupportedResourcesType = "notebook"
1718
SyntheticsAPITest SupportedResourcesType = "synthetics_api_test"
1819
SyntheticsBrowserTest SupportedResourcesType = "synthetics_browser_test"
@@ -22,7 +23,7 @@ const (
2223
// +k8s:openapi-gen=true
2324
type DatadogGenericResourceSpec struct {
2425
// Type is the type of the API object
25-
// +kubebuilder:validation:Enum=notebook;synthetics_api_test;synthetics_browser_test
26+
// +kubebuilder:validation:Enum=monitor;notebook;synthetics_api_test;synthetics_browser_test
2627
Type SupportedResourcesType `json:"type"`
2728
// JsonSpec is the specification of the API object
2829
JsonSpec string `json:"jsonSpec"`

api/datadoghq/v1alpha1/datadoggenericresource_validation.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,11 @@ import (
1212
)
1313

1414
var allowedCustomResourcesEnumMap = map[SupportedResourcesType]string{
15+
Monitor: "",
1516
Notebook: "",
1617
SyntheticsAPITest: "",
1718
SyntheticsBrowserTest: "",
18-
// mockSubresource is used to mock the subresource in tests
19+
// mock_resource is used to mock the subresource in tests
1920
"mock_resource": "",
2021
}
2122

config/crd/bases/v1/datadoghq.com_datadoggenericresources.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ spec:
5757
type:
5858
description: Type is the type of the API object
5959
enum:
60+
- monitor
6061
- notebook
6162
- synthetics_api_test
6263
- synthetics_browser_test

config/crd/bases/v1/datadoghq.com_datadoggenericresources_v1alpha1.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
"type": {
2525
"description": "Type is the type of the API object",
2626
"enum": [
27+
"monitor",
2728
"notebook",
2829
"synthetics_api_test",
2930
"synthetics_browser_test"

internal/controller/datadoggenericresource/controller.go

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -38,24 +38,23 @@ type Reconciler struct {
3838
client client.Client
3939
datadogSyntheticsClient *datadogV1.SyntheticsApi
4040
datadogNotebooksClient *datadogV1.NotebooksApi
41-
// TODO: add other clients
42-
datadogAuth context.Context
43-
scheme *runtime.Scheme
44-
log logr.Logger
45-
recorder record.EventRecorder
41+
datadogMonitorsClient *datadogV1.MonitorsApi
42+
datadogAuth context.Context
43+
scheme *runtime.Scheme
44+
log logr.Logger
45+
recorder record.EventRecorder
4646
}
4747

4848
func NewReconciler(client client.Client, ddClient datadogclient.DatadogGenericClient, scheme *runtime.Scheme, log logr.Logger, recorder record.EventRecorder) *Reconciler {
4949
return &Reconciler{
5050
client: client,
5151
datadogSyntheticsClient: ddClient.SyntheticsClient,
5252
datadogNotebooksClient: ddClient.NotebooksClient,
53-
// TODO: add other clients
54-
// datadogOtherClient: ddClient.OtherClient,
55-
datadogAuth: ddClient.Auth,
56-
scheme: scheme,
57-
log: log,
58-
recorder: recorder,
53+
datadogMonitorsClient: ddClient.MonitorsClient,
54+
datadogAuth: ddClient.Auth,
55+
scheme: scheme,
56+
log: log,
57+
recorder: recorder,
5958
}
6059
}
6160

internal/controller/datadoggenericresource/controller_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ import (
88
"net/url"
99
"testing"
1010

11+
datadogapi "github.com/DataDog/datadog-api-client-go/v2/api/datadog"
12+
"github.com/DataDog/datadog-api-client-go/v2/api/datadogV1"
13+
"github.com/stretchr/testify/assert"
1114
corev1 "k8s.io/api/core/v1"
1215
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1316
"k8s.io/apimachinery/pkg/types"
@@ -20,11 +23,8 @@ import (
2023
"sigs.k8s.io/controller-runtime/pkg/log/zap"
2124
"sigs.k8s.io/controller-runtime/pkg/reconcile"
2225

23-
datadogapi "github.com/DataDog/datadog-api-client-go/v2/api/datadog"
24-
"github.com/DataDog/datadog-api-client-go/v2/api/datadogV1"
2526
datadoghqv1alpha1 "github.com/DataDog/datadog-operator/api/datadoghq/v1alpha1"
2627
"github.com/DataDog/datadog-operator/pkg/controller/utils/comparison"
27-
"github.com/stretchr/testify/assert"
2828
)
2929

3030
const (

internal/controller/datadoggenericresource/finalizer_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,19 @@ import (
1010
"testing"
1111
"time"
1212

13+
"github.com/go-logr/logr"
14+
"github.com/stretchr/testify/assert"
1315
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
1416
"k8s.io/client-go/kubernetes/scheme"
1517
"k8s.io/client-go/rest"
18+
ctrl "sigs.k8s.io/controller-runtime"
1619
"sigs.k8s.io/controller-runtime/pkg/client"
1720
"sigs.k8s.io/controller-runtime/pkg/client/fake"
1821
"sigs.k8s.io/controller-runtime/pkg/log/zap"
1922
"sigs.k8s.io/controller-runtime/pkg/manager"
2023

2124
"github.com/DataDog/datadog-operator/api/datadoghq/v1alpha1"
2225
datadoghqv1alpha1 "github.com/DataDog/datadog-operator/api/datadoghq/v1alpha1"
23-
"github.com/go-logr/logr"
24-
"github.com/stretchr/testify/assert"
25-
ctrl "sigs.k8s.io/controller-runtime"
2626
)
2727

2828
const (
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package datadoggenericresource
2+
3+
import (
4+
"context"
5+
"encoding/json"
6+
7+
"github.com/DataDog/datadog-api-client-go/v2/api/datadogV1"
8+
"github.com/go-logr/logr"
9+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
10+
11+
"github.com/DataDog/datadog-operator/api/datadoghq/v1alpha1"
12+
)
13+
14+
type MonitorHandler struct{}
15+
16+
func (h *MonitorHandler) createResourcefunc(r *Reconciler, logger logr.Logger, instance *v1alpha1.DatadogGenericResource, status *v1alpha1.DatadogGenericResourceStatus, now metav1.Time, hash string) error {
17+
createdMonitor, err := createMonitor(r.datadogAuth, r.datadogMonitorsClient, instance)
18+
if err != nil {
19+
logger.Error(err, "error creating monitor")
20+
updateErrStatus(status, now, v1alpha1.DatadogSyncStatusCreateError, "CreatingCustomResource", err)
21+
return err
22+
}
23+
logger.Info("created a new monitor", "monitor Id", createdMonitor.GetId())
24+
status.Id = resourceInt64ToStringID(createdMonitor.GetId())
25+
createdTime := metav1.NewTime(*createdMonitor.Created)
26+
status.Created = &createdTime
27+
status.LastForceSyncTime = &createdTime
28+
status.Creator = *createdMonitor.GetCreator().Handle
29+
status.SyncStatus = v1alpha1.DatadogSyncStatusOK
30+
status.CurrentHash = hash
31+
return nil
32+
}
33+
34+
func (h *MonitorHandler) getResourcefunc(r *Reconciler, instance *v1alpha1.DatadogGenericResource) error {
35+
_, err := getMonitor(r.datadogAuth, r.datadogMonitorsClient, instance.Status.Id)
36+
return err
37+
}
38+
func (h *MonitorHandler) updateResourcefunc(r *Reconciler, instance *v1alpha1.DatadogGenericResource) error {
39+
_, err := updateMonitor(r.datadogAuth, r.datadogMonitorsClient, instance)
40+
return err
41+
}
42+
func (h *MonitorHandler) deleteResourcefunc(r *Reconciler, instance *v1alpha1.DatadogGenericResource) error {
43+
return deleteMonitor(r.datadogAuth, r.datadogMonitorsClient, instance.Status.Id)
44+
}
45+
46+
func getMonitor(auth context.Context, client *datadogV1.MonitorsApi, monitorStringID string) (datadogV1.Monitor, error) {
47+
monitorID, err := resourceStringToInt64ID(monitorStringID)
48+
if err != nil {
49+
return datadogV1.Monitor{}, err
50+
}
51+
monitor, _, err := client.GetMonitor(auth, monitorID)
52+
if err != nil {
53+
return datadogV1.Monitor{}, translateClientError(err, "error getting monitor")
54+
}
55+
return monitor, nil
56+
}
57+
58+
func deleteMonitor(auth context.Context, client *datadogV1.MonitorsApi, monitorStringID string) error {
59+
monitorID, err := resourceStringToInt64ID(monitorStringID)
60+
if err != nil {
61+
return err
62+
}
63+
if _, _, err := client.DeleteMonitor(auth, monitorID); err != nil {
64+
return translateClientError(err, "error deleting monitor")
65+
}
66+
return nil
67+
}
68+
69+
func createMonitor(auth context.Context, client *datadogV1.MonitorsApi, instance *v1alpha1.DatadogGenericResource) (datadogV1.Monitor, error) {
70+
monitorBody := &datadogV1.Monitor{}
71+
json.Unmarshal([]byte(instance.Spec.JsonSpec), monitorBody)
72+
monitor, _, err := client.CreateMonitor(auth, *monitorBody)
73+
if err != nil {
74+
return datadogV1.Monitor{}, translateClientError(err, "error creating monitor")
75+
}
76+
return monitor, nil
77+
}
78+
79+
func updateMonitor(auth context.Context, client *datadogV1.MonitorsApi, instance *v1alpha1.DatadogGenericResource) (datadogV1.Monitor, error) {
80+
monitorUpdateData := &datadogV1.MonitorUpdateRequest{}
81+
json.Unmarshal([]byte(instance.Spec.JsonSpec), monitorUpdateData)
82+
monitorID, err := resourceStringToInt64ID(instance.Status.Id)
83+
if err != nil {
84+
return datadogV1.Monitor{}, err
85+
}
86+
monitorUpdated, _, err := client.UpdateMonitor(auth, monitorID, *monitorUpdateData)
87+
if err != nil {
88+
return datadogV1.Monitor{}, translateClientError(err, "error updating monitor")
89+
}
90+
return monitorUpdated, nil
91+
}

internal/controller/datadoggenericresource/synthetics_test.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,11 @@ import (
55
"time"
66

77
"github.com/DataDog/datadog-api-client-go/v2/api/datadogV1"
8-
"github.com/DataDog/datadog-operator/api/datadoghq/v1alpha1"
98
"github.com/go-logr/logr"
109
"github.com/stretchr/testify/assert"
1110
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
11+
12+
"github.com/DataDog/datadog-operator/api/datadoghq/v1alpha1"
1213
)
1314

1415
func Test_updateStatusFromSyntheticsTest(t *testing.T) {

internal/controller/datadoggenericresource/utils.go

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,8 @@ func apiCreateAndUpdateStatus(r *Reconciler, logger logr.Logger, instance *v1alp
5656

5757
func getHandler(resourceType v1alpha1.SupportedResourcesType) ResourceHandler {
5858
switch resourceType {
59+
case v1alpha1.Monitor:
60+
return &MonitorHandler{}
5961
case v1alpha1.Notebook:
6062
return &NotebookHandler{}
6163
case v1alpha1.SyntheticsAPITest:

internal/controller/datadoggenericresource/utils_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ import (
77
"testing"
88

99
datadogapi "github.com/DataDog/datadog-api-client-go/v2/api/datadog"
10-
"github.com/DataDog/datadog-operator/api/datadoghq/v1alpha1"
1110
"github.com/go-logr/logr"
1211
"github.com/stretchr/testify/assert"
13-
1412
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
13+
14+
"github.com/DataDog/datadog-operator/api/datadoghq/v1alpha1"
1515
)
1616

1717
func Test_apiCreateAndUpdateStatus(t *testing.T) {

pkg/datadogclient/client.go

Lines changed: 9 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -97,8 +97,8 @@ func InitDatadogDashboardClient(logger logr.Logger, creds config.Creds) (Datadog
9797
type DatadogGenericClient struct {
9898
SyntheticsClient *datadogV1.SyntheticsApi
9999
NotebooksClient *datadogV1.NotebooksApi
100-
// TODO: other clients depending on the resource
101-
Auth context.Context
100+
MonitorsClient *datadogV1.MonitorsApi
101+
Auth context.Context
102102
}
103103

104104
// InitDatadogGenericClient initializes the Datadog Generic API Client and establishes credentials.
@@ -111,13 +111,19 @@ func InitDatadogGenericClient(logger logr.Logger, creds config.Creds) (DatadogGe
111111
apiClient := datadogapi.NewAPIClient(configV1)
112112
syntheticsClient := datadogV1.NewSyntheticsApi(apiClient)
113113
notebooksClient := datadogV1.NewNotebooksApi(apiClient)
114+
monitorsClient := datadogV1.NewMonitorsApi(apiClient)
114115

115116
authV1, err := setupAuth(logger, creds)
116117
if err != nil {
117118
return DatadogGenericClient{}, err
118119
}
119120

120-
return DatadogGenericClient{SyntheticsClient: syntheticsClient, NotebooksClient: notebooksClient, Auth: authV1}, nil
121+
return DatadogGenericClient{
122+
SyntheticsClient: syntheticsClient,
123+
NotebooksClient: notebooksClient,
124+
MonitorsClient: monitorsClient,
125+
Auth: authV1,
126+
}, nil
121127
}
122128

123129
func setupAuth(logger logr.Logger, creds config.Creds) (context.Context, error) {

0 commit comments

Comments
 (0)