diff --git a/internal/controller/datadoggenericresource/notebooks.go b/internal/controller/datadoggenericresource/notebooks.go index 3e2999708..dd588b7e7 100644 --- a/internal/controller/datadoggenericresource/notebooks.go +++ b/internal/controller/datadoggenericresource/notebooks.go @@ -7,10 +7,44 @@ import ( "strconv" "github.com/DataDog/datadog-operator/api/datadoghq/v1alpha1" + "github.com/go-logr/logr" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/DataDog/datadog-api-client-go/v2/api/datadogV1" ) +type NotebookHandler struct{} + +func (h *NotebookHandler) createResourcefunc(r *Reconciler, logger logr.Logger, instance *v1alpha1.DatadogGenericResource, status *v1alpha1.DatadogGenericResourceStatus, now metav1.Time, hash string) error { + createdNotebook, err := createNotebook(r.datadogAuth, r.datadogNotebooksClient, instance) + if err != nil { + logger.Error(err, "error creating notebook") + updateErrStatus(status, now, v1alpha1.DatadogSyncStatusCreateError, "CreatingCustomResource", err) + return err + } + logger.Info("created a new notebook", "notebook Id", createdNotebook.Data.GetId()) + status.Id = notebookInt64ToString(createdNotebook.Data.GetId()) + createdTime := metav1.NewTime(*createdNotebook.Data.GetAttributes().Created) + status.Created = &createdTime + status.LastForceSyncTime = &createdTime + status.Creator = *createdNotebook.Data.GetAttributes().Author.Handle + status.SyncStatus = v1alpha1.DatadogSyncStatusOK + status.CurrentHash = hash + return nil +} + +func (h *NotebookHandler) getResourcefunc(r *Reconciler, instance *v1alpha1.DatadogGenericResource) error { + _, err := getNotebook(r.datadogAuth, r.datadogNotebooksClient, instance.Status.Id) + return err +} +func (h *NotebookHandler) updateResourcefunc(r *Reconciler, instance *v1alpha1.DatadogGenericResource) error { + _, err := updateNotebook(r.datadogAuth, r.datadogNotebooksClient, instance) + return err +} +func (h *NotebookHandler) deleteResourcefunc(r *Reconciler, instance *v1alpha1.DatadogGenericResource) error { + return deleteNotebook(r.datadogAuth, r.datadogNotebooksClient, instance.Status.Id) +} + func getNotebook(auth context.Context, client *datadogV1.NotebooksApi, notebookStringID string) (datadogV1.NotebookResponse, error) { notebookID, err := notebookStringToInt64(notebookStringID) if err != nil { diff --git a/internal/controller/datadoggenericresource/resource_handler.go b/internal/controller/datadoggenericresource/resource_handler.go new file mode 100644 index 000000000..0fe4a7ba7 --- /dev/null +++ b/internal/controller/datadoggenericresource/resource_handler.go @@ -0,0 +1,14 @@ +package datadoggenericresource + +import ( + "github.com/DataDog/datadog-operator/api/datadoghq/v1alpha1" + "github.com/go-logr/logr" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +type ResourceHandler interface { + createResourcefunc(r *Reconciler, logger logr.Logger, instance *v1alpha1.DatadogGenericResource, status *v1alpha1.DatadogGenericResourceStatus, now metav1.Time, hash string) error + getResourcefunc(r *Reconciler, instance *v1alpha1.DatadogGenericResource) error + updateResourcefunc(r *Reconciler, instance *v1alpha1.DatadogGenericResource) error + deleteResourcefunc(r *Reconciler, instance *v1alpha1.DatadogGenericResource) error +} diff --git a/internal/controller/datadoggenericresource/synthetics.go b/internal/controller/datadoggenericresource/synthetics.go index f5d1811f8..ccbc5bac7 100644 --- a/internal/controller/datadoggenericresource/synthetics.go +++ b/internal/controller/datadoggenericresource/synthetics.go @@ -5,10 +5,62 @@ import ( "encoding/json" "github.com/DataDog/datadog-operator/api/datadoghq/v1alpha1" + "github.com/go-logr/logr" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/DataDog/datadog-api-client-go/v2/api/datadogV1" ) +type SyntheticsAPITestHandler struct{} + +func (h *SyntheticsAPITestHandler) createResourcefunc(r *Reconciler, logger logr.Logger, instance *v1alpha1.DatadogGenericResource, status *v1alpha1.DatadogGenericResourceStatus, now metav1.Time, hash string) error { + createdTest, err := createSyntheticsAPITest(r.datadogAuth, r.datadogSyntheticsClient, instance) + if err != nil { + logger.Error(err, "error creating API test") + updateErrStatus(status, now, v1alpha1.DatadogSyncStatusCreateError, "CreatingCustomResource", err) + return err + } + additionalProperties := createdTest.AdditionalProperties + return updateStatusFromSyntheticsTest(&createdTest, additionalProperties, status, logger, hash) +} + +func (h *SyntheticsAPITestHandler) getResourcefunc(r *Reconciler, instance *v1alpha1.DatadogGenericResource) error { + _, err := getSyntheticsTest(r.datadogAuth, r.datadogSyntheticsClient, instance.Status.Id) + return err +} +func (h *SyntheticsAPITestHandler) updateResourcefunc(r *Reconciler, instance *v1alpha1.DatadogGenericResource) error { + _, err := updateSyntheticsAPITest(r.datadogAuth, r.datadogSyntheticsClient, instance) + return err +} +func (h *SyntheticsAPITestHandler) deleteResourcefunc(r *Reconciler, instance *v1alpha1.DatadogGenericResource) error { + return deleteSyntheticTest(r.datadogAuth, r.datadogSyntheticsClient, instance.Status.Id) +} + +type SyntheticsBrowserTestHandler struct{} + +func (h *SyntheticsBrowserTestHandler) createResourcefunc(r *Reconciler, logger logr.Logger, instance *v1alpha1.DatadogGenericResource, status *v1alpha1.DatadogGenericResourceStatus, now metav1.Time, hash string) error { + createdTest, err := createSyntheticBrowserTest(r.datadogAuth, r.datadogSyntheticsClient, instance) + if err != nil { + logger.Error(err, "error creating browser test") + updateErrStatus(status, now, v1alpha1.DatadogSyncStatusCreateError, "CreatingCustomResource", err) + return err + } + additionalProperties := createdTest.AdditionalProperties + return updateStatusFromSyntheticsTest(&createdTest, additionalProperties, status, logger, hash) +} + +func (h *SyntheticsBrowserTestHandler) getResourcefunc(r *Reconciler, instance *v1alpha1.DatadogGenericResource) error { + _, err := getSyntheticsTest(r.datadogAuth, r.datadogSyntheticsClient, instance.Status.Id) + return err +} +func (h *SyntheticsBrowserTestHandler) updateResourcefunc(r *Reconciler, instance *v1alpha1.DatadogGenericResource) error { + _, err := updateSyntheticsBrowserTest(r.datadogAuth, r.datadogSyntheticsClient, instance) + return err +} +func (h *SyntheticsBrowserTestHandler) deleteResourcefunc(r *Reconciler, instance *v1alpha1.DatadogGenericResource) error { + return deleteSyntheticTest(r.datadogAuth, r.datadogSyntheticsClient, instance.Status.Id) +} + // Synthetic tests (encompass browser and API tests): get func getSyntheticsTest(auth context.Context, client *datadogV1.SyntheticsApi, testID string) (datadogV1.SyntheticsTestDetails, error) { test, _, err := client.GetTest(auth, testID) diff --git a/internal/controller/datadoggenericresource/utils.go b/internal/controller/datadoggenericresource/utils.go index 0c52f60d1..9b5eb701d 100644 --- a/internal/controller/datadoggenericresource/utils.go +++ b/internal/controller/datadoggenericresource/utils.go @@ -23,125 +23,57 @@ const ( operationUpdate operation = "update" ) -type apiHandlerKey struct { - resourceType v1alpha1.SupportedResourcesType - op operation +type MockHandler struct{} + +func (h *MockHandler) createResourcefunc(r *Reconciler, logger logr.Logger, instance *v1alpha1.DatadogGenericResource, status *v1alpha1.DatadogGenericResourceStatus, now metav1.Time, hash string) error { + status.Id = "mock-id" + status.Created = &now + status.LastForceSyncTime = &now + status.Creator = "mock-creator" + status.SyncStatus = v1alpha1.DatadogSyncStatusOK + status.CurrentHash = hash + return nil } -// Delete, Get and Update operations share the same signature -type apiHandlerFunc func(r *Reconciler, instance *v1alpha1.DatadogGenericResource) error - -var apiHandlers = map[apiHandlerKey]apiHandlerFunc{ - {v1alpha1.SyntheticsBrowserTest, operationGet}: func(r *Reconciler, instance *v1alpha1.DatadogGenericResource) error { - _, err := getSyntheticsTest(r.datadogAuth, r.datadogSyntheticsClient, instance.Status.Id) - return err - }, - {v1alpha1.SyntheticsBrowserTest, operationUpdate}: func(r *Reconciler, instance *v1alpha1.DatadogGenericResource) error { - _, err := updateSyntheticsBrowserTest(r.datadogAuth, r.datadogSyntheticsClient, instance) - return err - }, - {v1alpha1.SyntheticsBrowserTest, operationDelete}: func(r *Reconciler, instance *v1alpha1.DatadogGenericResource) error { - return deleteSyntheticTest(r.datadogAuth, r.datadogSyntheticsClient, instance.Status.Id) - }, - {v1alpha1.SyntheticsAPITest, operationGet}: func(r *Reconciler, instance *v1alpha1.DatadogGenericResource) error { - _, err := getSyntheticsTest(r.datadogAuth, r.datadogSyntheticsClient, instance.Status.Id) - return err - }, - {v1alpha1.SyntheticsAPITest, operationUpdate}: func(r *Reconciler, instance *v1alpha1.DatadogGenericResource) error { - _, err := updateSyntheticsAPITest(r.datadogAuth, r.datadogSyntheticsClient, instance) - return err - }, - {v1alpha1.SyntheticsAPITest, operationDelete}: func(r *Reconciler, instance *v1alpha1.DatadogGenericResource) error { - return deleteSyntheticTest(r.datadogAuth, r.datadogSyntheticsClient, instance.Status.Id) - }, - {v1alpha1.Notebook, operationGet}: func(r *Reconciler, instance *v1alpha1.DatadogGenericResource) error { - _, err := getNotebook(r.datadogAuth, r.datadogNotebooksClient, instance.Status.Id) - return err - }, - {v1alpha1.Notebook, operationUpdate}: func(r *Reconciler, instance *v1alpha1.DatadogGenericResource) error { - _, err := updateNotebook(r.datadogAuth, r.datadogNotebooksClient, instance) - return err - }, - {v1alpha1.Notebook, operationDelete}: func(r *Reconciler, instance *v1alpha1.DatadogGenericResource) error { - return deleteNotebook(r.datadogAuth, r.datadogNotebooksClient, instance.Status.Id) - }, - {mockSubresource, operationGet}: func(r *Reconciler, instance *v1alpha1.DatadogGenericResource) error { - return nil - }, - {mockSubresource, operationUpdate}: func(r *Reconciler, instance *v1alpha1.DatadogGenericResource) error { - return nil - }, - {mockSubresource, operationDelete}: func(r *Reconciler, instance *v1alpha1.DatadogGenericResource) error { - return nil - }, +func (h *MockHandler) getResourcefunc(r *Reconciler, instance *v1alpha1.DatadogGenericResource) error { + return nil +} +func (h *MockHandler) updateResourcefunc(r *Reconciler, instance *v1alpha1.DatadogGenericResource) error { + return nil +} +func (h *MockHandler) deleteResourcefunc(r *Reconciler, instance *v1alpha1.DatadogGenericResource) error { + return nil } -// Common handler executor (delete, get and update) -func executeHandler(operation operation, r *Reconciler, instance *v1alpha1.DatadogGenericResource) error { - key := apiHandlerKey{resourceType: instance.Spec.Type, op: operation} - if handler, found := apiHandlers[key]; found { - return handler(r, instance) - } - return unsupportedInstanceType(instance) +func apiDelete(r *Reconciler, instance *v1alpha1.DatadogGenericResource) error { + return getHandler(instance.Spec.Type).deleteResourcefunc(r, instance) } -// Create is handled separately due to the dynamic signature and need to extract/update status based on the returned struct -type createHandlerFunc func(r *Reconciler, logger logr.Logger, instance *v1alpha1.DatadogGenericResource, status *v1alpha1.DatadogGenericResourceStatus, now metav1.Time, hash string) error +func apiGet(r *Reconciler, instance *v1alpha1.DatadogGenericResource) error { + return getHandler(instance.Spec.Type).getResourcefunc(r, instance) +} -var createHandlers = map[v1alpha1.SupportedResourcesType]createHandlerFunc{ - v1alpha1.SyntheticsBrowserTest: func(r *Reconciler, logger logr.Logger, instance *v1alpha1.DatadogGenericResource, status *v1alpha1.DatadogGenericResourceStatus, now metav1.Time, hash string) error { - createdTest, err := createSyntheticBrowserTest(r.datadogAuth, r.datadogSyntheticsClient, instance) - if err != nil { - logger.Error(err, "error creating browser test") - updateErrStatus(status, now, v1alpha1.DatadogSyncStatusCreateError, "CreatingCustomResource", err) - return err - } - additionalProperties := createdTest.AdditionalProperties - return updateStatusFromSyntheticsTest(&createdTest, additionalProperties, status, logger, hash) - }, - v1alpha1.SyntheticsAPITest: func(r *Reconciler, logger logr.Logger, instance *v1alpha1.DatadogGenericResource, status *v1alpha1.DatadogGenericResourceStatus, now metav1.Time, hash string) error { - createdTest, err := createSyntheticsAPITest(r.datadogAuth, r.datadogSyntheticsClient, instance) - if err != nil { - logger.Error(err, "error creating API test") - updateErrStatus(status, now, v1alpha1.DatadogSyncStatusCreateError, "CreatingCustomResource", err) - return err - } - additionalProperties := createdTest.AdditionalProperties - return updateStatusFromSyntheticsTest(&createdTest, additionalProperties, status, logger, hash) - }, - v1alpha1.Notebook: func(r *Reconciler, logger logr.Logger, instance *v1alpha1.DatadogGenericResource, status *v1alpha1.DatadogGenericResourceStatus, now metav1.Time, hash string) error { - createdNotebook, err := createNotebook(r.datadogAuth, r.datadogNotebooksClient, instance) - if err != nil { - logger.Error(err, "error creating notebook") - updateErrStatus(status, now, v1alpha1.DatadogSyncStatusCreateError, "CreatingCustomResource", err) - return err - } - logger.Info("created a new notebook", "notebook Id", createdNotebook.Data.GetId()) - status.Id = notebookInt64ToString(createdNotebook.Data.GetId()) - createdTime := metav1.NewTime(*createdNotebook.Data.GetAttributes().Created) - status.Created = &createdTime - status.LastForceSyncTime = &createdTime - status.Creator = *createdNotebook.Data.GetAttributes().Author.Handle - status.SyncStatus = v1alpha1.DatadogSyncStatusOK - status.CurrentHash = hash - return nil - }, - mockSubresource: func(r *Reconciler, logger logr.Logger, instance *v1alpha1.DatadogGenericResource, status *v1alpha1.DatadogGenericResourceStatus, now metav1.Time, hash string) error { - status.Id = "mock-id" - status.Created = &now - status.LastForceSyncTime = &now - status.Creator = "mock-creator" - status.SyncStatus = v1alpha1.DatadogSyncStatusOK - status.CurrentHash = hash - return nil - }, +func apiUpdate(r *Reconciler, instance *v1alpha1.DatadogGenericResource) error { + return getHandler(instance.Spec.Type).updateResourcefunc(r, instance) +} + +func apiCreateAndUpdateStatus(r *Reconciler, logger logr.Logger, instance *v1alpha1.DatadogGenericResource, status *v1alpha1.DatadogGenericResourceStatus, now metav1.Time, hash string) error { + return getHandler(instance.Spec.Type).createResourcefunc(r, logger, instance, status, now, hash) } -func executeCreateHandler(r *Reconciler, logger logr.Logger, instance *v1alpha1.DatadogGenericResource, status *v1alpha1.DatadogGenericResourceStatus, now metav1.Time, hash string) error { - if handler, found := createHandlers[instance.Spec.Type]; found { - return handler(r, logger, instance, status, now, hash) +func getHandler(resourceType v1alpha1.SupportedResourcesType) ResourceHandler { + switch resourceType { + case v1alpha1.Notebook: + return &NotebookHandler{} + case v1alpha1.SyntheticsAPITest: + return &SyntheticsAPITestHandler{} + case v1alpha1.SyntheticsBrowserTest: + return &SyntheticsBrowserTestHandler{} + case mockSubresource: + return &MockHandler{} + default: + panic(unsupportedInstanceType(resourceType)) } - return unsupportedInstanceType(instance) } func translateClientError(err error, msg string) error { @@ -162,24 +94,8 @@ func translateClientError(err error, msg string) error { return fmt.Errorf(msg+": %w", err) } -func unsupportedInstanceType(instance *v1alpha1.DatadogGenericResource) error { - return fmt.Errorf("unsupported type: %s", instance.Spec.Type) -} - -func apiDelete(r *Reconciler, instance *v1alpha1.DatadogGenericResource) error { - return executeHandler(operationDelete, r, instance) -} - -func apiGet(r *Reconciler, instance *v1alpha1.DatadogGenericResource) error { - return executeHandler(operationGet, r, instance) -} - -func apiUpdate(r *Reconciler, instance *v1alpha1.DatadogGenericResource) error { - return executeHandler(operationUpdate, r, instance) -} - -func apiCreateAndUpdateStatus(r *Reconciler, logger logr.Logger, instance *v1alpha1.DatadogGenericResource, status *v1alpha1.DatadogGenericResourceStatus, now metav1.Time, hash string) error { - return executeCreateHandler(r, logger, instance, status, now, hash) +func unsupportedInstanceType(resourceType v1alpha1.SupportedResourcesType) error { + return fmt.Errorf("unsupported type: %s", resourceType) } func updateStatusFromSyntheticsTest(createdTest interface{ GetPublicId() string }, additionalProperties map[string]interface{}, status *v1alpha1.DatadogGenericResourceStatus, logger logr.Logger, hash string) error { diff --git a/internal/controller/datadoggenericresource/utils_test.go b/internal/controller/datadoggenericresource/utils_test.go index 38f7aa133..b802990f3 100644 --- a/internal/controller/datadoggenericresource/utils_test.go +++ b/internal/controller/datadoggenericresource/utils_test.go @@ -16,25 +16,7 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -func Test_executeHandler(t *testing.T) { - mockReconciler := &Reconciler{} - instance := &v1alpha1.DatadogGenericResource{ - Spec: v1alpha1.DatadogGenericResourceSpec{ - Type: mockSubresource, - }, - } - - // Valid operation and subresource case - err := executeHandler(operationGet, mockReconciler, instance) - assert.NoError(t, err) - - // Valid operation and invalid subresource case - instance.Spec.Type = "unsupportedType" - err = executeHandler(operationGet, mockReconciler, instance) - assert.EqualError(t, err, "unsupported type: unsupportedType") -} - -func Test_executeCreateHandler(t *testing.T) { +func Test_apiCreateAndUpdateStatus(t *testing.T) { mockReconciler := &Reconciler{} logger := &logr.Logger{} instance := &v1alpha1.DatadogGenericResource{ @@ -45,13 +27,14 @@ func Test_executeCreateHandler(t *testing.T) { status := &v1alpha1.DatadogGenericResourceStatus{} // Valid subresource case - err := executeCreateHandler(mockReconciler, *logger, instance, status, metav1.Now(), "test-hash") + err := apiCreateAndUpdateStatus(mockReconciler, *logger, instance, status, metav1.Now(), "test-hash") assert.NoError(t, err) // Invalid subresource case instance.Spec.Type = "unsupportedType" - err = executeCreateHandler(mockReconciler, *logger, instance, status, metav1.Now(), "test-hash") - assert.EqualError(t, err, "unsupported type: unsupportedType") + assert.PanicsWithError(t, "unsupported type: unsupportedType", func() { + apiCreateAndUpdateStatus(mockReconciler, *logger, instance, status, metav1.Now(), "test-hash") + }) } func Test_apiGet(t *testing.T) {