diff --git a/controllers/flowcollector_controller_console_test.go b/controllers/flowcollector_controller_console_test.go index 44cafec2d..20544c100 100644 --- a/controllers/flowcollector_controller_console_test.go +++ b/controllers/flowcollector_controller_console_test.go @@ -13,7 +13,6 @@ import ( "k8s.io/utils/ptr" flowslatest "github.com/netobserv/network-observability-operator/apis/flowcollector/v1beta2" - "github.com/netobserv/network-observability-operator/controllers/constants" "github.com/netobserv/network-observability-operator/pkg/test" ) @@ -24,15 +23,9 @@ const cpNamespace = "namespace-console-specs" // nolint:cyclop func flowCollectorConsolePluginSpecs() { - cpDepl := test.Deployment(constants.PluginName) - cpCM := test.ConfigMap("console-plugin-config") - cpSvc := test.Service(constants.PluginName) - cpSA := test.ServiceAccount(constants.PluginName) - cpCRB := test.ClusterRoleBinding(constants.PluginName) - cpSM := test.ServiceMonitor(constants.PluginName) crKey := types.NamespacedName{Name: "cluster"} consoleCRKey := types.NamespacedName{Name: "cluster"} - configKey := cpCM.GetKey(cpNamespace) + configKey := test.PluginCM.GetKey(cpNamespace) BeforeEach(func() { // Add any setup steps that needs to be executed before each test @@ -112,25 +105,18 @@ func flowCollectorConsolePluginSpecs() { // test Kubernetes API server, which isn't the goal here. Context("Deploying the console plugin", func() { It("Should create successfully", func() { - - objs := expectCreation(cpNamespace, - cpDepl, - cpSvc, - cpCM, - cpSA, - cpCRB, - cpSM, - ) + all := append(test.PluginResources, test.PluginCRB) + objs := expectPresence(cpNamespace, all...) Expect(objs).To(HaveLen(6)) Expect(*objs[0].(*appsv1.Deployment).Spec.Replicas).To(Equal(int32(1))) - Expect(objs[1].(*v1.Service).Spec.Ports[0].Port).To(Equal(int32(9001))) + Expect(objs[2].(*v1.Service).Spec.Ports[0].Port).To(Equal(int32(9001))) By("Creating the console plugin configmap") Eventually(getConfigMapData(configKey), timeout, interval).Should(ContainSubstring("url: http://loki:3100/")) By("Expecting to create console plugin role binding") - rb := objs[4].(*rbacv1.ClusterRoleBinding) + rb := objs[5].(*rbacv1.ClusterRoleBinding) Expect(rb.Subjects).Should(HaveLen(1)) Expect(rb.Subjects[0].Name).Should(Equal("netobserv-plugin")) Expect(rb.RoleRef.Name).Should(Equal("netobserv-plugin")) @@ -151,7 +137,7 @@ func flowCollectorConsolePluginSpecs() { By("Expecting the console plugin Deployment to be scaled up") Eventually(func() interface{} { dp := appsv1.Deployment{} - if err := k8sClient.Get(ctx, cpDepl.GetKey(cpNamespace), &dp); err != nil { + if err := k8sClient.Get(ctx, test.PluginDepl.GetKey(cpNamespace), &dp); err != nil { return err } return *dp.Spec.Replicas @@ -160,7 +146,7 @@ func flowCollectorConsolePluginSpecs() { By("Expecting the console plugin Service to be updated") Eventually(func() interface{} { svc := v1.Service{} - if err := k8sClient.Get(ctx, cpSvc.GetKey(cpNamespace), &svc); err != nil { + if err := k8sClient.Get(ctx, test.PluginSvc.GetKey(cpNamespace), &svc); err != nil { return err } return svc.Spec.Ports[0].Port @@ -170,7 +156,7 @@ func flowCollectorConsolePluginSpecs() { It("Should create desired objects when they're not found (e.g. case of an operator upgrade)", func() { // Manually delete ServiceMonitor By("Deleting ServiceMonitor") - Eventually(func() error { return k8sClient.Delete(ctx, cpSM.Resource) }, timeout, interval).Should(Succeed()) + Eventually(func() error { return k8sClient.Delete(ctx, test.PluginSM.Resource) }, timeout, interval).Should(Succeed()) // Do a dummy change that will trigger reconcile, and make sure SM is created again updateCR(crKey, func(fc *flowslatest.FlowCollector) { @@ -178,7 +164,7 @@ func flowCollectorConsolePluginSpecs() { }) By("Expecting ServiceMonitor to exist") - expectCreation(cpNamespace, cpSM) + expectPresence(cpNamespace, test.PluginSM) }) }) @@ -248,13 +234,7 @@ func flowCollectorConsolePluginSpecs() { fc.Spec.ConsolePlugin.Enable = ptr.To(false) }) - expectDeletion(cpNamespace, - cpDepl, - cpSvc, - cpSA, - cpCM, - cpSM, - ) + expectAbsence(cpNamespace, test.PluginResources...) }) It("Should recreate console plugin if enabled back", func() { @@ -262,25 +242,13 @@ func flowCollectorConsolePluginSpecs() { fc.Spec.ConsolePlugin.Enable = ptr.To(true) }) - expectCreation(cpNamespace, - cpDepl, - cpSvc, - cpSA, - cpCM, - cpSM, - ) + expectPresence(cpNamespace, test.PluginResources...) }) }) Context("Checking CR ownership", func() { It("Should be garbage collected", func() { - expectOwnership(cpNamespace, - cpDepl, - cpSvc, - cpSA, - cpCM, - cpSM, - ) + expectOwnership(cpNamespace, test.PluginResources...) }) }) @@ -294,21 +262,8 @@ func flowCollectorConsolePluginSpecs() { }) It("Should redeploy console plugin in new namespace", func() { - expectDeletion(cpNamespace, - cpDepl, - cpSvc, - cpSA, - cpCM, - cpSM, - ) - - expectCreation(otherNamespace, - cpDepl, - cpSvc, - cpSA, - cpCM, - cpSM, - ) + expectAbsence(cpNamespace, test.PluginResources...) + expectPresence(otherNamespace, test.PluginResources...) }) }) diff --git a/controllers/flowcollector_controller_ebpf_test.go b/controllers/flowcollector_controller_ebpf_test.go index 1eb452cca..3ac568789 100644 --- a/controllers/flowcollector_controller_ebpf_test.go +++ b/controllers/flowcollector_controller_ebpf_test.go @@ -33,15 +33,6 @@ func flowCollectorEBPFSpecs() { operatorNamespace2 := "namespace-ebpf-specs2" operatorPrivilegedNamespace2 := operatorNamespace2 + "-privileged" - dsRef := test.DaemonSet(constants.EBPFAgentName) - saRef := test.ServiceAccount(constants.EBPFServiceAccount) - svcMetricsRef := test.Service(constants.EBPFAgentMetricsSvcName) - svcFLPMetricsRef := test.Service("netobserv-ebpf-agent-prom") - smRef := test.ServiceMonitor(constants.EBPFAgentMetricsSvcMonitoringName) - smFLPRef := test.ServiceMonitor(constants.EBPFAgentName + "-monitor") - ruleFLPRef := test.PrometheusRule(constants.EBPFAgentName + "-alert") - nsRef := test.Namespace(operatorPrivilegedNamespace) - crKey := types.NamespacedName{Name: "cluster"} Context("Netobserv eBPF Agent Reconciler", func() { @@ -78,16 +69,8 @@ func flowCollectorEBPFSpecs() { return k8sClient.Create(ctx, desired) }).WithTimeout(timeout).WithPolling(interval).Should(Succeed()) - objs := expectCreation(operatorPrivilegedNamespace, - dsRef, - saRef, - svcMetricsRef, - svcFLPMetricsRef, - smRef, - smFLPRef, - ruleFLPRef, - nsRef, - ) + agentResourcesWithNS := append(test.AgentResources, test.AgentNS) + objs := expectPresence(operatorPrivilegedNamespace, agentResourcesWithNS...) Expect(objs).To(HaveLen(8)) spec := objs[0].(*appsv1.DaemonSet).Spec.Template.Spec @@ -171,14 +154,6 @@ func flowCollectorEBPFSpecs() { Expect(container.SecurityContext.Privileged).To(Not(BeNil())) Expect(*container.SecurityContext.Privileged).To(BeTrue()) Expect(container.SecurityContext.Capabilities).To(BeNil()) - - expectDeletion(operatorNamespace+"-privileged", - svcMetricsRef, - smRef, - svcFLPMetricsRef, - smFLPRef, - ruleFLPRef, - ) }) It("Should redeploy all when changing namespace", func() { @@ -186,21 +161,21 @@ func flowCollectorEBPFSpecs() { fc.Spec.Namespace = operatorNamespace2 }) - expectDeletion(operatorPrivilegedNamespace, - dsRef, - saRef, + expectAbsence(operatorPrivilegedNamespace, + test.AgentDS, + test.AgentSA, ) - expectCreation(operatorPrivilegedNamespace2, - dsRef, - saRef, + expectPresence(operatorPrivilegedNamespace2, + test.AgentDS, + test.AgentSA, ) }) It("Should be garbage collected", func() { expectOwnership(operatorPrivilegedNamespace2, - dsRef, + test.AgentDS, + test.AgentSA, test.Namespace(operatorPrivilegedNamespace2), - saRef, ) }) @@ -253,7 +228,7 @@ func flowCollectorEBPFKafkaSpecs() { } Expect(k8sClient.Create(ctx, descriptor)).Should(Succeed()) - objs := expectCreation(operatorPrivilegedNamespace, + objs := expectPresence(operatorPrivilegedNamespace, dsRef, saRef, ) @@ -270,7 +245,7 @@ func flowCollectorEBPFKafkaSpecs() { }) It("Should properly deploy flowlogs-pipeline", func() { - objs := expectCreation(operatorNamespace, + objs := expectPresence(operatorNamespace, flpRef, flpSvcRef, flpSMRef, diff --git a/controllers/flowcollector_controller_test.go b/controllers/flowcollector_controller_test.go index 7f216726f..dd9c6a806 100644 --- a/controllers/flowcollector_controller_test.go +++ b/controllers/flowcollector_controller_test.go @@ -1,12 +1,16 @@ package controllers import ( + . "github.com/onsi/ginkgo/v2" + operatorsv1 "github.com/openshift/api/operator/v1" + v1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" + "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client" flowslatest "github.com/netobserv/network-observability-operator/apis/flowcollector/v1beta2" "github.com/netobserv/network-observability-operator/pkg/test" - "github.com/onsi/ginkgo/v2" ) const ( @@ -21,16 +25,238 @@ var ( cleanupCR = func(key types.NamespacedName) { test.CleanupCR(ctx, k8sClient, key) } - expectCreation = func(namespace string, objs ...test.ResourceRef) []client.Object { - ginkgo.GinkgoHelper() - return test.ExpectCreation(ctx, k8sClient, namespace, objs...) + installResources = func(objs ...client.Object) []client.Object { + GinkgoHelper() + return test.InstallResources(ctx, k8sClient, objs) } - expectDeletion = func(namespace string, objs ...test.ResourceRef) { - ginkgo.GinkgoHelper() - test.ExpectDeletion(ctx, k8sClient, namespace, objs...) + cleanupResources = func(objs []client.Object) { + GinkgoHelper() + test.CleanupResources(ctx, k8sClient, objs) + } + expectPresence = func(namespace string, objs ...test.ResourceRef) []client.Object { + GinkgoHelper() + return test.ExpectPresence(ctx, k8sClient, namespace, objs...) + } + expectAbsence = func(namespace string, objs ...test.ResourceRef) { + GinkgoHelper() + test.ExpectAbsence(ctx, k8sClient, namespace, objs...) } expectOwnership = func(namespace string, objs ...test.ResourceRef) { - ginkgo.GinkgoHelper() + GinkgoHelper() test.ExpectOwnership(ctx, k8sClient, namespace, objs...) } ) + +type testCase struct { + name string + using *flowslatest.FlowCollectorSpec + expect []test.ResourceRef +} + +// nolint:cyclop +func checkInstalledResources() { + cases := []testCase{ + { + name: "Minimal CR", + using: &flowslatest.FlowCollectorSpec{}, + expect: []test.ResourceRef{ + test.AgentDS, + test.AgentSA, + test.AgentFLPMetricsSvc, + test.AgentFLPSM, + test.AgentFLPRule, + test.AgentFLPCRB, + test.AgentNS, + test.PluginDepl, + test.PluginCM, + test.PluginSvc, + test.PluginSA, + test.PluginCRB, + test.PluginSM, + }, + }, + { + name: "With agent metrics", + using: &flowslatest.FlowCollectorSpec{ + Agent: flowslatest.FlowCollectorAgent{ + EBPF: flowslatest.FlowCollectorEBPF{ + Metrics: flowslatest.EBPFMetrics{ + Enable: ptr.To(true), + }, + }, + }, + }, + expect: []test.ResourceRef{ + test.AgentDS, + test.AgentSA, + test.AgentFLPMetricsSvc, + test.AgentFLPSM, + test.AgentFLPRule, + test.AgentFLPCRB, + test.AgentMetricsSvc, + test.AgentSM, + test.AgentNS, + test.PluginDepl, + test.PluginCM, + test.PluginSvc, + test.PluginSA, + test.PluginCRB, + test.PluginSM, + }, + }, + { + name: "With Kafka", + using: &flowslatest.FlowCollectorSpec{ + DeploymentModel: flowslatest.DeploymentModelKafka, + }, + expect: []test.ResourceRef{ + test.AgentDS, + test.AgentSA, + test.AgentNS, + test.FLPDepl, + test.FLPCM, + test.FLPSA, + test.FLPMetricsSvc, + test.FLPSM, + test.FLPRule, + test.FLPCRB, + test.PluginDepl, + test.PluginCM, + test.PluginSvc, + test.PluginSA, + test.PluginCRB, + test.PluginSM, + }, + }, + { + name: "Without Console plugin", + using: &flowslatest.FlowCollectorSpec{ + DeploymentModel: flowslatest.DeploymentModelKafka, + ConsolePlugin: flowslatest.FlowCollectorConsolePlugin{ + Enable: ptr.To(false), + }, + }, + expect: []test.ResourceRef{ + test.AgentDS, + test.AgentSA, + test.AgentNS, + test.FLPDepl, + test.FLPCM, + test.FLPSA, + test.FLPMetricsSvc, + test.FLPSM, + test.FLPRule, + test.FLPCRB, + }, + }, + { + name: "With LokiStack", + using: &flowslatest.FlowCollectorSpec{ + Loki: flowslatest.FlowCollectorLoki{ + Mode: flowslatest.LokiModeLokiStack, + LokiStack: flowslatest.LokiStackRef{ + Name: "loki", + Namespace: "default", + }, + }, + }, + expect: []test.ResourceRef{ + test.AgentDS, + test.AgentSA, + test.AgentFLPMetricsSvc, + test.AgentFLPSM, + test.AgentFLPRule, + test.AgentFLPCRB, + test.AgentNS, + test.PluginDepl, + test.PluginCM, + test.PluginSvc, + test.PluginSA, + test.PluginCRB, + test.PluginSM, + test.LokiReaderCR, + test.LokiWriterCR, + test.LokiWriterCRB, + }, + }, + } + + var installed []client.Object + It("Should install initial resources", func() { + installed = installResources( + &operatorsv1.Console{ + ObjectMeta: metav1.ObjectMeta{Name: "cluster"}, + Spec: operatorsv1.ConsoleSpec{ + OperatorSpec: operatorsv1.OperatorSpec{ + ManagementState: operatorsv1.Unmanaged, + }, + }, + }, + &flowslatest.FlowCollector{ + ObjectMeta: metav1.ObjectMeta{Name: "cluster"}, + Spec: flowslatest.FlowCollectorSpec{Namespace: test.TestNamespace}, + }, + &v1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: "loki-gateway-ca-bundle", + Namespace: "default", + }, + Data: map[string]string{"service-ca.crt": "certificate data"}, + }, + ) + }) + + Context("Iterating over test cases", func() { + for _, c := range cases { + checkCase(c) + } + }) + + Context("Iterating in reverse order", func() { + for i := len(cases) - 1; i >= 0; i-- { + checkCase(cases[i]) + } + }) + + Context("Cleanup", func() { + It("Should delete initial resources", func() { + cleanupResources(installed) + }) + + It("Should cleanup other data", func() { + for _, obj := range test.ClusterResources { + _ = k8sClient.Delete(ctx, obj.Resource) + } + }) + }) +} + +func checkCase(c testCase) { + It("Running case: "+c.name, func() { + updateCR(types.NamespacedName{Name: "cluster"}, func(fc *flowslatest.FlowCollector) { + fc.Spec = *c.using + fc.Spec.Namespace = test.TestNamespace + }) + clusterResources := test.GetClusterResourcesIn(c.expect) + resourcesMainNamespace := append( + test.GetFLPResourcesIn(c.expect), + test.GetPluginResourcesIn(c.expect)..., + ) + resourcesPrivilegedNamespace := test.GetAgentResourcesIn(c.expect) + + // Ensure presence + expectPresence("", clusterResources...) + expectPresence(test.TestNamespace, resourcesMainNamespace...) + expectPresence(test.TestNamespace+"-privileged", resourcesPrivilegedNamespace...) + expectOwnership(test.TestNamespace, resourcesMainNamespace...) + expectOwnership(test.TestNamespace+"-privileged", resourcesPrivilegedNamespace...) + // Ensure absence + unusedResourcesMainNamespace := append( + test.GetFLPResourcesNotIn(c.expect), + test.GetPluginResourcesNotIn(c.expect)..., + ) + unusedResourcesPrivilegedNamespace := test.GetAgentResourcesNotIn(c.expect) + expectAbsence(test.TestNamespace, unusedResourcesMainNamespace...) + expectAbsence(test.TestNamespace+"-privileged", unusedResourcesPrivilegedNamespace...) + }) +} diff --git a/controllers/flp/flp_controller_test.go b/controllers/flp/flp_controller_test.go index 8d738d8c6..a2288328b 100644 --- a/controllers/flp/flp_controller_test.go +++ b/controllers/flp/flp_controller_test.go @@ -37,17 +37,17 @@ var ( cleanupCR = func(key types.NamespacedName) { test.CleanupCR(ctx, k8sClient, key) } - expectCreation = func(namespace string, objs ...test.ResourceRef) []client.Object { + expectPresence = func(namespace string, objs ...test.ResourceRef) []client.Object { GinkgoHelper() - return test.ExpectCreation(ctx, k8sClient, namespace, objs...) + return test.ExpectPresence(ctx, k8sClient, namespace, objs...) } - expectDeletion = func(namespace string, objs ...test.ResourceRef) { + expectAbsence = func(namespace string, objs ...test.ResourceRef) { GinkgoHelper() - test.ExpectDeletion(ctx, k8sClient, namespace, objs...) + test.ExpectAbsence(ctx, k8sClient, namespace, objs...) } - expectNoCreation = func(namespace string, objs ...test.ResourceRef) { + expectContinuedAbsence = func(namespace string, objs ...test.ResourceRef) { GinkgoHelper() - test.ExpectNoCreation(ctx, k8sClient, namespace, objs...) + test.ExpectContinuedAbsence(ctx, k8sClient, namespace, objs...) } expectOwnership = func(namespace string, objs ...test.ResourceRef) { GinkgoHelper() @@ -59,13 +59,11 @@ var ( func ControllerSpecs() { const operatorNamespace = "main-namespace" const otherNamespace = "other-namespace" - crKey := types.NamespacedName{ - Name: "cluster", + crKey := types.NamespacedName{Name: "cluster"} + flpResources := []test.ResourceRef{ + test.FLPDepl, test.FLPCM, test.FLPSA, test.FLPMetricsSvc, test.FLPSM, test.FLPRule, } - deplRef := test.Deployment(constants.FLPName) - cmRef := test.ConfigMap(constants.FLPName + "-config") - saRef := test.ServiceAccount(constants.FLPName) - crbRef := test.ClusterRoleBinding(constants.FLPName) + flpResourcesWithCRB := append(flpResources, test.FLPCRB) // Created objects to cleanup cleanupList := []client.Object{} @@ -115,11 +113,7 @@ func ControllerSpecs() { }) It("Should not create flowlogs-pipeline when using agent direct-flp", func() { - expectNoCreation(operatorNamespace, - deplRef, - cmRef, - test.DaemonSet(constants.FLPName), - ) + expectContinuedAbsence(operatorNamespace, test.FLPDepl, test.FLPCM, test.FLPDS) }) }) @@ -145,18 +139,13 @@ func ControllerSpecs() { var digest string It("Should deploy kafka transformer", func() { - objs := expectCreation(operatorNamespace, - deplRef, - cmRef, - saRef, - crbRef, - ) - Expect(objs).To(HaveLen(4)) + objs := expectPresence(operatorNamespace, flpResourcesWithCRB...) + Expect(objs).To(HaveLen(7)) depl = objs[0].(*appsv1.Deployment) digest = depl.Spec.Template.Annotations[constants.PodConfigurationDigest] Expect(digest).NotTo(BeEmpty()) - rb := objs[3].(*rbacv1.ClusterRoleBinding) + rb := objs[6].(*rbacv1.ClusterRoleBinding) Expect(rb.Subjects).Should(HaveLen(1)) Expect(rb.Subjects[0].Name).Should(Equal("flowlogs-pipeline")) Expect(rb.RoleRef.Name).Should(Equal("flowlogs-pipeline")) @@ -195,7 +184,7 @@ func ControllerSpecs() { By("CR updated", func() { Eventually(func() error { - err := k8sClient.Get(ctx, deplRef.GetKey(operatorNamespace), depl) + err := k8sClient.Get(ctx, test.FLPDepl.GetKey(operatorNamespace), depl) if err != nil { return err } @@ -213,7 +202,7 @@ func ControllerSpecs() { By("Expecting that the flowlogsPipeline.PodConfigurationDigest attribute has changed") Eventually(func() error { - if err := k8sClient.Get(ctx, deplRef.GetKey(operatorNamespace), depl); err != nil { + if err := k8sClient.Get(ctx, test.FLPDepl.GetKey(operatorNamespace), depl); err != nil { return err } return checkDigestUpdate(&digest, depl.Spec.Template.Annotations) @@ -246,7 +235,7 @@ func ControllerSpecs() { It("Should have HPA installed", func() { By("Expecting HPA to be created") Eventually(func() interface{} { - return k8sClient.Get(ctx, deplRef.GetKey(operatorNamespace), &hpa) + return k8sClient.Get(ctx, test.FLPDepl.GetKey(operatorNamespace), &hpa) }, timeout, interval).Should(Succeed()) Expect(*hpa.Spec.MinReplicas).To(Equal(int32(1))) Expect(hpa.Spec.MaxReplicas).To(Equal(int32(1))) @@ -261,7 +250,7 @@ func ControllerSpecs() { By("Changing the Horizontal Pod Autoscaler instance") Eventually(func() error { - if err := k8sClient.Get(ctx, deplRef.GetKey(operatorNamespace), &hpa); err != nil { + if err := k8sClient.Get(ctx, test.FLPDepl.GetKey(operatorNamespace), &hpa); err != nil { return err } if *hpa.Spec.MinReplicas != int32(2) || hpa.Spec.MaxReplicas != int32(2) || @@ -277,10 +266,10 @@ func ControllerSpecs() { Context("Checking monitoring resources", func() { It("Should create desired objects when they're not found (e.g. case of an operator upgrade)", func() { - objs := expectCreation(operatorNamespace, - test.Service("flowlogs-pipeline-prom"), - test.ServiceMonitor("flowlogs-pipeline-monitor"), - test.PrometheusRule("flowlogs-pipeline-alert"), + objs := expectPresence(operatorNamespace, + test.FLPMetricsSvc, + test.FLPSM, + test.FLPRule, ) Expect(objs).To(HaveLen(3)) sm := objs[1].(*monitoringv1.ServiceMonitor) @@ -298,7 +287,7 @@ func ControllerSpecs() { }) By("Expecting ServiceMonitor to exist") - expectCreation(operatorNamespace, test.ServiceMonitor("flowlogs-pipeline-monitor")) + expectPresence(operatorNamespace, test.ServiceMonitor("flowlogs-pipeline-monitor")) // Manually delete Rule By("Deleting prom rule") @@ -309,12 +298,12 @@ func ControllerSpecs() { fc.Spec.Processor.LogLevel = "debug" }) By("Expecting PrometheusRule to exist") - expectCreation(operatorNamespace, test.PrometheusRule("flowlogs-pipeline-alert")) + expectPresence(operatorNamespace, test.PrometheusRule("flowlogs-pipeline-alert")) }) }) Context("Using certificates with loki manual mode", func() { - flpKey := deplRef.GetKey(operatorNamespace) + flpKey := test.FLPDepl.GetKey(operatorNamespace) depl := appsv1.Deployment{} It("Should update Loki to use TLS", func() { // Create CM certificate @@ -369,7 +358,7 @@ func ControllerSpecs() { }) Context("Using certificates with loki distributed mode", func() { - flpKey := deplRef.GetKey(operatorNamespace) + flpKey := test.FLPDepl.GetKey(operatorNamespace) depl := appsv1.Deployment{} It("Should update Loki to use TLS", func() { // Create CM certificate @@ -428,7 +417,7 @@ func ControllerSpecs() { }) Context("Using certificates with loki monolithic mode", func() { - flpKey := deplRef.GetKey(operatorNamespace) + flpKey := test.FLPDepl.GetKey(operatorNamespace) depl := appsv1.Deployment{} It("Should update Loki to use TLS", func() { // Create CM certificate @@ -486,7 +475,7 @@ func ControllerSpecs() { }) Context("Using Certificates With Loki in LokiStack Mode", func() { - flpKey := deplRef.GetKey(operatorNamespace) + flpKey := test.FLPDepl.GetKey(operatorNamespace) depl := appsv1.Deployment{} It("Should update Loki config successfully", func() { // Create CM certificate @@ -565,20 +554,10 @@ func ControllerSpecs() { It("Should redeploy FLP in new namespace", func() { By("Expecting resources in previous namespace to be deleted") - expectDeletion(operatorNamespace, - deplRef, - cmRef, - saRef, - ) - - objs := expectCreation(otherNamespace, - deplRef, - cmRef, - saRef, - crbRef, - ) - Expect(objs).To(HaveLen(4)) - crb := objs[3].(*rbacv1.ClusterRoleBinding) + expectAbsence(operatorNamespace, flpResources...) + objs := expectPresence(otherNamespace, flpResourcesWithCRB...) + Expect(objs).To(HaveLen(7)) + crb := objs[6].(*rbacv1.ClusterRoleBinding) Expect(crb.Subjects).To(HaveLen(1)) Expect(crb.Subjects[0].Namespace).To(Equal(otherNamespace)) }) @@ -586,11 +565,7 @@ func ControllerSpecs() { Context("Checking CR ownership", func() { It("Should be garbage collected", func() { - expectOwnership(otherNamespace, - deplRef, - cmRef, - saRef, - ) + expectOwnership(otherNamespace, flpResources...) }) }) @@ -602,11 +577,7 @@ func ControllerSpecs() { }) It("Should delete kafka transformer", func() { - expectDeletion(otherNamespace, - deplRef, - cmRef, - saRef, - ) + expectAbsence(otherNamespace, flpResources...) }) }) @@ -617,9 +588,7 @@ func ControllerSpecs() { It("Should cleanup other data", func() { for _, obj := range cleanupList { - Eventually(func() error { - return k8sClient.Delete(ctx, obj) - }, timeout, interval).Should(Succeed()) + Eventually(func() error { return k8sClient.Delete(ctx, obj) }, timeout, interval).Should(Succeed()) } }) }) diff --git a/controllers/flp/flp_objects.go b/controllers/flp/flp_objects.go index 7c175b289..5442a2e1e 100644 --- a/controllers/flp/flp_objects.go +++ b/controllers/flp/flp_objects.go @@ -525,7 +525,7 @@ func rbacInfo(appName, saName, saNamespace string) (*corev1.ServiceAccount, *rba } crb := rbacv1.ClusterRoleBinding{ ObjectMeta: metav1.ObjectMeta{ - Name: constants.FLPName, + Name: appName, Labels: map[string]string{ appLabel: appName, }, diff --git a/controllers/monitoring/monitoring_controller_test.go b/controllers/monitoring/monitoring_controller_test.go index 528f193ea..602024673 100644 --- a/controllers/monitoring/monitoring_controller_test.go +++ b/controllers/monitoring/monitoring_controller_test.go @@ -30,13 +30,13 @@ var ( cleanupCR = func(key types.NamespacedName) { test.CleanupCR(ctx, k8sClient, key) } - expectCreation = func(namespace string, objs ...test.ResourceRef) []client.Object { + expectPresence = func(namespace string, objs ...test.ResourceRef) []client.Object { GinkgoHelper() - return test.ExpectCreation(ctx, k8sClient, namespace, objs...) + return test.ExpectPresence(ctx, k8sClient, namespace, objs...) } - expectDeletion = func(namespace string, objs ...test.ResourceRef) { + expectAbsence = func(namespace string, objs ...test.ResourceRef) { GinkgoHelper() - test.ExpectDeletion(ctx, k8sClient, namespace, objs...) + test.ExpectAbsence(ctx, k8sClient, namespace, objs...) } expectOwnership = func(namespace string, objs ...test.ResourceRef) { GinkgoHelper() @@ -73,7 +73,7 @@ func ControllerSpecs() { // Create Expect(k8sClient.Create(ctx, created)).Should(Succeed()) - objs := expectCreation("openshift-config-managed", + objs := expectPresence("openshift-config-managed", test.ConfigMap("grafana-dashboard-netobserv-flow-metrics"), test.ConfigMap("grafana-dashboard-netobserv-health"), ) @@ -101,7 +101,7 @@ func ControllerSpecs() { } }) - expectDeletion("openshift-config-managed", + expectAbsence("openshift-config-managed", test.ConfigMap("grafana-dashboard-netobserv-flow-metrics"), ) diff --git a/controllers/suite_test.go b/controllers/suite_test.go index d7261fe82..759edbd9e 100644 --- a/controllers/suite_test.go +++ b/controllers/suite_test.go @@ -50,6 +50,7 @@ func TestAPIs(t *testing.T) { // go test ./... runs always Ginkgo test suites in parallel and they would interfere // this way we make sure that both test sub-suites are executed serially var _ = Describe("FlowCollector Controller", Ordered, Serial, func() { + checkInstalledResources() flowCollectorConsolePluginSpecs() flowCollectorEBPFSpecs() flowCollectorEBPFKafkaSpecs() diff --git a/pkg/test/all_resources.go b/pkg/test/all_resources.go new file mode 100644 index 000000000..5ffe8b70e --- /dev/null +++ b/pkg/test/all_resources.go @@ -0,0 +1,113 @@ +package test + +import ( + "github.com/netobserv/network-observability-operator/controllers/constants" +) + +const ( + TestNamespace = "test-ns" +) + +var ( + // All managed resources + AgentDS = DaemonSet(constants.EBPFAgentName) + AgentSA = ServiceAccount(constants.EBPFServiceAccount) + AgentMetricsSvc = Service(constants.EBPFAgentMetricsSvcName) + AgentFLPMetricsSvc = Service("netobserv-ebpf-agent-prom") + AgentSM = ServiceMonitor(constants.EBPFAgentMetricsSvcMonitoringName) + AgentFLPSM = ServiceMonitor(constants.EBPFAgentName + "-monitor") + AgentFLPRule = PrometheusRule(constants.EBPFAgentName + "-alert") + AgentFLPCRB = ClusterRoleBinding(constants.EBPFAgentName) + AgentNS = Namespace(TestNamespace + "-privileged") + FLPDepl = Deployment(constants.FLPName) + FLPCM = ConfigMap(constants.FLPName + "-config") + FLPSA = ServiceAccount(constants.FLPName) + FLPCRB = ClusterRoleBinding(constants.FLPName) + FLPHPA = HPA(constants.FLPName) + FLPMetricsSvc = Service(constants.FLPName + "-prom") + FLPSM = ServiceMonitor(constants.FLPName + "-monitor") + FLPRule = PrometheusRule(constants.FLPName + "-alert") + PluginDepl = Deployment(constants.PluginName) + PluginCM = ConfigMap("console-plugin-config") + PluginSvc = Service(constants.PluginName) + PluginSA = ServiceAccount(constants.PluginName) + PluginCRB = ClusterRoleBinding(constants.PluginName) + PluginSM = ServiceMonitor(constants.PluginName) + LokiWriterCR = ClusterRole(constants.LokiCRWriter) + LokiReaderCR = ClusterRole(constants.LokiCRReader) + LokiWriterCRB = ClusterRoleBinding(constants.LokiCRBWriter) + + // Old resources + FLPDS = DaemonSet(constants.FLPName) + + ClusterResources = []ResourceRef{ + FLPCRB, AgentFLPCRB, AgentNS, PluginCRB, + LokiWriterCR, LokiReaderCR, LokiWriterCRB, + } + FLPResources = []ResourceRef{ + FLPDepl, FLPDS, FLPCM, FLPSA, FLPHPA, FLPMetricsSvc, FLPSM, FLPRule, + } + PluginResources = []ResourceRef{ + PluginDepl, PluginCM, PluginSvc, PluginSA, PluginSM, + } + AgentResources = []ResourceRef{ + AgentDS, AgentSA, AgentMetricsSvc, AgentFLPMetricsSvc, AgentSM, AgentFLPSM, AgentFLPRule, + } +) + +func GetClusterResourcesIn(used []ResourceRef) []ResourceRef { + return filter(used, ClusterResources) +} + +func GetAgentResourcesIn(used []ResourceRef) []ResourceRef { + return filter(used, AgentResources) +} + +func GetFLPResourcesIn(used []ResourceRef) []ResourceRef { + return filter(used, FLPResources) +} + +func GetPluginResourcesIn(used []ResourceRef) []ResourceRef { + return filter(used, PluginResources) +} + +func GetAgentResourcesNotIn(used []ResourceRef) []ResourceRef { + return getComplement(used, AgentResources) +} + +func GetFLPResourcesNotIn(used []ResourceRef) []ResourceRef { + return getComplement(used, FLPResources) +} + +func GetPluginResourcesNotIn(used []ResourceRef) []ResourceRef { + return getComplement(used, PluginResources) +} + +func filter(used []ResourceRef, among []ResourceRef) []ResourceRef { + var ret []ResourceRef + for _, r := range among { + if hasResource(r, used) { + ret = append(ret, r) + } + } + return ret +} + +func getComplement(used []ResourceRef, among []ResourceRef) []ResourceRef { + var compl []ResourceRef + for _, r := range among { + if !hasResource(r, used) { + compl = append(compl, r) + } + } + return compl +} + +func hasResource(toCheck ResourceRef, list []ResourceRef) bool { + for _, rUsed := range list { + if rUsed.kind == toCheck.kind && rUsed.name == toCheck.name { + return true + } + } + return false +} diff --git a/pkg/test/util.go b/pkg/test/util.go index 9b088de6c..cac16d1e3 100644 --- a/pkg/test/util.go +++ b/pkg/test/util.go @@ -8,6 +8,7 @@ import ( "github.com/onsi/gomega" monitoringv1 "github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring/v1" appsv1 "k8s.io/api/apps/v1" + ascv2 "k8s.io/api/autoscaling/v2" v1 "k8s.io/api/core/v1" rbacv1 "k8s.io/api/rbac/v1" "k8s.io/apimachinery/pkg/types" @@ -25,14 +26,33 @@ func (r *ResourceRef) GetKey(ns string) types.NamespacedName { return types.NamespacedName{Name: r.name, Namespace: ns} } -func ExpectCreation(ctx context.Context, k8sClient client.Client, namespace string, objs ...ResourceRef) []client.Object { +func InstallResources(ctx context.Context, k8sClient client.Client, toInstall []client.Object) []client.Object { + ginkgo.GinkgoHelper() + for _, obj := range toInstall { + gomega.Eventually(func() interface{} { + return k8sClient.Create(ctx, obj) + }).WithTimeout(Timeout).WithPolling(Interval).Should(gomega.Succeed()) + } + return toInstall +} + +func CleanupResources(ctx context.Context, k8sClient client.Client, resources []client.Object) { + ginkgo.GinkgoHelper() + for _, obj := range resources { + gomega.Eventually(func() interface{} { + return k8sClient.Delete(ctx, obj) + }).WithTimeout(Timeout).WithPolling(Interval).Should(gomega.Succeed()) + } +} + +func ExpectPresence(ctx context.Context, k8sClient client.Client, namespace string, objs ...ResourceRef) []client.Object { ginkgo.GinkgoHelper() var refs []client.Object for _, obj := range objs { refs = append(refs, obj.Resource) } for _, obj := range objs { - ginkgo.By(fmt.Sprintf(`Expecting to create "%s" %s`, obj.name, obj.kind)) + ginkgo.By(fmt.Sprintf(`Expecting to create "%s" %s in namespace %s`, obj.name, obj.kind, namespace)) gomega.Eventually(func() interface{} { return k8sClient.Get(ctx, types.NamespacedName{Name: obj.name, Namespace: namespace}, obj.Resource) }).WithTimeout(Timeout).WithPolling(Interval).Should(gomega.Succeed()) @@ -40,20 +60,20 @@ func ExpectCreation(ctx context.Context, k8sClient client.Client, namespace stri return refs } -func ExpectDeletion(ctx context.Context, k8sClient client.Client, namespace string, objs ...ResourceRef) { +func ExpectAbsence(ctx context.Context, k8sClient client.Client, namespace string, objs ...ResourceRef) { ginkgo.GinkgoHelper() for _, obj := range objs { - ginkgo.By(fmt.Sprintf(`Expecting to delete "%s" %s`, obj.name, obj.kind)) + ginkgo.By(fmt.Sprintf(`Expecting not to have "%s" %s in namespace %s`, obj.name, obj.kind, namespace)) gomega.Eventually(func() interface{} { return k8sClient.Get(ctx, types.NamespacedName{Name: obj.name, Namespace: namespace}, obj.Resource) }).WithTimeout(Timeout).WithPolling(Interval).Should(gomega.MatchError(fmt.Sprintf(`%s "%s" not found`, obj.pluralKind, obj.name))) } } -func ExpectNoCreation(ctx context.Context, k8sClient client.Client, namespace string, objs ...ResourceRef) { +func ExpectContinuedAbsence(ctx context.Context, k8sClient client.Client, namespace string, objs ...ResourceRef) { ginkgo.GinkgoHelper() for _, obj := range objs { - ginkgo.By(fmt.Sprintf(`Expecting to not create "%s" %s`, obj.name, obj.kind)) + ginkgo.By(fmt.Sprintf(`Expecting to have "%s" %s in namespace %s`, obj.name, obj.kind, namespace)) gomega.Consistently(func() error { return k8sClient.Get(ctx, types.NamespacedName{Name: obj.name, Namespace: namespace}, obj.Resource) }, ConsistentlyTimeout, ConsistentlyInterval).Should(gomega.MatchError(fmt.Sprintf(`%s "%s" not found`, obj.pluralKind, obj.name))) @@ -98,11 +118,11 @@ func DaemonSet(name string) ResourceRef { } func ClusterRole(name string) ResourceRef { - return ResourceRef{name: name, Resource: &rbacv1.ClusterRole{}, kind: "ClusterRole", pluralKind: "clusterroles"} + return ResourceRef{name: name, Resource: &rbacv1.ClusterRole{}, kind: "ClusterRole", pluralKind: "clusterroles.rbac.authorization.k8s.io"} } func ClusterRoleBinding(name string) ResourceRef { - return ResourceRef{name: name, Resource: &rbacv1.ClusterRoleBinding{}, kind: "ClusterRoleBinding", pluralKind: "clusterrolebindings"} + return ResourceRef{name: name, Resource: &rbacv1.ClusterRoleBinding{}, kind: "ClusterRoleBinding", pluralKind: "clusterrolebindings.rbac.authorization.k8s.io"} } func ServiceMonitor(name string) ResourceRef { @@ -112,3 +132,7 @@ func ServiceMonitor(name string) ResourceRef { func PrometheusRule(name string) ResourceRef { return ResourceRef{name: name, Resource: &monitoringv1.PrometheusRule{}, kind: "PrometheusRule", pluralKind: "prometheusrules.monitoring.coreos.com"} } + +func HPA(name string) ResourceRef { + return ResourceRef{name: name, Resource: &ascv2.HorizontalPodAutoscaler{}, kind: "HorizontalPodAutoscaler", pluralKind: "horizontalpodautoscalers.autoscaling"} +}