From 2f3ff141866442a3b14146373c2aca4b6b6afe65 Mon Sep 17 00:00:00 2001 From: "assaf.admi" Date: Thu, 10 Oct 2024 13:27:10 +0300 Subject: [PATCH] Add E2E tests --- go.mod | 2 +- go.sum | 2 + tests/e2e/alert_test.go | 153 +++++++++++++++++++++++++ tests/e2e/e2e_suite_test.go | 19 ++- tests/e2e/outbound_webhook_test.go | 133 +++++++++++++++++++++ tests/e2e/recording_rule_group_test.go | 126 ++++++++++++++++++++ tests/e2e/rule_group_test.go | 9 +- 7 files changed, 438 insertions(+), 6 deletions(-) create mode 100644 tests/e2e/alert_test.go create mode 100644 tests/e2e/outbound_webhook_test.go create mode 100644 tests/e2e/recording_rule_group_test.go diff --git a/go.mod b/go.mod index 1e6681a..3a8bfff 100644 --- a/go.mod +++ b/go.mod @@ -3,7 +3,7 @@ module github.com/coralogix/coralogix-operator go 1.22.5 require ( - github.com/coralogix/coralogix-management-sdk v0.2.2-0.20241010083215-2b986bff8a4e + github.com/coralogix/coralogix-management-sdk v0.2.2-0.20241010112755-3e0665911b8e github.com/go-logr/logr v1.3.0 github.com/golang/protobuf v1.5.4 github.com/google/uuid v1.6.0 diff --git a/go.sum b/go.sum index 04f0eca..32f80c4 100644 --- a/go.sum +++ b/go.sum @@ -39,6 +39,8 @@ github.com/coralogix/coralogix-management-sdk v0.2.2-0.20241010082835-a716a0125e github.com/coralogix/coralogix-management-sdk v0.2.2-0.20241010082835-a716a0125e89/go.mod h1:1aa/coMEMe5M1NvnRymOrBF2iCdefaWR0CMaMjPu0oI= github.com/coralogix/coralogix-management-sdk v0.2.2-0.20241010083215-2b986bff8a4e h1:YZhAgtvQdlavo+RlLe1dF7ogmlPlltqX7eCM78GrGqI= github.com/coralogix/coralogix-management-sdk v0.2.2-0.20241010083215-2b986bff8a4e/go.mod h1:1aa/coMEMe5M1NvnRymOrBF2iCdefaWR0CMaMjPu0oI= +github.com/coralogix/coralogix-management-sdk v0.2.2-0.20241010112755-3e0665911b8e h1:suehHoHaJt1A8DzNiuDjFK6eFvkGy5C1pmVlMBvZ3jc= +github.com/coralogix/coralogix-management-sdk v0.2.2-0.20241010112755-3e0665911b8e/go.mod h1:1aa/coMEMe5M1NvnRymOrBF2iCdefaWR0CMaMjPu0oI= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/tests/e2e/alert_test.go b/tests/e2e/alert_test.go new file mode 100644 index 0000000..0e7ad2f --- /dev/null +++ b/tests/e2e/alert_test.go @@ -0,0 +1,153 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package e2e + +import ( + "context" + "fmt" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "k8s.io/utils/ptr" + + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "google.golang.org/protobuf/types/known/wrapperspb" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + + cxsdk "github.com/coralogix/coralogix-management-sdk/go" + + utils "github.com/coralogix/coralogix-operator/apis" + coralogixv1alpha1 "github.com/coralogix/coralogix-operator/apis/coralogix/v1alpha1" +) + +var _ = Describe("Alert", Ordered, func() { + var ( + crClient client.Client + alertClient *cxsdk.AlertsClient + alertID string + alert *coralogixv1alpha1.Alert + ) + + BeforeAll(func() { + crClient = ClientsInstance.GetControllerRuntimeClient() + alertClient = ClientsInstance.GetCoralogixClientSet().Alerts() + }) + + It("Should be created successfully", func(ctx context.Context) { + By("Defining an Alert resource") + const alertName = "promql-alert-zz" + alert = &coralogixv1alpha1.Alert{ + ObjectMeta: metav1.ObjectMeta{ + Name: alertName, + Namespace: testNamespace, + }, + Spec: coralogixv1alpha1.AlertSpec{ + Name: alertName, + Description: "alert from k8s operator", + Severity: "Critical", + NotificationGroups: []coralogixv1alpha1.NotificationGroup{ + { + GroupByFields: []string{"coralogix.metadata.sdkId"}, + Notifications: []coralogixv1alpha1.Notification{ + { + NotifyOn: "TriggeredOnly", + IntegrationName: ptr.To("Email"), + RetriggeringPeriodMinutes: 1, + }, + }, + }, + }, + Scheduling: &coralogixv1alpha1.Scheduling{ + DaysEnabled: []coralogixv1alpha1.Day{"Wednesday", "Thursday"}, + TimeZone: "UTC+02", + StartTime: ptr.To(coralogixv1alpha1.Time("08:30")), + EndTime: ptr.To(coralogixv1alpha1.Time("20:30")), + }, + AlertType: coralogixv1alpha1.AlertType{ + Metric: &coralogixv1alpha1.Metric{ + Promql: &coralogixv1alpha1.Promql{ + SearchQuery: "http_requests_total{status!~\"4..\"}", + Conditions: coralogixv1alpha1.PromqlConditions{ + AlertWhen: "More", + Threshold: utils.FloatToQuantity(3), + SampleThresholdPercentage: 50, + TimeWindow: "TwelveHours", + MinNonNullValuesPercentage: ptr.To(10), + }, + }, + }, + }, + }, + } + + By("Creating the Alert resource in the cluster") + Expect(crClient.Create(ctx, alert)).To(Succeed()) + + By("Fetching the Alert ID") + fetchedAlert := &coralogixv1alpha1.Alert{} + Eventually(func(g Gomega) error { + err := crClient.Get(ctx, types.NamespacedName{Name: alertName, Namespace: testNamespace}, fetchedAlert) + g.Expect(err).NotTo(HaveOccurred()) + + if fetchedAlert.Status.ID != nil { + alertID = *fetchedAlert.Status.ID + return nil + } + + return fmt.Errorf("Alert ID is not set") + }, time.Minute, time.Second).Should(Succeed()) + + By("Verifying Alert exists in Coralogix backend") + Eventually(func() error { + _, err := alertClient.Get(ctx, &cxsdk.GetAlertDefRequest{Id: wrapperspb.String(alertID)}) + return err + }, time.Minute, time.Second).Should(Succeed()) + }) + + It("Should be updated successfully", func(ctx context.Context) { + By("Patching the Alert resource") + const newAlertName = "promql-alert-updated" + modifiedAlert := alert.DeepCopy() + modifiedAlert.Spec.Name = newAlertName + err := crClient.Patch(ctx, modifiedAlert, client.MergeFrom(alert)) + Expect(err).NotTo(HaveOccurred()) + + By("Verifying Alert is updated in Coralogix backend") + Eventually(func(g Gomega) bool { + getAlertRes, err := alertClient.Get(ctx, &cxsdk.GetAlertDefRequest{Id: wrapperspb.String(alertID)}) + g.Expect(err).NotTo(HaveOccurred()) + return getAlertRes.GetAlertDef().GetUpdatedTime().AsTime(). + After(getAlertRes.GetAlertDef().GetCreatedTime().AsTime()) + }, time.Minute, time.Second).Should(BeTrue()) + }) + + It("Should be deleted successfully", func(ctx context.Context) { + By("Deleting the Alert resource") + err := crClient.Delete(ctx, alert) + Expect(err).NotTo(HaveOccurred()) + + By("Verifying Alert is deleted from Coralogix backend") + Eventually(func() codes.Code { + _, err := alertClient.Get(ctx, &cxsdk.GetAlertDefRequest{Id: wrapperspb.String(alertID)}) + return status.Code(err) + }, time.Minute, time.Second).Should(Equal(codes.NotFound)) + }) +}) diff --git a/tests/e2e/e2e_suite_test.go b/tests/e2e/e2e_suite_test.go index 74d4004..31810f7 100644 --- a/tests/e2e/e2e_suite_test.go +++ b/tests/e2e/e2e_suite_test.go @@ -31,6 +31,8 @@ import ( cxsdk "github.com/coralogix/coralogix-management-sdk/go" ) +const testNamespace = "coralogix-e2e-test" + func TestE2E(t *testing.T) { RegisterFailHandler(Fail) RunSpecs(t, "Coralogix operator E2E test suite") @@ -45,9 +47,18 @@ var _ = BeforeSuite(func(ctx context.Context) { Expect(ClientsInstance.InitControllerRuntimeClient()).To(Succeed()) Expect(ClientsInstance.InitK8sClient()).To(Succeed()) + k8sClient := ClientsInstance.GetK8sClient() + + By("Creating test namespace") + _, err := k8sClient.CoreV1().Namespaces().Create(ctx, &corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: testNamespace, + }, + }, metav1.CreateOptions{}) + Expect(err).NotTo(HaveOccurred()) + By("Validating that the controller-manager pod is running") Eventually(func() corev1.PodPhase { - k8sClient := ClientsInstance.GetK8sClient() podList, err := k8sClient.CoreV1(). Pods("coralogix-operator-system"). List(ctx, metav1.ListOptions{LabelSelector: "control-plane=controller-manager"}) @@ -55,3 +66,9 @@ var _ = BeforeSuite(func(ctx context.Context) { return podList.Items[0].Status.Phase }, time.Minute, time.Second).Should(Equal(corev1.PodRunning)) }) + +var _ = AfterSuite(func(ctx context.Context) { + By("Deleting test namespace") + k8sClient := ClientsInstance.GetK8sClient() + Expect(k8sClient.CoreV1().Namespaces().Delete(ctx, testNamespace, metav1.DeleteOptions{})).To(Succeed()) +}) diff --git a/tests/e2e/outbound_webhook_test.go b/tests/e2e/outbound_webhook_test.go new file mode 100644 index 0000000..9e04e32 --- /dev/null +++ b/tests/e2e/outbound_webhook_test.go @@ -0,0 +1,133 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package e2e + +import ( + "context" + "fmt" + + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/types/known/wrapperspb" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + + cxsdk "github.com/coralogix/coralogix-management-sdk/go" + + coralogixv1alpha1 "github.com/coralogix/coralogix-operator/apis/coralogix/v1alpha1" +) + +var _ = Describe("Outbound Webhook", Ordered, func() { + var ( + crClient client.Client + OutboundWebhookClient *cxsdk.WebhooksClient + outboundWebhookID string + outBoundWebhhok *coralogixv1alpha1.OutboundWebhook + ) + + BeforeAll(func() { + crClient = ClientsInstance.GetControllerRuntimeClient() + OutboundWebhookClient = ClientsInstance.GetCoralogixClientSet().Webhooks() + }) + + It("Should be created successfully", func(ctx context.Context) { + By("Defining an Outbound webhook resource") + const outboundWebhookName = "slack-outbound-webhook" + outBoundWebhhok = &coralogixv1alpha1.OutboundWebhook{ + ObjectMeta: metav1.ObjectMeta{ + Name: outboundWebhookName, + Namespace: testNamespace, + }, + Spec: coralogixv1alpha1.OutboundWebhookSpec{ + Name: outboundWebhookName, + OutboundWebhookType: coralogixv1alpha1.OutboundWebhookType{ + Slack: &coralogixv1alpha1.Slack{ + Url: "https://hooks.slack.com/services", + Attachments: []coralogixv1alpha1.SlackConfigAttachment{ + { + Type: "MetricSnapshot", + IsActive: true, + }, + }, + Digests: []coralogixv1alpha1.SlackConfigDigest{ + { + Type: "FlowAnomalies", + IsActive: true, + }, + }, + }, + }, + }, + } + + By("Creating the OutboundWebhook resource in the cluster") + Expect(crClient.Create(ctx, outBoundWebhhok)).To(Succeed()) + + By("Fetching the OutboundWebhook ID") + fetchedOutboundWebhook := &coralogixv1alpha1.OutboundWebhook{} + Eventually(func(g Gomega) error { + err := crClient.Get(ctx, types.NamespacedName{Name: outboundWebhookName, Namespace: testNamespace}, fetchedOutboundWebhook) + g.Expect(err).NotTo(HaveOccurred()) + + if fetchedOutboundWebhook.Status.ID != nil { + outboundWebhookID = *fetchedOutboundWebhook.Status.ID + return nil + } + + return fmt.Errorf("OutboundWebhook ID is not set") + }, time.Minute, time.Second).Should(Succeed()) + + By("Verifying OutboundWebhook exists in Coralogix backend") + Eventually(func() error { + _, err := OutboundWebhookClient.Get(ctx, &cxsdk.GetOutgoingWebhookRequest{Id: wrapperspb.String(outboundWebhookID)}) + return err + }, time.Minute, time.Second).Should(Succeed()) + }) + + It("Should be updated successfully", func(ctx context.Context) { + By("Patching the OutboundWebhook resource") + const newOutboundWebhookName = "slack-outbound-webhook-updated" + modifiedOutboundWebhook := outBoundWebhhok.DeepCopy() + modifiedOutboundWebhook.Spec.Name = newOutboundWebhookName + err := crClient.Patch(ctx, modifiedOutboundWebhook, client.MergeFrom(outBoundWebhhok)) + Expect(err).NotTo(HaveOccurred()) + + By("Verifying OutboundWebhook is updated in Coralogix backend") + Eventually(func(g Gomega) string { + getOutboundWebhookRes, err := OutboundWebhookClient.Get(ctx, &cxsdk.GetOutgoingWebhookRequest{Id: wrapperspb.String(outboundWebhookID)}) + g.Expect(err).NotTo(HaveOccurred()) + return getOutboundWebhookRes.GetWebhook().GetName().GetValue() + }, time.Minute, time.Second).Should(Equal(newOutboundWebhookName)) + }) + + It("Should be deleted successfully", func(ctx context.Context) { + By("Deleting the OutboundWebhook resource") + err := crClient.Delete(ctx, outBoundWebhhok) + Expect(err).NotTo(HaveOccurred()) + + By("Verifying OutboundWebhook is deleted from Coralogix backend") + Eventually(func() codes.Code { + _, err := OutboundWebhookClient.Get(ctx, &cxsdk.GetOutgoingWebhookRequest{Id: wrapperspb.String(outboundWebhookID)}) + return status.Code(err) + }, time.Minute, time.Second).Should(Equal(codes.NotFound)) + }) +}) diff --git a/tests/e2e/recording_rule_group_test.go b/tests/e2e/recording_rule_group_test.go new file mode 100644 index 0000000..d34d456 --- /dev/null +++ b/tests/e2e/recording_rule_group_test.go @@ -0,0 +1,126 @@ +/* +Copyright 2024. + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package e2e + +import ( + "context" + "fmt" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + "time" + + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "k8s.io/apimachinery/pkg/types" + "sigs.k8s.io/controller-runtime/pkg/client" + + cxsdk "github.com/coralogix/coralogix-management-sdk/go" + + coralogixv1alpha1 "github.com/coralogix/coralogix-operator/apis/coralogix/v1alpha1" +) + +var _ = Describe("Recording Rule Group Set", Ordered, func() { + var ( + crClient client.Client + recordingRuleGroupSetClient *cxsdk.RecordingRuleGroupSetsClient + recordingRuleGroupSetID string + recordingRuleGroupSet *coralogixv1alpha1.RecordingRuleGroupSet + ) + + BeforeAll(func() { + crClient = ClientsInstance.GetControllerRuntimeClient() + recordingRuleGroupSetClient = ClientsInstance.GetCoralogixClientSet().RecordingRuleGroups() + }) + + It("Should be created successfully", func(ctx context.Context) { + By("Defining a RecordingRuleGroupSet resource") + const recordingRuleGroupSetName = "recording-rule-group-set" + recordingRuleGroupSet = &coralogixv1alpha1.RecordingRuleGroupSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: recordingRuleGroupSetName, + Namespace: testNamespace, + }, + Spec: coralogixv1alpha1.RecordingRuleGroupSetSpec{ + Groups: []coralogixv1alpha1.RecordingRuleGroup{ + { + Name: "rules", + IntervalSeconds: 60, + Rules: []coralogixv1alpha1.RecordingRule{ + { + Expr: "vector(1)", + Record: "ExampleRecord", + }, + }, + }, + }, + }, + } + + By("Creating the RecordingRuleGroupSet resource in the cluster") + Expect(crClient.Create(ctx, recordingRuleGroupSet)).To(Succeed()) + + By("Fetching the RecordingRuleGroupSet ID") + fetchedRecordingRuleGroupSet := &coralogixv1alpha1.RecordingRuleGroupSet{} + Eventually(func(g Gomega) error { + err := crClient.Get(ctx, types.NamespacedName{Name: recordingRuleGroupSetName, Namespace: testNamespace}, fetchedRecordingRuleGroupSet) + g.Expect(err).NotTo(HaveOccurred()) + + if fetchedRecordingRuleGroupSet.Status.ID != nil { + recordingRuleGroupSetID = *fetchedRecordingRuleGroupSet.Status.ID + return nil + } + + return fmt.Errorf("RecordingRuleGroupSet ID is not set") + }, time.Minute, time.Second).Should(Succeed()) + + By("Verifying RecordingRuleGroupSet exists in Coralogix backend") + Eventually(func() error { + _, err := recordingRuleGroupSetClient.Get(ctx, &cxsdk.GetRuleGroupSetRequest{Id: recordingRuleGroupSetID}) + return err + }, time.Minute, time.Second).Should(Succeed()) + }) + + It("Should be updated successfully", func(ctx context.Context) { + By("Patching the RecordingRuleGroupSet resource") + const newRuleName = "rules-updated" + modifiedRecordingRuleGroupSet := recordingRuleGroupSet.DeepCopy() + modifiedRecordingRuleGroupSet.Spec.Groups[0].Name = newRuleName + err := crClient.Patch(ctx, modifiedRecordingRuleGroupSet, client.MergeFrom(recordingRuleGroupSet)) + Expect(err).NotTo(HaveOccurred()) + + By("Verifying RecordingRuleGroupSet is updated in Coralogix backend") + Eventually(func(g Gomega) string { + getRuleGroupRes, err := recordingRuleGroupSetClient.Get(ctx, &cxsdk.GetRuleGroupSetRequest{Id: recordingRuleGroupSetID}) + g.Expect(err).NotTo(HaveOccurred()) + return getRuleGroupRes.GetGroups()[0].GetName() + }, time.Minute, time.Second).Should(Equal(newRuleName)) + }) + + It("Should be deleted successfully", func(ctx context.Context) { + By("Deleting the RecordingRuleGroupSet resource") + err := crClient.Delete(ctx, recordingRuleGroupSet) + Expect(err).NotTo(HaveOccurred()) + + By("Verifying RecordingRuleGroupSet is deleted from Coralogix backend") + Eventually(func() codes.Code { + _, err = recordingRuleGroupSetClient.Get(ctx, &cxsdk.GetRuleGroupSetRequest{Id: recordingRuleGroupSetID}) + return status.Code(err) + }, time.Minute, time.Second).Should(Equal(codes.NotFound)) + }) +}) diff --git a/tests/e2e/rule_group_test.go b/tests/e2e/rule_group_test.go index a298b2b..da0d2aa 100644 --- a/tests/e2e/rule_group_test.go +++ b/tests/e2e/rule_group_test.go @@ -50,13 +50,14 @@ var _ = Describe("Rule Group", Ordered, func() { It("Should be created successfully", func(ctx context.Context) { By("Defining a RuleGroup resource") + const ruleGroupName = "json-extract-rule" ruleGroup = &coralogixv1alpha1.RuleGroup{ ObjectMeta: metav1.ObjectMeta{ - Name: "json-extract-rule", - Namespace: "default", + Name: ruleGroupName, + Namespace: testNamespace, }, Spec: coralogixv1alpha1.RuleGroupSpec{ - Name: "json-extract-rule", + Name: ruleGroupName, Description: "rule-group from k8s operator", RuleSubgroups: []coralogixv1alpha1.RuleSubGroup{ { @@ -81,7 +82,7 @@ var _ = Describe("Rule Group", Ordered, func() { By("Fetching the RuleGroup ID") fetchedRuleGroup := &coralogixv1alpha1.RuleGroup{} Eventually(func(g Gomega) error { - err := crClient.Get(ctx, types.NamespacedName{Name: "json-extract-rule", Namespace: "default"}, fetchedRuleGroup) + err := crClient.Get(ctx, types.NamespacedName{Name: ruleGroupName, Namespace: testNamespace}, fetchedRuleGroup) g.Expect(err).NotTo(HaveOccurred()) if fetchedRuleGroup.Status.ID != nil {