diff --git a/Makefile b/Makefile index 7937e98e23..dc276f9bac 100644 --- a/Makefile +++ b/Makefile @@ -34,19 +34,19 @@ endif # by default, do not run the manager with webhooks enabled. This only affects local runs, not the build or in-cluster deployments. ENABLE_WEBHOOKS ?= false -# If we are running in CI, run ginkgo with the recommended CI settings +# If we are running in CI, run go test in verbose mode ifeq (,$(CI)) -GINKGO_OPTS=-r +GOTEST_OPTS=-race else -GINKGO_OPTS=-r --randomizeAllSpecs --randomizeSuites --failOnPending --cover --trace --race --progress --compilers=2 -v +GOTEST_OPTS=-race -v endif all: manager ci: test # Run tests -test: ginkgo generate fmt vet manifests - $(GINKGO) $(GINKGO_OPTS) +test: generate fmt vet manifests + go test ${GOTEST_OPTS} ./... # Build manager binary manager: generate fmt vet @@ -125,19 +125,6 @@ else CONTROLLER_GEN=$(shell which controller-gen) endif -# find or download ginkgo -# download ginkgo if necessary -ginkgo: -ifeq (, $(shell which ginkgo)) - @{ \ - set -e ;\ - go get github.com/onsi/ginkgo/ginkgo@v1.14.2 ;\ - } -GINKGO=$(GOBIN)/ginkgo -else -GINKGO=$(shell which ginkgo) -endif - kustomize: ifeq (, $(shell which kustomize)) @{ \ diff --git a/controllers/opentelemetrycollector_controller_test.go b/controllers/opentelemetrycollector_controller_test.go index 6ec3a25791..6a8990bb85 100644 --- a/controllers/opentelemetrycollector_controller_test.go +++ b/controllers/opentelemetrycollector_controller_test.go @@ -4,9 +4,10 @@ import ( "context" "errors" "fmt" + "testing" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -24,210 +25,212 @@ import ( "github.com/open-telemetry/opentelemetry-operator/pkg/collector/reconcile" ) -var _ = Describe("OpenTelemetryCollector controller", func() { - logger := logf.Log.WithName("unit-tests") +var logger = logf.Log.WithName("unit-tests") + +func TestNewObjectsOnReconciliation(t *testing.T) { + // prepare cfg := config.New() - cfg.FlagSet().Parse([]string{}) //nolint errcheck - - It("should generate the underlying objects on reconciliation", func() { - // prepare - nsn := types.NamespacedName{Name: "my-instance", Namespace: "default"} - reconciler := controllers.NewReconciler(controllers.Params{ - Client: k8sClient, - Log: logger, - Scheme: testScheme, - Config: cfg, - }) - created := &v1alpha1.OpenTelemetryCollector{ - ObjectMeta: metav1.ObjectMeta{ - Name: nsn.Name, - Namespace: nsn.Namespace, - }, - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - Mode: v1alpha1.ModeDeployment, - }, - } - err := k8sClient.Create(context.Background(), created) - Expect(err).ToNot(HaveOccurred()) - - // test - req := k8sreconcile.Request{ - NamespacedName: nsn, - } - _, err = reconciler.Reconcile(req) - - // verify - Expect(err).ToNot(HaveOccurred()) - - // the base query for the underlying objects - opts := []client.ListOption{ - client.InNamespace(nsn.Namespace), - client.MatchingLabels(map[string]string{ - "app.kubernetes.io/instance": fmt.Sprintf("%s.%s", nsn.Namespace, nsn.Name), - "app.kubernetes.io/managed-by": "opentelemetry-operator", - }), - } - - // verify that we have at least one object for each of the types we create - // whether we have the right ones is up to the specific tests for each type - { - list := &corev1.ConfigMapList{} - err = k8sClient.List(context.Background(), list, opts...) - Expect(err).ToNot(HaveOccurred()) - Expect(list.Items).ToNot(BeEmpty()) - } - { - list := &corev1.ServiceAccountList{} - err = k8sClient.List(context.Background(), list, opts...) - Expect(err).ToNot(HaveOccurred()) - Expect(list.Items).ToNot(BeEmpty()) - } - { - list := &corev1.ServiceList{} - err = k8sClient.List(context.Background(), list, opts...) - Expect(err).ToNot(HaveOccurred()) - Expect(list.Items).ToNot(BeEmpty()) - } - { - list := &appsv1.DeploymentList{} - err = k8sClient.List(context.Background(), list, opts...) - Expect(err).ToNot(HaveOccurred()) - Expect(list.Items).ToNot(BeEmpty()) - } - { - list := &appsv1.DaemonSetList{} - err = k8sClient.List(context.Background(), list, opts...) - Expect(err).ToNot(HaveOccurred()) - // attention! we expect daemonsets to be empty in the default configuration - Expect(list.Items).To(BeEmpty()) - } - - // cleanup - Expect(k8sClient.Delete(context.Background(), created)).ToNot(HaveOccurred()) + nsn := types.NamespacedName{Name: "my-instance", Namespace: "default"} + reconciler := controllers.NewReconciler(controllers.Params{ + Client: k8sClient, + Log: logger, + Scheme: testScheme, + Config: cfg, }) - - It("should continue when a task's failure can be recovered", func() { - // prepare - taskCalled := false - reconciler := controllers.NewReconciler(controllers.Params{ - Log: logger, - Tasks: []controllers.Task{ - { - Name: "should-fail", - Do: func(context.Context, reconcile.Params) error { - return errors.New("should fail!") - }, - BailOnError: false, + created := &v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: nsn.Name, + Namespace: nsn.Namespace, + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Mode: v1alpha1.ModeDeployment, + }, + } + err := k8sClient.Create(context.Background(), created) + require.NoError(t, err) + + // test + req := k8sreconcile.Request{ + NamespacedName: nsn, + } + _, err = reconciler.Reconcile(req) + + // verify + require.NoError(t, err) + + // the base query for the underlying objects + opts := []client.ListOption{ + client.InNamespace(nsn.Namespace), + client.MatchingLabels(map[string]string{ + "app.kubernetes.io/instance": fmt.Sprintf("%s.%s", nsn.Namespace, nsn.Name), + "app.kubernetes.io/managed-by": "opentelemetry-operator", + }), + } + + // verify that we have at least one object for each of the types we create + // whether we have the right ones is up to the specific tests for each type + { + list := &corev1.ConfigMapList{} + err = k8sClient.List(context.Background(), list, opts...) + assert.NoError(t, err) + assert.NotEmpty(t, list.Items) + } + { + list := &corev1.ServiceAccountList{} + err = k8sClient.List(context.Background(), list, opts...) + assert.NoError(t, err) + assert.NotEmpty(t, list.Items) + } + { + list := &corev1.ServiceList{} + err = k8sClient.List(context.Background(), list, opts...) + assert.NoError(t, err) + assert.NotEmpty(t, list.Items) + } + { + list := &appsv1.DeploymentList{} + err = k8sClient.List(context.Background(), list, opts...) + assert.NoError(t, err) + assert.NotEmpty(t, list.Items) + } + { + list := &appsv1.DaemonSetList{} + err = k8sClient.List(context.Background(), list, opts...) + assert.NoError(t, err) + // attention! we expect daemonsets to be empty in the default configuration + assert.Empty(t, list.Items) + } + + // cleanup + require.NoError(t, k8sClient.Delete(context.Background(), created)) + +} + +func TestContinueOnRecoverableFailure(t *testing.T) { + // prepare + taskCalled := false + reconciler := controllers.NewReconciler(controllers.Params{ + Log: logger, + Tasks: []controllers.Task{ + { + Name: "should-fail", + Do: func(context.Context, reconcile.Params) error { + return errors.New("should fail!") }, - { - Name: "should-be-called", - Do: func(context.Context, reconcile.Params) error { - taskCalled = true - return nil - }, + BailOnError: false, + }, + { + Name: "should-be-called", + Do: func(context.Context, reconcile.Params) error { + taskCalled = true + return nil }, }, - }) + }, + }) - // test - err := reconciler.RunTasks(context.Background(), reconcile.Params{}) + // test + err := reconciler.RunTasks(context.Background(), reconcile.Params{}) - // verify - Expect(err).ToNot(HaveOccurred()) - Expect(taskCalled).To(BeTrue()) - }) + // verify + assert.NoError(t, err) + assert.True(t, taskCalled) +} - It("should not continue when a task's failure can't be recovered", func() { - // prepare - taskCalled := false - expectedErr := errors.New("should fail!") - nsn := types.NamespacedName{Name: "my-instance", Namespace: "default"} - reconciler := controllers.NewReconciler(controllers.Params{ - Client: k8sClient, - Log: logger, - Scheme: scheme.Scheme, - Config: cfg, - Tasks: []controllers.Task{ - { - Name: "should-fail", - Do: func(context.Context, reconcile.Params) error { - taskCalled = true - return expectedErr - }, - BailOnError: true, - }, - { - Name: "should-not-be-called", - Do: func(context.Context, reconcile.Params) error { - Fail("should not have been called") - return nil - }, +func TestBreakOnUnrecoverableError(t *testing.T) { + // prepare + cfg := config.New() + taskCalled := false + expectedErr := errors.New("should fail!") + nsn := types.NamespacedName{Name: "my-instance", Namespace: "default"} + reconciler := controllers.NewReconciler(controllers.Params{ + Client: k8sClient, + Log: logger, + Scheme: scheme.Scheme, + Config: cfg, + Tasks: []controllers.Task{ + { + Name: "should-fail", + Do: func(context.Context, reconcile.Params) error { + taskCalled = true + return expectedErr }, + BailOnError: true, }, - }) - created := &v1alpha1.OpenTelemetryCollector{ - ObjectMeta: metav1.ObjectMeta{ - Name: nsn.Name, - Namespace: nsn.Namespace, + { + Name: "should-not-be-called", + Do: func(context.Context, reconcile.Params) error { + assert.Fail(t, "should not have been called") + return nil + }, }, - } - err := k8sClient.Create(context.Background(), created) - Expect(err).ToNot(HaveOccurred()) - - // test - req := k8sreconcile.Request{ - NamespacedName: nsn, - } - _, err = reconciler.Reconcile(req) - - // verify - Expect(err).To(MatchError(expectedErr)) - Expect(taskCalled).To(BeTrue()) - - // cleanup - Expect(k8sClient.Delete(context.Background(), created)).ToNot(HaveOccurred()) + }, }) - - It("should skip when the instance doesn't exist", func() { - // prepare - nsn := types.NamespacedName{Name: "non-existing-my-instance", Namespace: "default"} - reconciler := controllers.NewReconciler(controllers.Params{ - Client: k8sClient, - Log: logger, - Scheme: scheme.Scheme, - Config: cfg, - Tasks: []controllers.Task{ - { - Name: "should-not-be-called", - Do: func(context.Context, reconcile.Params) error { - Fail("should not have been called") - return nil - }, + created := &v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: nsn.Name, + Namespace: nsn.Namespace, + }, + } + err := k8sClient.Create(context.Background(), created) + require.NoError(t, err) + + // test + req := k8sreconcile.Request{ + NamespacedName: nsn, + } + _, err = reconciler.Reconcile(req) + + // verify + assert.Equal(t, expectedErr, err) + assert.True(t, taskCalled) + + // cleanup + assert.NoError(t, k8sClient.Delete(context.Background(), created)) +} + +func TestSkipWhenInstanceDoesNotExist(t *testing.T) { + // prepare + cfg := config.New() + nsn := types.NamespacedName{Name: "non-existing-my-instance", Namespace: "default"} + reconciler := controllers.NewReconciler(controllers.Params{ + Client: k8sClient, + Log: logger, + Scheme: scheme.Scheme, + Config: cfg, + Tasks: []controllers.Task{ + { + Name: "should-not-be-called", + Do: func(context.Context, reconcile.Params) error { + assert.Fail(t, "should not have been called") + return nil }, }, - }) + }, + }) - // test - req := k8sreconcile.Request{ - NamespacedName: nsn, - } - _, err := reconciler.Reconcile(req) + // test + req := k8sreconcile.Request{ + NamespacedName: nsn, + } + _, err := reconciler.Reconcile(req) - // verify - Expect(err).ToNot(HaveOccurred()) - }) + // verify + assert.NoError(t, err) +} - It("should be able to register with the manager", func() { - Skip("this test requires a real cluster, otherwise the GetConfigOrDie will die") - // prepare - mgr, err := manager.New(k8sconfig.GetConfigOrDie(), manager.Options{}) - Expect(err).ToNot(HaveOccurred()) - reconciler := controllers.NewReconciler(controllers.Params{}) +func TestRegisterWithManager(t *testing.T) { + t.Skip("this test requires a real cluster, otherwise the GetConfigOrDie will die") - // test - err = reconciler.SetupWithManager(mgr) + // prepare + mgr, err := manager.New(k8sconfig.GetConfigOrDie(), manager.Options{}) + require.NoError(t, err) - // verify - Expect(err).ToNot(HaveOccurred()) - }) -}) + reconciler := controllers.NewReconciler(controllers.Params{}) + + // test + err = reconciler.SetupWithManager(mgr) + + // verify + assert.NoError(t, err) +} diff --git a/controllers/suite_test.go b/controllers/suite_test.go index 3fe32b3fa5..7b3cfdceff 100644 --- a/controllers/suite_test.go +++ b/controllers/suite_test.go @@ -15,67 +15,54 @@ package controllers_test import ( + "fmt" + "os" "path/filepath" "testing" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" "k8s.io/apimachinery/pkg/runtime" "k8s.io/client-go/kubernetes/scheme" - "k8s.io/client-go/rest" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/envtest" - "sigs.k8s.io/controller-runtime/pkg/envtest/printer" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/log/zap" "github.com/open-telemetry/opentelemetry-operator/api/v1alpha1" // +kubebuilder:scaffold:imports ) -// These tests use Ginkgo (BDD-style Go testing framework). Refer to -// http://onsi.github.io/ginkgo/ to learn more about Ginkgo. - -var cfg *rest.Config var k8sClient client.Client var testEnv *envtest.Environment var testScheme *runtime.Scheme = scheme.Scheme -func TestAPIs(t *testing.T) { - RegisterFailHandler(Fail) - - RunSpecsWithDefaultAndCustomReporters(t, - "Controller Suite", - []Reporter{printer.NewlineReporter{}}) -} - -var _ = BeforeSuite(func(done Done) { - logf.SetLogger(zap.LoggerTo(GinkgoWriter, true)) - - By("bootstrapping test environment") +func TestMain(m *testing.M) { testEnv = &envtest.Environment{ CRDDirectoryPaths: []string{filepath.Join("..", "config", "crd", "bases")}, } - var err error - cfg, err = testEnv.Start() - Expect(err).ToNot(HaveOccurred()) - Expect(cfg).ToNot(BeNil()) - - err = v1alpha1.AddToScheme(testScheme) - Expect(err).NotTo(HaveOccurred()) + cfg, err := testEnv.Start() + if err != nil { + fmt.Printf("failed to start testEnv: %v", err) + os.Exit(1) + } + if err := v1alpha1.AddToScheme(testScheme); err != nil { + fmt.Printf("failed to register scheme: %v", err) + os.Exit(1) + } // +kubebuilder:scaffold:scheme k8sClient, err = client.New(cfg, client.Options{Scheme: testScheme}) - Expect(err).ToNot(HaveOccurred()) - Expect(k8sClient).ToNot(BeNil()) + if err != nil { + fmt.Printf("failed to setup a Kubernetes client: %v", err) + os.Exit(1) + } - close(done) -}, 60) + code := m.Run() -var _ = AfterSuite(func() { - By("tearing down the test environment") - err := testEnv.Stop() - Expect(err).ToNot(HaveOccurred()) -}) + err = testEnv.Stop() + if err != nil { + fmt.Printf("failed to stop testEnv: %v", err) + os.Exit(1) + } + + os.Exit(code) +} diff --git a/go.mod b/go.mod index 233cdc9fb4..7a91ec9e9d 100644 --- a/go.mod +++ b/go.mod @@ -8,7 +8,9 @@ require ( github.com/onsi/ginkgo v1.14.2 // keep the Makefile in sync! github.com/onsi/gomega v1.10.3 github.com/spf13/pflag v1.0.5 + github.com/stretchr/testify v1.4.0 gopkg.in/yaml.v2 v2.3.0 + gotest.tools v2.2.0+incompatible k8s.io/api v0.19.3 k8s.io/apimachinery v0.19.3 k8s.io/client-go v0.19.3 diff --git a/go.sum b/go.sum index 33f376c441..b96560deaa 100644 --- a/go.sum +++ b/go.sum @@ -612,6 +612,7 @@ gopkg.in/yaml.v2 v2.2.5/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.3.0 h1:clyUAQHOM3G0M3f5vQj7LuJrETvjVot3Z5el9nffUtU= gopkg.in/yaml.v2 v2.3.0/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gotest.tools v2.2.0+incompatible h1:VsBPFP1AI068pPrMxtb/S8Zkgf9xEmTLJjfM+P5UIEo= gotest.tools v2.2.0+incompatible/go.mod h1:DsYFclhRJ6vuDpmuTbkuFWG+y2sxOXAzmJt81HFBacw= gotest.tools/v3 v3.0.2/go.mod h1:3SzNCllyD9/Y+b5r9JIKQ474KzkZyqLqEfYqMsX94Bk= honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/internal/config/config_suite_test.go b/internal/config/config_suite_test.go deleted file mode 100644 index c7d2e82ab5..0000000000 --- a/internal/config/config_suite_test.go +++ /dev/null @@ -1,13 +0,0 @@ -package config_test - -import ( - "testing" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -func TestConfig(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Config Suite") -} diff --git a/internal/config/main_test.go b/internal/config/main_test.go index 47a0fae98f..8159b22800 100644 --- a/internal/config/main_test.go +++ b/internal/config/main_test.go @@ -2,10 +2,11 @@ package config_test import ( "sync" + "testing" "time" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "github.com/open-telemetry/opentelemetry-operator/internal/config" "github.com/open-telemetry/opentelemetry-operator/internal/version" @@ -13,88 +14,85 @@ import ( "github.com/open-telemetry/opentelemetry-operator/pkg/platform" ) -var _ = Describe("Config", func() { - - It("should build new configuration with given options", func() { - // prepare - cfg := config.New( - config.WithCollectorImage("some-image"), - config.WithCollectorConfigMapEntry("some-config.yaml"), - config.WithPlatform(platform.Kubernetes), - ) - - // test - Expect(cfg.CollectorImage()).To(Equal("some-image")) - Expect(cfg.CollectorConfigMapEntry()).To(Equal("some-config.yaml")) - Expect(cfg.Platform()).To(Equal(platform.Kubernetes)) - }) - - It("should use the version as part of the default image", func() { - // prepare - v := version.Version{ - OpenTelemetryCollector: "the-version", - } - cfg := config.New(config.WithVersion(v)) - - // test - Expect(cfg.CollectorImage()).To(ContainSubstring("the-version")) - }) - - It("should callback when configuration changes happen", func() { - // prepare - calledBack := false - mock := &mockAutoDetect{ - PlatformFunc: func() (platform.Platform, error) { - return platform.OpenShift, nil - }, - } - cfg := config.New( - config.WithAutoDetect(mock), - config.WithOnChange(func() error { - calledBack = true - return nil - }), - ) - - // sanity check - Expect(cfg.Platform()).To(Equal(platform.Unknown)) - - // test - err := cfg.AutoDetect() - Expect(err).ToNot(HaveOccurred()) - - // verify - Expect(cfg.Platform()).To(Equal(platform.OpenShift)) - Expect(calledBack).To(BeTrue()) - }) - - It("should run the auto-detect routine in the background", func() { - // prepare - wg := &sync.WaitGroup{} - wg.Add(2) - mock := &mockAutoDetect{ - PlatformFunc: func() (platform.Platform, error) { - wg.Done() - // returning Unknown will cause the auto-detection to keep trying to detect the platform - return platform.Unknown, nil - }, - } - cfg := config.New( - config.WithAutoDetect(mock), - config.WithAutoDetectFrequency(100*time.Millisecond), - ) - - // sanity check - Expect(cfg.Platform()).To(Equal(platform.Unknown)) - - // test - err := cfg.StartAutoDetect() - Expect(err).ToNot(HaveOccurred()) - - // verify - wg.Wait() - }) -}) +func TestNewConfig(t *testing.T) { + // prepare + cfg := config.New( + config.WithCollectorImage("some-image"), + config.WithCollectorConfigMapEntry("some-config.yaml"), + config.WithPlatform(platform.Kubernetes), + ) + + // test + assert.Equal(t, "some-image", cfg.CollectorImage()) + assert.Equal(t, "some-config.yaml", cfg.CollectorConfigMapEntry()) + assert.Equal(t, platform.Kubernetes, cfg.Platform()) +} + +func TestOverrideVersion(t *testing.T) { + // prepare + v := version.Version{ + OpenTelemetryCollector: "the-version", + } + cfg := config.New(config.WithVersion(v)) + + // test + assert.Contains(t, cfg.CollectorImage(), "the-version") +} + +func TestCallbackOnChanges(t *testing.T) { + // prepare + calledBack := false + mock := &mockAutoDetect{ + PlatformFunc: func() (platform.Platform, error) { + return platform.OpenShift, nil + }, + } + cfg := config.New( + config.WithAutoDetect(mock), + config.WithOnChange(func() error { + calledBack = true + return nil + }), + ) + + // sanity check + require.Equal(t, platform.Unknown, cfg.Platform()) + + // test + err := cfg.AutoDetect() + require.NoError(t, err) + + // verify + assert.Equal(t, platform.OpenShift, cfg.Platform()) + assert.True(t, calledBack) +} + +func TestAutoDetectInBackground(t *testing.T) { + // prepare + wg := &sync.WaitGroup{} + wg.Add(2) + mock := &mockAutoDetect{ + PlatformFunc: func() (platform.Platform, error) { + wg.Done() + // returning Unknown will cause the auto-detection to keep trying to detect the platform + return platform.Unknown, nil + }, + } + cfg := config.New( + config.WithAutoDetect(mock), + config.WithAutoDetectFrequency(100*time.Millisecond), + ) + + // sanity check + require.Equal(t, platform.Unknown, cfg.Platform()) + + // test + err := cfg.StartAutoDetect() + require.NoError(t, err) + + // verify + wg.Wait() +} var _ autodetect.AutoDetect = (*mockAutoDetect)(nil) diff --git a/internal/podinjector/main_test.go b/internal/podinjector/main_test.go index 08163d928c..836113e5a8 100644 --- a/internal/podinjector/main_test.go +++ b/internal/podinjector/main_test.go @@ -4,9 +4,10 @@ import ( "context" "encoding/json" "net/http" + "testing" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" "k8s.io/api/admission/v1beta1" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -22,388 +23,389 @@ import ( "github.com/open-telemetry/opentelemetry-operator/pkg/sidecar" ) -var _ = Describe("PodInjector", func() { - logger := logf.Log.WithName("unit-tests") +var logger = logf.Log.WithName("unit-tests") - It("should inject an appropriate sidecar", func() { - for _, tt := range []struct { - name string - ns corev1.Namespace - pod corev1.Pod - otelcol v1alpha1.OpenTelemetryCollector - }{ - { - // this is the simplest positive test: a pod is being created with an annotation - // telling the operator to inject an instance, and the annotation's value contains - // the name of an existing otelcol instance with Mode=Sidecar - name: "simplest positive case", - ns: corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-namespace-simplest-positive-case", - }, +func TestShouldInjectSidecar(t *testing.T) { + for _, tt := range []struct { + name string + ns corev1.Namespace + pod corev1.Pod + otelcol v1alpha1.OpenTelemetryCollector + }{ + { + // this is the simplest positive test: a pod is being created with an annotation + // telling the operator to inject an instance, and the annotation's value contains + // the name of an existing otelcol instance with Mode=Sidecar + name: "simplest positive case", + ns: corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-namespace-simplest-positive-case", }, - pod: corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Annotations: map[string]string{sidecar.Annotation: "my-instance"}, - }, + }, + pod: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{sidecar.Annotation: "my-instance"}, }, - otelcol: v1alpha1.OpenTelemetryCollector{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-instance", - Namespace: "my-namespace-simplest-positive-case", - }, - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - Mode: v1alpha1.ModeSidecar, - }, + }, + otelcol: v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + Namespace: "my-namespace-simplest-positive-case", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Mode: v1alpha1.ModeSidecar, }, }, - { - // in this case, the annotation is at the namespace instead of at the pod - name: "namespace is annotated", - ns: corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-annotated-namespace", - Annotations: map[string]string{sidecar.Annotation: "my-instance"}, - }, + }, + { + // in this case, the annotation is at the namespace instead of at the pod + name: "namespace is annotated", + ns: corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-annotated-namespace", + Annotations: map[string]string{sidecar.Annotation: "my-instance"}, }, - pod: corev1.Pod{}, - otelcol: v1alpha1.OpenTelemetryCollector{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-instance", - Namespace: "my-annotated-namespace", - }, - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - Mode: v1alpha1.ModeSidecar, - }, + }, + pod: corev1.Pod{}, + otelcol: v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + Namespace: "my-annotated-namespace", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Mode: v1alpha1.ModeSidecar, }, }, - { - // now, we automatically select an existing otelcol - name: "auto-select basde on the annotation's value", - ns: corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-namespace-with-autoselect", - Annotations: map[string]string{sidecar.Annotation: "true"}, - }, + }, + { + // now, we automatically select an existing otelcol + name: "auto-select based on the annotation's value", + ns: corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-namespace-with-autoselect", + Annotations: map[string]string{sidecar.Annotation: "true"}, }, - pod: corev1.Pod{}, - otelcol: v1alpha1.OpenTelemetryCollector{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-instance", - Namespace: "my-namespace-with-autoselect", - }, - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - Mode: v1alpha1.ModeSidecar, - }, + }, + pod: corev1.Pod{}, + otelcol: v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + Namespace: "my-namespace-with-autoselect", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Mode: v1alpha1.ModeSidecar, }, }, - } { - When(tt.name, func() { - err := k8sClient.Create(context.Background(), &tt.ns) - Expect(err).ToNot(HaveOccurred()) + }, + } { + t.Run(tt.name, func(t *testing.T) { + err := k8sClient.Create(context.Background(), &tt.ns) + require.NoError(t, err) - err = k8sClient.Create(context.Background(), &tt.otelcol) - Expect(err).ToNot(HaveOccurred()) + err = k8sClient.Create(context.Background(), &tt.otelcol) + require.NoError(t, err) - encoded, err := json.Marshal(tt.pod) - Expect(err).ToNot(HaveOccurred()) + encoded, err := json.Marshal(tt.pod) + require.NoError(t, err) - // the actual request we see in the webhook - req := admission.Request{ - AdmissionRequest: v1beta1.AdmissionRequest{ - Namespace: tt.ns.Name, - Object: runtime.RawExtension{ - Raw: encoded, - }, + // the actual request we see in the webhook + req := admission.Request{ + AdmissionRequest: v1beta1.AdmissionRequest{ + Namespace: tt.ns.Name, + Object: runtime.RawExtension{ + Raw: encoded, }, - } + }, + } - // the webhook handler - cfg := config.New() - decoder, err := admission.NewDecoder(scheme.Scheme) - Expect(err).ToNot(HaveOccurred()) - injector := NewPodSidecarInjector(cfg, logger, k8sClient) - err = injector.InjectDecoder(decoder) - Expect(err).ToNot(HaveOccurred()) + // the webhook handler + cfg := config.New() + decoder, err := admission.NewDecoder(scheme.Scheme) + require.NoError(t, err) - // test - res := injector.Handle(context.Background(), req) + injector := NewPodSidecarInjector(cfg, logger, k8sClient) + err = injector.InjectDecoder(decoder) + require.NoError(t, err) - // verify - Expect(res.Allowed).To(BeTrue()) - Expect(res.AdmissionResponse.Result).To(BeNil()) - Expect(res.Patches).To(HaveLen(3)) + // test + res := injector.Handle(context.Background(), req) - expectedMap := map[string]bool{ - "/metadata/labels": false, - "/spec/volumes": false, - "/spec/containers": false, - } - for _, patch := range res.Patches { - Expect(patch.Operation).To(Equal("add")) - expectedMap[patch.Path] = true - } - for k := range expectedMap { - Expect(expectedMap[k]).To(BeTrue(), "patch with path %s not found", k) - } + // verify + assert.True(t, res.Allowed) + assert.Nil(t, res.AdmissionResponse.Result) + assert.Len(t, res.Patches, 3) - // cleanup - Expect(k8sClient.Delete(context.Background(), &tt.otelcol)).ToNot(HaveOccurred()) - Expect(k8sClient.Delete(context.Background(), &tt.ns)).ToNot(HaveOccurred()) - }) - } - }) + expectedMap := map[string]bool{ + "/metadata/labels": false, + "/spec/volumes": false, + "/spec/containers": false, + } + for _, patch := range res.Patches { + assert.Equal(t, "add", patch.Operation) + expectedMap[patch.Path] = true + } + for k := range expectedMap { + assert.True(t, expectedMap[k], "patch with path %s not found", k) + } - It("pod should not be changed", func() { - for _, tt := range []struct { - name string - ns corev1.Namespace - pod corev1.Pod - otelcols []v1alpha1.OpenTelemetryCollector - }{ - { - name: "namespace has no annotations", - ns: corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-namespace-no-annotations", - }, + // cleanup + require.NoError(t, k8sClient.Delete(context.Background(), &tt.otelcol)) + require.NoError(t, k8sClient.Delete(context.Background(), &tt.ns)) + }) + } +} + +func TestPodShouldNotBeChanged(t *testing.T) { + for _, tt := range []struct { + name string + ns corev1.Namespace + pod corev1.Pod + otelcols []v1alpha1.OpenTelemetryCollector + }{ + { + name: "namespace has no annotations", + ns: corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-namespace-no-annotations", + }, + }, + pod: corev1.Pod{}, + otelcols: []v1alpha1.OpenTelemetryCollector{{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + Namespace: "my-namespace-no-annotations", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Mode: v1alpha1.ModeSidecar, }, - pod: corev1.Pod{}, - otelcols: []v1alpha1.OpenTelemetryCollector{{ + }}, + }, + { + name: "multiple possible otelcols", + ns: corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-namespace-multiple-otelcols", + Annotations: map[string]string{sidecar.Annotation: "true"}, + }, + }, + pod: corev1.Pod{}, + otelcols: []v1alpha1.OpenTelemetryCollector{ + { ObjectMeta: metav1.ObjectMeta{ - Name: "my-instance", - Namespace: "my-namespace-no-annotations", + Name: "my-instance-1", + Namespace: "my-namespace-multiple-otelcols", }, Spec: v1alpha1.OpenTelemetryCollectorSpec{ Mode: v1alpha1.ModeSidecar, }, - }}, - }, - { - name: "multiple possible otelcols", - ns: corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-namespace-multiple-otelcols", - Annotations: map[string]string{sidecar.Annotation: "true"}, - }, }, - pod: corev1.Pod{}, - otelcols: []v1alpha1.OpenTelemetryCollector{ - { - ObjectMeta: metav1.ObjectMeta{ - Name: "my-instance-1", - Namespace: "my-namespace-multiple-otelcols", - }, - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - Mode: v1alpha1.ModeSidecar, - }, + { + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance-2", + Namespace: "my-namespace-multiple-otelcols", }, - { - ObjectMeta: metav1.ObjectMeta{ - Name: "my-instance-2", - Namespace: "my-namespace-multiple-otelcols", - }, - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - Mode: v1alpha1.ModeSidecar, - }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Mode: v1alpha1.ModeSidecar, }, }, }, - { - name: "no otelcols", - ns: corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-namespace-no-otelcols", - Annotations: map[string]string{sidecar.Annotation: "true"}, - }, + }, + { + name: "no otelcols", + ns: corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-namespace-no-otelcols", + Annotations: map[string]string{sidecar.Annotation: "true"}, }, - pod: corev1.Pod{}, - otelcols: []v1alpha1.OpenTelemetryCollector{}, }, - { - name: "otelcol is not a sidecar", - ns: corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-namespace-no-sidecar-otelcol", - Annotations: map[string]string{sidecar.Annotation: "my-instance"}, - }, + pod: corev1.Pod{}, + otelcols: []v1alpha1.OpenTelemetryCollector{}, + }, + { + name: "otelcol is not a sidecar", + ns: corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-namespace-no-sidecar-otelcol", + Annotations: map[string]string{sidecar.Annotation: "my-instance"}, }, - pod: corev1.Pod{}, - otelcols: []v1alpha1.OpenTelemetryCollector{{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-instance", - Namespace: "my-namespace-no-sidecar-otelcol", - }, - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - Mode: v1alpha1.ModeDaemonSet, - }, - }}, }, - { - name: "automatically injected otelcol is not a sidecar", - ns: corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-namespace-no-automatic-sidecar-otelcol", - Annotations: map[string]string{sidecar.Annotation: "true"}, - }, + pod: corev1.Pod{}, + otelcols: []v1alpha1.OpenTelemetryCollector{{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + Namespace: "my-namespace-no-sidecar-otelcol", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Mode: v1alpha1.ModeDaemonSet, + }, + }}, + }, + { + name: "automatically injected otelcol is not a sidecar", + ns: corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-namespace-no-automatic-sidecar-otelcol", + Annotations: map[string]string{sidecar.Annotation: "true"}, }, - pod: corev1.Pod{}, - otelcols: []v1alpha1.OpenTelemetryCollector{{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-instance", - Namespace: "my-namespace-no-automatic-sidecar-otelcol", - }, - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - Mode: v1alpha1.ModeDaemonSet, - }, - }}, }, - { - name: "pod has sidecar already", - ns: corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-namespace-pod-has-sidecar", - }, + pod: corev1.Pod{}, + otelcols: []v1alpha1.OpenTelemetryCollector{{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + Namespace: "my-namespace-no-automatic-sidecar-otelcol", }, - pod: corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Annotations: map[string]string{sidecar.Annotation: "my-instance"}, - }, - Spec: corev1.PodSpec{ - Containers: []corev1.Container{{ - Name: naming.Container(), - }}, - }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Mode: v1alpha1.ModeDaemonSet, + }, + }}, + }, + { + name: "pod has sidecar already", + ns: corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-namespace-pod-has-sidecar", }, - otelcols: []v1alpha1.OpenTelemetryCollector{{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-instance", - Namespace: "my-namespace-pod-has-sidecar", - }, - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - Mode: v1alpha1.ModeSidecar, - }, - }}, }, - { - name: "sidecar not desired", - ns: corev1.Namespace{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-namespace-sidecar-not-desired", - }, + pod: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{sidecar.Annotation: "my-instance"}, }, - pod: corev1.Pod{ - ObjectMeta: metav1.ObjectMeta{ - Annotations: map[string]string{sidecar.Annotation: "false"}, - }, + Spec: corev1.PodSpec{ + Containers: []corev1.Container{{ + Name: naming.Container(), + }}, + }, + }, + otelcols: []v1alpha1.OpenTelemetryCollector{{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + Namespace: "my-namespace-pod-has-sidecar", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Mode: v1alpha1.ModeSidecar, + }, + }}, + }, + { + name: "sidecar not desired", + ns: corev1.Namespace{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-namespace-sidecar-not-desired", }, - otelcols: []v1alpha1.OpenTelemetryCollector{{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-instance", - Namespace: "my-namespace-sidecar-not-desired", - }, - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - Mode: v1alpha1.ModeSidecar, - }, - }}, }, - } { - When(tt.name, func() { - err := k8sClient.Create(context.Background(), &tt.ns) - Expect(err).ToNot(HaveOccurred()) + pod: corev1.Pod{ + ObjectMeta: metav1.ObjectMeta{ + Annotations: map[string]string{sidecar.Annotation: "false"}, + }, + }, + otelcols: []v1alpha1.OpenTelemetryCollector{{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + Namespace: "my-namespace-sidecar-not-desired", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Mode: v1alpha1.ModeSidecar, + }, + }}, + }, + } { + t.Run(tt.name, func(t *testing.T) { + err := k8sClient.Create(context.Background(), &tt.ns) + require.NoError(t, err) - for i := range tt.otelcols { - err := k8sClient.Create(context.Background(), &tt.otelcols[i]) - Expect(err).ToNot(HaveOccurred()) - } + for i := range tt.otelcols { + err := k8sClient.Create(context.Background(), &tt.otelcols[i]) + require.NoError(t, err) + } - encoded, err := json.Marshal(tt.pod) - Expect(err).ToNot(HaveOccurred()) + encoded, err := json.Marshal(tt.pod) + require.NoError(t, err) - // the actual request we see in the webhook - req := admission.Request{ - AdmissionRequest: v1beta1.AdmissionRequest{ - Namespace: tt.ns.Name, - Object: runtime.RawExtension{ - Raw: encoded, - }, + // the actual request we see in the webhook + req := admission.Request{ + AdmissionRequest: v1beta1.AdmissionRequest{ + Namespace: tt.ns.Name, + Object: runtime.RawExtension{ + Raw: encoded, }, - } + }, + } - // the webhook handler - cfg := config.New() - decoder, err := admission.NewDecoder(scheme.Scheme) - Expect(err).ToNot(HaveOccurred()) - injector := NewPodSidecarInjector(cfg, logger, k8sClient) - err = injector.InjectDecoder(decoder) - Expect(err).ToNot(HaveOccurred()) + // the webhook handler + cfg := config.New() + decoder, err := admission.NewDecoder(scheme.Scheme) + require.NoError(t, err) - // test - res := injector.Handle(context.Background(), req) + injector := NewPodSidecarInjector(cfg, logger, k8sClient) + err = injector.InjectDecoder(decoder) + require.NoError(t, err) - // verify - Expect(res.Allowed).To(BeTrue()) - Expect(res.AdmissionResponse.Result).To(BeNil()) - Expect(res.Patches).To(HaveLen(0)) + // test + res := injector.Handle(context.Background(), req) - // cleanup - for i := range tt.otelcols { - Expect(k8sClient.Delete(context.Background(), &tt.otelcols[i])).ToNot(HaveOccurred()) - } - Expect(k8sClient.Delete(context.Background(), &tt.ns)).ToNot(HaveOccurred()) - }) - } - }) + // verify + assert.True(t, res.Allowed) + assert.Nil(t, res.AdmissionResponse.Result) + assert.Len(t, res.Patches, 0) - It("it should fail when the request is invalid", func() { - // we use a typical Go table-test instad of Ginkgo's DescribeTable because we need to - // do an assertion during the declaration of the table params, which isn't supported (yet?) - for _, tt := range []struct { - name string - req admission.Request - expected int32 - }{ - { - "empty payload", - admission.Request{}, - http.StatusBadRequest, - }, - { - "namespace doesn't exist", - func() admission.Request { - pod := corev1.Pod{} - encoded, err := json.Marshal(pod) - Expect(err).ToNot(HaveOccurred()) + // cleanup + for i := range tt.otelcols { + require.NoError(t, k8sClient.Delete(context.Background(), &tt.otelcols[i])) + } + require.NoError(t, k8sClient.Delete(context.Background(), &tt.ns)) + }) + } +} + +func TestFailOnInvalidRequest(t *testing.T) { + // we use a typical Go table-test instad of Ginkgo's DescribeTable because we need to + // do an assertion during the declaration of the table params, which isn't supported (yet?) + for _, tt := range []struct { + name string + req admission.Request + expected int32 + }{ + { + "empty payload", + admission.Request{}, + http.StatusBadRequest, + }, + { + "namespace doesn't exist", + func() admission.Request { + pod := corev1.Pod{} + encoded, err := json.Marshal(pod) + require.NoError(t, err) - return admission.Request{ - AdmissionRequest: v1beta1.AdmissionRequest{ - Namespace: "non-existing", - Object: runtime.RawExtension{ - Raw: encoded, - }, + return admission.Request{ + AdmissionRequest: v1beta1.AdmissionRequest{ + Namespace: "non-existing", + Object: runtime.RawExtension{ + Raw: encoded, }, - } - }(), - http.StatusInternalServerError, - }, - } { - When(tt.name, func() { - // prepare - cfg := config.New() - decoder, err := admission.NewDecoder(scheme.Scheme) - Expect(err).ToNot(HaveOccurred()) - injector := NewPodSidecarInjector(cfg, logger, k8sClient) - err = injector.InjectDecoder(decoder) - Expect(err).ToNot(HaveOccurred()) + }, + } + }(), + http.StatusInternalServerError, + }, + } { + t.Run(tt.name, func(t *testing.T) { + // prepare + cfg := config.New() + decoder, err := admission.NewDecoder(scheme.Scheme) + require.NoError(t, err) + + injector := NewPodSidecarInjector(cfg, logger, k8sClient) + err = injector.InjectDecoder(decoder) + require.NoError(t, err) - // test - res := injector.Handle(context.Background(), tt.req) + // test + res := injector.Handle(context.Background(), tt.req) - // verify - Expect(res.Allowed).To(BeFalse()) - Expect(res.AdmissionResponse.Result).ToNot(BeNil()) - Expect(res.AdmissionResponse.Result.Code).To(Equal(tt.expected)) - }) - } - }) -}) + // verify + assert.False(t, res.Allowed) + assert.NotNil(t, res.AdmissionResponse.Result) + assert.Equal(t, tt.expected, res.AdmissionResponse.Result.Code) + }) + } +} diff --git a/internal/podinjector/podinjector_suite_test.go b/internal/podinjector/podinjector_suite_test.go index fe179f3f18..5ba5b73498 100644 --- a/internal/podinjector/podinjector_suite_test.go +++ b/internal/podinjector/podinjector_suite_test.go @@ -1,57 +1,68 @@ +// Copyright The OpenTelemetry Authors +// +// 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 podinjector_test import ( + "fmt" + "os" "path/filepath" "testing" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - "k8s.io/client-go/rest" - "k8s.io/kubectl/pkg/scheme" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes/scheme" "sigs.k8s.io/controller-runtime/pkg/client" "sigs.k8s.io/controller-runtime/pkg/envtest" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/log/zap" "github.com/open-telemetry/opentelemetry-operator/api/v1alpha1" + // +kubebuilder:scaffold:imports ) -var cfg *rest.Config var k8sClient client.Client var testEnv *envtest.Environment +var testScheme *runtime.Scheme = scheme.Scheme -func TestPodinjector(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Podinjector Suite") -} - -var _ = BeforeSuite(func(done Done) { - logf.SetLogger(zap.LoggerTo(GinkgoWriter, true)) - - By("bootstrapping test environment") +func TestMain(m *testing.M) { testEnv = &envtest.Environment{ CRDDirectoryPaths: []string{filepath.Join("..", "..", "config", "crd", "bases")}, } - var err error - cfg, err = testEnv.Start() - Expect(err).ToNot(HaveOccurred()) - Expect(cfg).ToNot(BeNil()) - - err = v1alpha1.AddToScheme(scheme.Scheme) - Expect(err).NotTo(HaveOccurred()) + cfg, err := testEnv.Start() + if err != nil { + fmt.Printf("failed to start testEnv: %v", err) + os.Exit(1) + } + if err := v1alpha1.AddToScheme(testScheme); err != nil { + fmt.Printf("failed to register scheme: %v", err) + os.Exit(1) + } // +kubebuilder:scaffold:scheme - k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) - Expect(err).ToNot(HaveOccurred()) - Expect(k8sClient).ToNot(BeNil()) + k8sClient, err = client.New(cfg, client.Options{Scheme: testScheme}) + if err != nil { + fmt.Printf("failed to setup a Kubernetes client: %v", err) + os.Exit(1) + } - close(done) -}, 60) + code := m.Run() -var _ = AfterSuite(func() { - By("tearing down the test environment") - err := testEnv.Stop() - Expect(err).ToNot(HaveOccurred()) -}) + err = testEnv.Stop() + if err != nil { + fmt.Printf("failed to stop testEnv: %v", err) + os.Exit(1) + } + + os.Exit(code) +} diff --git a/internal/version/main_test.go b/internal/version/main_test.go index cb2ec2b0d6..682a2cfc9d 100644 --- a/internal/version/main_test.go +++ b/internal/version/main_test.go @@ -1,23 +1,22 @@ package version import ( - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" + "testing" + + "github.com/stretchr/testify/assert" ) -var _ = Describe("Version management", func() { - It("should have a fallback version", func() { - Expect(OpenTelemetryCollector()).To(Equal("0.0.0")) - }) +func TestFallbackVersion(t *testing.T) { + assert.Equal(t, "0.0.0", OpenTelemetryCollector()) +} - It("should use a version set during the build", func() { - // prepare - otelCol = "0.0.2" // set during the build - defer func() { - otelCol = "" - }() +func TestVersionFromBuild(t *testing.T) { + // prepare + otelCol = "0.0.2" // set during the build + defer func() { + otelCol = "" + }() - Expect(OpenTelemetryCollector()).To(Equal(otelCol)) - Expect(Get()).To(ContainSubstring(otelCol)) - }) -}) + assert.Equal(t, otelCol, OpenTelemetryCollector()) + assert.Contains(t, Get().String(), otelCol) +} diff --git a/internal/version/version_suite_test.go b/internal/version/version_suite_test.go deleted file mode 100644 index fa25d588f4..0000000000 --- a/internal/version/version_suite_test.go +++ /dev/null @@ -1,13 +0,0 @@ -package version_test - -import ( - "testing" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -func TestVersion(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Version Suite") -} diff --git a/pkg/autodetect/autodetect_suite_test.go b/pkg/autodetect/autodetect_suite_test.go deleted file mode 100644 index 513ad24f67..0000000000 --- a/pkg/autodetect/autodetect_suite_test.go +++ /dev/null @@ -1,13 +0,0 @@ -package autodetect_test - -import ( - "testing" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -func TestAutodetect(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Autodetect Suite") -} diff --git a/pkg/autodetect/main_test.go b/pkg/autodetect/main_test.go index a90fbe1f9e..7855315ac9 100644 --- a/pkg/autodetect/main_test.go +++ b/pkg/autodetect/main_test.go @@ -4,10 +4,10 @@ import ( "encoding/json" "net/http" "net/http/httptest" + "testing" - . "github.com/onsi/ginkgo" - . "github.com/onsi/ginkgo/extensions/table" - . "github.com/onsi/gomega" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/rest" @@ -15,55 +15,62 @@ import ( "github.com/open-telemetry/opentelemetry-operator/pkg/platform" ) -var _ = Describe("Autodetect", func() { - DescribeTable("detect platform based on available API groups", - func(expected platform.Platform, apiGroupList *metav1.APIGroupList) { - server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - output, err := json.Marshal(apiGroupList) - Expect(err).ToNot(HaveOccurred()) - - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(http.StatusOK) - _, err = w.Write(output) - Expect(err).ToNot(HaveOccurred()) - })) - defer server.Close() - - autoDetect, err := autodetect.New(&rest.Config{Host: server.URL}) - Expect(err).ToNot(HaveOccurred()) - - // test - plt, err := autoDetect.Platform() - - // verify - Expect(err).ToNot(HaveOccurred()) - Expect(plt).To(Equal(expected)) +func TestDetectPlatformBasedOnAvailableAPIGroups(t *testing.T) { + for _, tt := range []struct { + apiGroupList *metav1.APIGroupList + expected platform.Platform + }{ + { + &metav1.APIGroupList{}, + platform.Kubernetes, }, - - Entry("kubernetes", platform.Kubernetes, &metav1.APIGroupList{}), - Entry("openshift", platform.OpenShift, &metav1.APIGroupList{ - Groups: []metav1.APIGroup{ - { - Name: "route.openshift.io", + { + &metav1.APIGroupList{ + Groups: []metav1.APIGroup{ + { + Name: "route.openshift.io", + }, }, }, - }), - ) - - It("should return unknown platform when errors occur", func() { + platform.OpenShift, + }, + } { server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { - w.WriteHeader(http.StatusInternalServerError) + output, err := json.Marshal(tt.apiGroupList) + require.NoError(t, err) + + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(http.StatusOK) + _, err = w.Write(output) + require.NoError(t, err) })) defer server.Close() autoDetect, err := autodetect.New(&rest.Config{Host: server.URL}) - Expect(err).ToNot(HaveOccurred()) + require.NoError(t, err) // test plt, err := autoDetect.Platform() // verify - Expect(err).To(HaveOccurred()) - Expect(plt).To(Equal(platform.Unknown)) - }) -}) + assert.NoError(t, err) + assert.Equal(t, tt.expected, plt) + } +} + +func TestUnknownPlatformOnError(t *testing.T) { + server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { + w.WriteHeader(http.StatusInternalServerError) + })) + defer server.Close() + + autoDetect, err := autodetect.New(&rest.Config{Host: server.URL}) + require.NoError(t, err) + + // test + plt, err := autoDetect.Platform() + + // verify + assert.Error(t, err) + assert.Equal(t, platform.Unknown, plt) +} diff --git a/pkg/collector/adapters/adapters_suite_test.go b/pkg/collector/adapters/adapters_suite_test.go deleted file mode 100644 index 75c8c3c76b..0000000000 --- a/pkg/collector/adapters/adapters_suite_test.go +++ /dev/null @@ -1,13 +0,0 @@ -package adapters_test - -import ( - "testing" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -func TestAdapters(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Adapters Suite") -} diff --git a/pkg/collector/adapters/config_from_test.go b/pkg/collector/adapters/config_from_test.go index b87fea6fa9..cc2081151c 100644 --- a/pkg/collector/adapters/config_from_test.go +++ b/pkg/collector/adapters/config_from_test.go @@ -1,28 +1,25 @@ package adapters_test import ( - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" + "testing" + + "github.com/stretchr/testify/assert" "github.com/open-telemetry/opentelemetry-operator/pkg/collector/adapters" ) -var _ = Describe("ConfigFromString", func() { - Describe("Invalid YAML", func() { - It("should return an error", func() { - // test - config, err := adapters.ConfigFromString("🦄") +func TestInvalidYAML(t *testing.T) { + // test + config, err := adapters.ConfigFromString("🦄") - // verify - Expect(config).To(BeNil()) - Expect(err).To(MatchError(adapters.ErrInvalidYAML)) - }) - }) + // verify + assert.Nil(t, config) + assert.Equal(t, adapters.ErrInvalidYAML, err) +} - Describe("Empty string", func() { - It("should return an empty config", func() { - // test and verify - Expect(adapters.ConfigFromString("")).To(BeEmpty()) - }) - }) -}) +func TestEmptyString(t *testing.T) { + // test and verify + res, err := adapters.ConfigFromString("") + assert.NoError(t, err) + assert.Empty(t, res, 0) +} diff --git a/pkg/collector/adapters/config_to_ports_test.go b/pkg/collector/adapters/config_to_ports_test.go index d36240ad65..f2d0ceb8fb 100644 --- a/pkg/collector/adapters/config_to_ports_test.go +++ b/pkg/collector/adapters/config_to_ports_test.go @@ -2,11 +2,11 @@ package adapters_test import ( "errors" + "testing" "github.com/go-logr/logr" - . "github.com/onsi/ginkgo" - . "github.com/onsi/ginkgo/extensions/table" - . "github.com/onsi/gomega" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" corev1 "k8s.io/api/core/v1" v1 "k8s.io/api/core/v1" logf "sigs.k8s.io/controller-runtime/pkg/log" @@ -15,12 +15,10 @@ import ( "github.com/open-telemetry/opentelemetry-operator/pkg/collector/parser" ) -var _ = Describe("ConfigToReceiverPorts", func() { - logger := logf.Log.WithName("unit-tests") +var logger = logf.Log.WithName("unit-tests") - Describe("Extract ports from config", func() { - It("extract all known ports", func() { - configStr := `receivers: +func TestExtractPortsFromConfig(t *testing.T) { + configStr := `receivers: examplereceiver: endpoint: "0.0.0.0:12345" examplereceiver/settings: @@ -43,114 +41,136 @@ var _ = Describe("ConfigToReceiverPorts", func() { endpoint: 0.0.0.0:15268 ` - // prepare - config, err := adapters.ConfigFromString(configStr) - Expect(config).ToNot(BeEmpty()) - Expect(err).ToNot(HaveOccurred()) - - // test - ports, err := adapters.ConfigToReceiverPorts(logger, config) - Expect(ports).To(HaveLen(6)) - Expect(err).ToNot(HaveOccurred()) - - // verify - expectedPorts := map[int32]bool{} - expectedPorts[int32(12345)] = false - expectedPorts[int32(12346)] = false - expectedPorts[int32(14250)] = false - expectedPorts[int32(6831)] = false - expectedPorts[int32(6833)] = false - expectedPorts[int32(15268)] = false - - expectedNames := map[string]bool{} - expectedNames["examplereceiver"] = false - expectedNames["examplereceiver-settings"] = false - expectedNames["jaeger-grpc"] = false - expectedNames["jaeger-thrift-compact"] = false - expectedNames["jaeger-thrift-binary"] = false - expectedNames["jaeger-custom-thrift-http"] = false - - // make sure we only have the ports in the set - for _, port := range ports { - Expect(expectedPorts).To(HaveKey(port.Port)) - Expect(expectedNames).To(HaveKey(port.Name)) - expectedPorts[port.Port] = true - expectedNames[port.Name] = true - } - - // and make sure all the ports from the set are there - for _, val := range expectedPorts { - Expect(val).To(BeTrue()) - } - - }) - }) + // prepare + config, err := adapters.ConfigFromString(configStr) + require.NoError(t, err) + require.NotEmpty(t, config) + + // test + ports, err := adapters.ConfigToReceiverPorts(logger, config) + assert.NoError(t, err) + assert.Len(t, ports, 6) + + // verify + expectedPorts := map[int32]bool{} + expectedPorts[int32(12345)] = false + expectedPorts[int32(12346)] = false + expectedPorts[int32(14250)] = false + expectedPorts[int32(6831)] = false + expectedPorts[int32(6833)] = false + expectedPorts[int32(15268)] = false + + expectedNames := map[string]bool{} + expectedNames["examplereceiver"] = false + expectedNames["examplereceiver-settings"] = false + expectedNames["jaeger-grpc"] = false + expectedNames["jaeger-thrift-compact"] = false + expectedNames["jaeger-thrift-binary"] = false + expectedNames["jaeger-custom-thrift-http"] = false + + // make sure we only have the ports in the set + for _, port := range ports { + assert.NotNil(t, expectedPorts[port.Port]) + assert.NotNil(t, expectedNames[port.Name]) + expectedPorts[port.Port] = true + expectedNames[port.Name] = true + } - DescribeTable("No ports are parsed", - func(configStr string, expected error) { - // prepare - config, err := adapters.ConfigFromString(configStr) - Expect(err).ToNot(HaveOccurred()) + // and make sure all the ports from the set are there + for _, val := range expectedPorts { + assert.True(t, val) + } - // test - ports, err := adapters.ConfigToReceiverPorts(logger, config) +} - // verify - Expect(ports).To(BeNil()) - Expect(err).To(MatchError(expected)) +func TestNoPortsParsed(t *testing.T) { + for _, tt := range []struct { + desc string + configStr string + expected error + }{ + { + "empty", + "", + adapters.ErrNoReceivers, }, - Entry("empty", "", adapters.ErrNoReceivers), - Entry("not a map", "receivers: some-string", adapters.ErrReceiversNotAMap), - ) - - DescribeTable("Invalid receivers", - func(configStr string) { + { + "not a map", + "receivers: some-string", + adapters.ErrReceiversNotAMap, + }, + } { + t.Run(tt.desc, func(t *testing.T) { // prepare - config, err := adapters.ConfigFromString(configStr) - Expect(err).ToNot(HaveOccurred()) + config, err := adapters.ConfigFromString(tt.configStr) + require.NoError(t, err) // test ports, err := adapters.ConfigToReceiverPorts(logger, config) // verify - Expect(ports).To(HaveLen(0)) - Expect(err).ToNot(HaveOccurred()) - }, - Entry("receiver isn't a map", "receivers:\n some-receiver: string"), - Entry("receiver's endpoint isn't string", "receivers:\n some-receiver:\n endpoint: 123"), - ) + assert.Nil(t, ports) + assert.Equal(t, tt.expected, err) + }) + } +} - Describe("Parser failed", func() { - It("should return an empty list of ports", func() { +func TestInvalidReceivers(t *testing.T) { + for _, tt := range []struct { + desc string + configStr string + }{ + { + "receiver isn't a map", + "receivers:\n some-receiver: string", + }, + { + "receiver's endpoint isn't string", + "receivers:\n some-receiver:\n endpoint: 123", + }, + } { + t.Run(tt.desc, func(t *testing.T) { // prepare - mockParserCalled := false - mockParser := &mockParser{ - portsFunc: func() ([]v1.ServicePort, error) { - mockParserCalled = true - return nil, errors.New("mocked error") - }, - } - parser.Register("mock", func(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ReceiverParser { - return mockParser - }) - - config := map[interface{}]interface{}{ - "receivers": map[interface{}]interface{}{ - "mock": map[interface{}]interface{}{}, - }, - } + config, err := adapters.ConfigFromString(tt.configStr) + require.NoError(t, err) // test ports, err := adapters.ConfigToReceiverPorts(logger, config) // verify - Expect(ports).To(HaveLen(0)) - Expect(err).ToNot(HaveOccurred()) - Expect(mockParserCalled).To(BeTrue()) + assert.NoError(t, err) + assert.Len(t, ports, 0) }) + } +} + +func TestParserFailed(t *testing.T) { + // prepare + mockParserCalled := false + mockParser := &mockParser{ + portsFunc: func() ([]v1.ServicePort, error) { + mockParserCalled = true + return nil, errors.New("mocked error") + }, + } + parser.Register("mock", func(logger logr.Logger, name string, config map[interface{}]interface{}) parser.ReceiverParser { + return mockParser }) -}) + config := map[interface{}]interface{}{ + "receivers": map[interface{}]interface{}{ + "mock": map[interface{}]interface{}{}, + }, + } + + // test + ports, err := adapters.ConfigToReceiverPorts(logger, config) + + // verify + assert.Len(t, ports, 0) + assert.NoError(t, err) + assert.True(t, mockParserCalled) +} type mockParser struct { portsFunc func() ([]corev1.ServicePort, error) diff --git a/pkg/collector/collector_suite_test.go b/pkg/collector/collector_suite_test.go deleted file mode 100644 index 4e1cd50d21..0000000000 --- a/pkg/collector/collector_suite_test.go +++ /dev/null @@ -1,13 +0,0 @@ -package collector_test - -import ( - "testing" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -func TestCollector(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Collector Suite") -} diff --git a/pkg/collector/container_test.go b/pkg/collector/container_test.go index 86ad9e770a..69af8b5ec1 100644 --- a/pkg/collector/container_test.go +++ b/pkg/collector/container_test.go @@ -1,119 +1,120 @@ package collector_test import ( - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" + "testing" + corev1 "k8s.io/api/core/v1" logf "sigs.k8s.io/controller-runtime/pkg/log" "github.com/open-telemetry/opentelemetry-operator/api/v1alpha1" "github.com/open-telemetry/opentelemetry-operator/internal/config" . "github.com/open-telemetry/opentelemetry-operator/pkg/collector" + "github.com/stretchr/testify/assert" ) -var _ = Describe("Container", func() { - logger := logf.Log.WithName("unit-tests") - - It("should generate a new default container", func() { - // prepare - otelcol := v1alpha1.OpenTelemetryCollector{} - cfg := config.New(config.WithCollectorImage("default-image")) - - // test - c := Container(cfg, logger, otelcol) - - // verify - Expect(c.Image).To(Equal("default-image")) - }) - - It("should allow image to be overridden", func() { - // prepare - otelcol := v1alpha1.OpenTelemetryCollector{ - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - Image: "overridden-image", - }, - } - cfg := config.New(config.WithCollectorImage("default-image")) - - // test - c := Container(cfg, logger, otelcol) - - // verify - Expect(c.Image).To(Equal("overridden-image")) - }) - - It("config flag is ignored", func() { - // prepare - otelcol := v1alpha1.OpenTelemetryCollector{ - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - Args: map[string]string{ - "key": "value", - "config": "/some-custom-file.yaml", - }, - }, - } - cfg := config.New() - - // test - c := Container(cfg, logger, otelcol) - - // verify - Expect(c.Args).To(HaveLen(2)) - Expect(c.Args).To(ContainElement("--key=value")) // sanity check - Expect(c.Args).ToNot(ContainElement("--config=/some-custom-file.yaml")) - }) - - It("custom volumes are mounted", func() { - // prepare - otelcol := v1alpha1.OpenTelemetryCollector{ - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - VolumeMounts: []corev1.VolumeMount{{ - Name: "custom-volume-mount", - }}, +var logger = logf.Log.WithName("unit-tests") + +func TestContainerNewDefault(t *testing.T) { + // prepare + otelcol := v1alpha1.OpenTelemetryCollector{} + cfg := config.New(config.WithCollectorImage("default-image")) + + // test + c := Container(cfg, logger, otelcol) + + // verify + assert.Equal(t, "default-image", c.Image) +} + +func TestContainerWithImageOverridden(t *testing.T) { + // prepare + otelcol := v1alpha1.OpenTelemetryCollector{ + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Image: "overridden-image", + }, + } + cfg := config.New(config.WithCollectorImage("default-image")) + + // test + c := Container(cfg, logger, otelcol) + + // verify + assert.Equal(t, "overridden-image", c.Image) +} + +func TestContainerConfigFlagIsIgnored(t *testing.T) { + // prepare + otelcol := v1alpha1.OpenTelemetryCollector{ + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Args: map[string]string{ + "key": "value", + "config": "/some-custom-file.yaml", }, - } - cfg := config.New() - - // test - c := Container(cfg, logger, otelcol) - - // verify - Expect(c.VolumeMounts).To(HaveLen(2)) - Expect(c.VolumeMounts[1].Name).To(Equal("custom-volume-mount")) - }) - - It("should allow for env vars to be overridden", func() { - otelcol := v1alpha1.OpenTelemetryCollector{ - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - Env: []corev1.EnvVar{ - { - Name: "foo", - Value: "bar", - }, + }, + } + cfg := config.New() + + // test + c := Container(cfg, logger, otelcol) + + // verify + assert.Len(t, c.Args, 2) + assert.Contains(t, c.Args, "--key=value") + assert.NotContains(t, c.Args, "--config=/some-custom-file.yaml") +} + +func TestContainerCustomVolumes(t *testing.T) { + // prepare + otelcol := v1alpha1.OpenTelemetryCollector{ + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + VolumeMounts: []corev1.VolumeMount{{ + Name: "custom-volume-mount", + }}, + }, + } + cfg := config.New() + + // test + c := Container(cfg, logger, otelcol) + + // verify + assert.Len(t, c.VolumeMounts, 2) + assert.Equal(t, "custom-volume-mount", c.VolumeMounts[1].Name) +} + +func TestContainerEnvVarsOverridden(t *testing.T) { + otelcol := v1alpha1.OpenTelemetryCollector{ + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Env: []corev1.EnvVar{ + { + Name: "foo", + Value: "bar", }, }, - } + }, + } - cfg := config.New() + cfg := config.New() - // test - c := Container(cfg, logger, otelcol) + // test + c := Container(cfg, logger, otelcol) - Expect(c.Env).To(HaveLen(1)) - Expect(c.Env[0].Name).To(Equal("foo")) - Expect(c.Env[0].Value).To(Equal("bar")) - }) + // verify + assert.Len(t, c.Env, 1) + assert.Equal(t, "foo", c.Env[0].Name) + assert.Equal(t, "bar", c.Env[0].Value) +} - It("should allow for empty env vars by default", func() { - otelcol := v1alpha1.OpenTelemetryCollector{ - Spec: v1alpha1.OpenTelemetryCollectorSpec{}, - } +func TestContainerEmptyEnvVarsByDefault(t *testing.T) { + otelcol := v1alpha1.OpenTelemetryCollector{ + Spec: v1alpha1.OpenTelemetryCollectorSpec{}, + } - cfg := config.New() + cfg := config.New() - // test - c := Container(cfg, logger, otelcol) + // test + c := Container(cfg, logger, otelcol) - Expect(c.Env).To(BeEmpty()) - }) -}) + // verify + assert.Empty(t, c.Env) +} diff --git a/pkg/collector/daemonset_test.go b/pkg/collector/daemonset_test.go index 93c88435e8..53dacf8b6e 100644 --- a/pkg/collector/daemonset_test.go +++ b/pkg/collector/daemonset_test.go @@ -1,44 +1,40 @@ package collector_test import ( - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" + "testing" + + "github.com/stretchr/testify/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - logf "sigs.k8s.io/controller-runtime/pkg/log" "github.com/open-telemetry/opentelemetry-operator/api/v1alpha1" "github.com/open-telemetry/opentelemetry-operator/internal/config" . "github.com/open-telemetry/opentelemetry-operator/pkg/collector" ) -var _ = Describe("Daemonset", func() { - logger := logf.Log.WithName("unit-tests") - - It("should build a default new daemonset", func() { - // prepare - otelcol := v1alpha1.OpenTelemetryCollector{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-instance", - }, - } - cfg := config.New() - - // test - d := DaemonSet(cfg, logger, otelcol) - - // verify - Expect(d.Name).To(Equal("my-instance-collector")) - Expect(d.Labels["app.kubernetes.io/name"]).To(Equal("my-instance-collector")) - Expect(d.Annotations["prometheus.io/scrape"]).To(Equal("true")) - Expect(d.Annotations["prometheus.io/port"]).To(Equal("8888")) - Expect(d.Annotations["prometheus.io/path"]).To(Equal("/metrics")) - - Expect(d.Spec.Template.Spec.Containers).To(HaveLen(1)) - - // none of the default annotations should propagate down to the pod - Expect(d.Spec.Template.Annotations).To(BeEmpty()) - - // the pod selector should match the pod spec's labels - Expect(d.Spec.Template.Labels).To(Equal(d.Spec.Selector.MatchLabels)) - }) -}) +func TestDaemonSetNewDefault(t *testing.T) { + // prepare + otelcol := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + }, + } + cfg := config.New() + + // test + d := DaemonSet(cfg, logger, otelcol) + + // verify + assert.Equal(t, "my-instance-collector", d.Name) + assert.Equal(t, "my-instance-collector", d.Labels["app.kubernetes.io/name"]) + assert.Equal(t, "true", d.Annotations["prometheus.io/scrape"]) + assert.Equal(t, "8888", d.Annotations["prometheus.io/port"]) + assert.Equal(t, "/metrics", d.Annotations["prometheus.io/path"]) + + assert.Len(t, d.Spec.Template.Spec.Containers, 1) + + // none of the default annotations should propagate down to the pod + assert.Empty(t, d.Spec.Template.Annotations) + + // the pod selector should match the pod spec's labels + assert.Equal(t, d.Spec.Selector.MatchLabels, d.Spec.Template.Labels) +} diff --git a/pkg/collector/deployment_test.go b/pkg/collector/deployment_test.go index fad045e2cf..10ed8f66e2 100644 --- a/pkg/collector/deployment_test.go +++ b/pkg/collector/deployment_test.go @@ -1,44 +1,40 @@ package collector_test import ( - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" + "testing" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - logf "sigs.k8s.io/controller-runtime/pkg/log" "github.com/open-telemetry/opentelemetry-operator/api/v1alpha1" "github.com/open-telemetry/opentelemetry-operator/internal/config" . "github.com/open-telemetry/opentelemetry-operator/pkg/collector" + "github.com/stretchr/testify/assert" ) -var _ = Describe("Deployment", func() { - logger := logf.Log.WithName("unit-tests") - - It("should build a default new deployment", func() { - // prepare - otelcol := v1alpha1.OpenTelemetryCollector{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-instance", - }, - } - cfg := config.New() - - // test - d := Deployment(cfg, logger, otelcol) - - // verify - Expect(d.Name).To(Equal("my-instance-collector")) - Expect(d.Labels["app.kubernetes.io/name"]).To(Equal("my-instance-collector")) - Expect(d.Annotations["prometheus.io/scrape"]).To(Equal("true")) - Expect(d.Annotations["prometheus.io/port"]).To(Equal("8888")) - Expect(d.Annotations["prometheus.io/path"]).To(Equal("/metrics")) - - Expect(d.Spec.Template.Spec.Containers).To(HaveLen(1)) - - // none of the default annotations should propagate down to the pod - Expect(d.Spec.Template.Annotations).To(BeEmpty()) - - // the pod selector should match the pod spec's labels - Expect(d.Spec.Template.Labels).To(Equal(d.Spec.Selector.MatchLabels)) - }) -}) +func TestDeploymentNewDefault(t *testing.T) { + // prepare + otelcol := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + }, + } + cfg := config.New() + + // test + d := Deployment(cfg, logger, otelcol) + + // verify + assert.Equal(t, "my-instance-collector", d.Name) + assert.Equal(t, "my-instance-collector", d.Labels["app.kubernetes.io/name"]) + assert.Equal(t, "true", d.Annotations["prometheus.io/scrape"]) + assert.Equal(t, "8888", d.Annotations["prometheus.io/port"]) + assert.Equal(t, "/metrics", d.Annotations["prometheus.io/path"]) + + assert.Len(t, d.Spec.Template.Spec.Containers, 1) + + // none of the default annotations should propagate down to the pod + assert.Empty(t, d.Spec.Template.Annotations) + + // the pod selector should match the pod spec's labels + assert.Equal(t, d.Spec.Template.Labels, d.Spec.Selector.MatchLabels) +} diff --git a/pkg/collector/labels_test.go b/pkg/collector/labels_test.go index 99911a9bf2..bccb005971 100644 --- a/pkg/collector/labels_test.go +++ b/pkg/collector/labels_test.go @@ -1,48 +1,44 @@ package collector_test import ( - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" + "testing" + + "github.com/stretchr/testify/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/open-telemetry/opentelemetry-operator/api/v1alpha1" . "github.com/open-telemetry/opentelemetry-operator/pkg/collector" ) -var _ = Describe("Labels", func() { - It("should build a common set of labels", func() { - // prepare - otelcol := v1alpha1.OpenTelemetryCollector{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-instance", - Namespace: "my-ns", - }, - } - - // test - labels := Labels(otelcol) - - // verify - Expect(labels).To(HaveLen(4)) - Expect(labels["app.kubernetes.io/managed-by"]).To(Equal("opentelemetry-operator")) - Expect(labels["app.kubernetes.io/instance"]).To(Equal("my-ns.my-instance")) - Expect(labels["app.kubernetes.io/part-of"]).To(Equal("opentelemetry")) - Expect(labels["app.kubernetes.io/component"]).To(Equal("opentelemetry-collector")) - }) - - It("should propagate down the instance's labels", func() { - // prepare - otelcol := v1alpha1.OpenTelemetryCollector{ - ObjectMeta: metav1.ObjectMeta{ - Labels: map[string]string{"myapp": "mycomponent"}, - }, - } - - // test - labels := Labels(otelcol) - - // verify - Expect(labels).To(HaveLen(5)) - Expect(labels["myapp"]).To(Equal("mycomponent")) - }) -}) +func TestLabelsCommonSet(t *testing.T) { + // prepare + otelcol := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + Namespace: "my-ns", + }, + } + + // test + labels := Labels(otelcol) + assert.Equal(t, "opentelemetry-operator", labels["app.kubernetes.io/managed-by"]) + assert.Equal(t, "my-ns.my-instance", labels["app.kubernetes.io/instance"]) + assert.Equal(t, "opentelemetry", labels["app.kubernetes.io/part-of"]) + assert.Equal(t, "opentelemetry-collector", labels["app.kubernetes.io/component"]) +} + +func TestLabelsPropagateDown(t *testing.T) { + // prepare + otelcol := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Labels: map[string]string{"myapp": "mycomponent"}, + }, + } + + // test + labels := Labels(otelcol) + + // verify + assert.Len(t, labels, 5) + assert.Equal(t, "mycomponent", labels["myapp"]) +} diff --git a/pkg/collector/parser/parser_suite_test.go b/pkg/collector/parser/parser_suite_test.go deleted file mode 100644 index 8c5384c3d3..0000000000 --- a/pkg/collector/parser/parser_suite_test.go +++ /dev/null @@ -1,13 +0,0 @@ -package parser_test - -import ( - "testing" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -func TestParser(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Parser Suite") -} diff --git a/pkg/collector/parser/receiver_generic_test.go b/pkg/collector/parser/receiver_generic_test.go index c6a1c4847d..dba6b7961a 100644 --- a/pkg/collector/parser/receiver_generic_test.go +++ b/pkg/collector/parser/receiver_generic_test.go @@ -1,76 +1,93 @@ package parser_test import ( - "github.com/go-logr/logr" - . "github.com/onsi/ginkgo" - . "github.com/onsi/ginkgo/extensions/table" - . "github.com/onsi/gomega" - logf "sigs.k8s.io/controller-runtime/pkg/log" + "testing" + "github.com/go-logr/logr" "github.com/open-telemetry/opentelemetry-operator/pkg/collector/parser" + "github.com/stretchr/testify/assert" + logf "sigs.k8s.io/controller-runtime/pkg/log" ) -var _ = Describe("Generic receivers", func() { - logger := logf.Log.WithName("unit-tests") - - It("should parse the endpoint", func() { - // prepare - // there's no parser registered to handle "myreceiver", so, it falls back to the generic parser - builder := parser.NewGenericReceiverParser(logger, "myreceiver", map[interface{}]interface{}{ - "endpoint": "0.0.0.0:1234", - }) - - // test - ports, err := builder.Ports() +var logger = logf.Log.WithName("unit-tests") - // verify - Expect(err).ToNot(HaveOccurred()) - Expect(ports).To(HaveLen(1)) - Expect(ports[0].Port).To(BeEquivalentTo(1234)) +func TestParseEndpoint(t *testing.T) { + // prepare + // there's no parser registered to handle "myreceiver", so, it falls back to the generic parser + builder := parser.NewGenericReceiverParser(logger, "myreceiver", map[interface{}]interface{}{ + "endpoint": "0.0.0.0:1234", }) - It("should have failed to parse the endpoint", func() { - // prepare - // there's no parser registered to handle "myreceiver", so, it falls back to the generic parser - builder := parser.NewGenericReceiverParser(logger, "myreceiver", map[interface{}]interface{}{ - "endpoint": "0.0.0.0", - }) + // test + ports, err := builder.Ports() - // test - ports, err := builder.Ports() + // verify + assert.NoError(t, err) + assert.Len(t, ports, 1) + assert.EqualValues(t, 1234, ports[0].Port) +} - // verify - Expect(err).ToNot(HaveOccurred()) - Expect(ports).To(HaveLen(0)) +func TestFailedToParseEndpoint(t *testing.T) { + // prepare + // there's no parser registered to handle "myreceiver", so, it falls back to the generic parser + builder := parser.NewGenericReceiverParser(logger, "myreceiver", map[interface{}]interface{}{ + "endpoint": "0.0.0.0", }) - DescribeTable("downstream parsers", - func(receiverName, parserName string, defaultPort int, builder func(logr.Logger, string, map[interface{}]interface{}) parser.ReceiverParser) { - Describe("builds successfully", func() { + // test + ports, err := builder.Ports() + + // verify + assert.NoError(t, err) + assert.Len(t, ports, 0) +} + +func TestDownstreamParsers(t *testing.T) { + for _, tt := range []struct { + desc string + receiverName string + parserName string + defaultPort int + builder func(logr.Logger, string, map[interface{}]interface{}) parser.ReceiverParser + }{ + {"zipkin", "zipkin", "__zipkin", 9411, parser.NewZipkinReceiverParser}, + {"opencensus", "opencensus", "__opencensus", 55678, parser.NewOpenCensusReceiverParser}, + {"otlp", "otlp", "__otlp", 55680, parser.NewOTLPReceiverParser}, + + // contrib receivers + {"carbon", "carbon", "__carbon", 2003, parser.NewCarbonReceiverParser}, + {"collectd", "collectd", "__collectd", 8081, parser.NewCollectdReceiverParser}, + {"sapm", "sapm", "__sapm", 7276, parser.NewSAPMReceiverParser}, + {"signalfx", "signalfx", "__signalfx", 9943, parser.NewSignalFxReceiverParser}, + {"wavefront", "wavefront", "__wavefront", 2003, parser.NewWavefrontReceiverParser}, + {"zipkin-scribe", "zipkin-scribe", "__zipkinscribe", 9410, parser.NewZipkinScribeReceiverParser}, + } { + t.Run(tt.receiverName, func(t *testing.T) { + t.Run("builds successfully", func(t *testing.T) { // test - builder := builder(logger, receiverName, map[interface{}]interface{}{}) + builder := tt.builder(logger, tt.receiverName, map[interface{}]interface{}{}) // verify - Expect(builder.ParserName()).To(Equal(parserName)) + assert.Equal(t, tt.parserName, builder.ParserName()) }) - Describe("assigns the expected port", func() { + t.Run("assigns the expected port", func(t *testing.T) { // prepare - builder := builder(logger, receiverName, map[interface{}]interface{}{}) + builder := tt.builder(logger, tt.receiverName, map[interface{}]interface{}{}) // test ports, err := builder.Ports() // verify - Expect(err).ToNot(HaveOccurred()) - Expect(ports).To(HaveLen(1)) - Expect(ports[0].Port).To(BeEquivalentTo(defaultPort)) - Expect(ports[0].Name).To(Equal(receiverName)) + assert.NoError(t, err) + assert.Len(t, ports, 1) + assert.EqualValues(t, tt.defaultPort, ports[0].Port) + assert.Equal(t, tt.receiverName, ports[0].Name) }) - Describe("allows port to be overridden", func() { + t.Run("allows port to be overridden", func(t *testing.T) { // prepare - builder := builder(logger, receiverName, map[interface{}]interface{}{ + builder := tt.builder(logger, tt.receiverName, map[interface{}]interface{}{ "endpoint": "0.0.0.0:65535", }) @@ -78,23 +95,11 @@ var _ = Describe("Generic receivers", func() { ports, err := builder.Ports() // verify - Expect(err).ToNot(HaveOccurred()) - Expect(ports).To(HaveLen(1)) - Expect(ports[0].Port).To(BeEquivalentTo(65535)) - Expect(ports[0].Name).To(Equal(receiverName)) + assert.NoError(t, err) + assert.Len(t, ports, 1) + assert.EqualValues(t, 65535, ports[0].Port) + assert.Equal(t, tt.receiverName, ports[0].Name) }) - }, - Entry("zipkin", "zipkin", "__zipkin", 9411, parser.NewZipkinReceiverParser), - Entry("opencensus", "opencensus", "__opencensus", 55678, parser.NewOpenCensusReceiverParser), - Entry("otlp", "otlp", "__otlp", 55680, parser.NewOTLPReceiverParser), - - // contrib receivers - Entry("carbon", "carbon", "__carbon", 2003, parser.NewCarbonReceiverParser), - Entry("collectd", "collectd", "__collectd", 8081, parser.NewCollectdReceiverParser), - Entry("sapm", "sapm", "__sapm", 7276, parser.NewSAPMReceiverParser), - Entry("signalfx", "signalfx", "__signalfx", 9943, parser.NewSignalFxReceiverParser), - Entry("wavefront", "wavefront", "__wavefront", 2003, parser.NewWavefrontReceiverParser), - Entry("zipkin-scribe", "zipkin-scribe", "__zipkinscribe", 9410, parser.NewZipkinScribeReceiverParser), - ) - -}) + }) + } +} diff --git a/pkg/collector/parser/receiver_jaeger_test.go b/pkg/collector/parser/receiver_jaeger_test.go index 29e1d9d338..46bd0a16eb 100644 --- a/pkg/collector/parser/receiver_jaeger_test.go +++ b/pkg/collector/parser/receiver_jaeger_test.go @@ -1,101 +1,95 @@ -package parser_test +package parser import ( - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - logf "sigs.k8s.io/controller-runtime/pkg/log" + "testing" - "github.com/open-telemetry/opentelemetry-operator/pkg/collector/parser" + "github.com/stretchr/testify/assert" ) -var _ = Describe("Jaeger receiver", func() { - logger := logf.Log.WithName("unit-tests") - - It("should have self registered", func() { - // verify - Expect(parser.IsRegistered("jaeger")).To(BeTrue()) - }) - - It("should be found via its parser name", func() { - // test - p := parser.For(logger, "jaeger", map[interface{}]interface{}{}) - - // verify - Expect(p.ParserName()).To(Equal("__jaeger")) +func TestJaegerSelfRegisters(t *testing.T) { + // verify + assert.True(t, IsRegistered("jaeger")) +} + +func TestJaegerIsFoundByName(t *testing.T) { + // test + p := For(logger, "jaeger", map[interface{}]interface{}{}) + + // verify + assert.Equal(t, "__jaeger", p.ParserName()) +} + +func TestJaegerMinimalConfiguration(t *testing.T) { + // prepare + builder := NewJaegerReceiverParser(logger, "jaeger", map[interface{}]interface{}{ + "protocols": map[interface{}]interface{}{ + "grpc": map[interface{}]interface{}{}, + }, }) - It("should build with a minimal configuration", func() { - // prepare - builder := parser.NewJaegerReceiverParser(logger, "jaeger", map[interface{}]interface{}{ - "protocols": map[interface{}]interface{}{ - "grpc": map[interface{}]interface{}{}, + // test + ports, err := builder.Ports() + + // verify + assert.NoError(t, err) + assert.Len(t, ports, 1) + assert.EqualValues(t, 14250, ports[0].Port) +} + +func TestJaegerPortsOverridden(t *testing.T) { + // prepare + builder := NewJaegerReceiverParser(logger, "jaeger", map[interface{}]interface{}{ + "protocols": map[interface{}]interface{}{ + "grpc": map[interface{}]interface{}{ + "endpoint": "0.0.0.0:1234", }, - }) - - // test - ports, err := builder.Ports() - - // verify - Expect(err).ToNot(HaveOccurred()) - Expect(ports).To(HaveLen(1)) - Expect(ports[0].Port).To(BeEquivalentTo(14250)) + }, }) - It("should allow ports to be overridden", func() { - // prepare - builder := parser.NewJaegerReceiverParser(logger, "jaeger", map[interface{}]interface{}{ - "protocols": map[interface{}]interface{}{ - "grpc": map[interface{}]interface{}{ - "endpoint": "0.0.0.0:1234", - }, - }, - }) - - // test - ports, err := builder.Ports() - - // verify - Expect(err).ToNot(HaveOccurred()) - Expect(ports).To(HaveLen(1)) - Expect(ports[0].Port).To(BeEquivalentTo(1234)) + // test + ports, err := builder.Ports() + + // verify + assert.NoError(t, err) + assert.Len(t, ports, 1) + assert.EqualValues(t, 1234, ports[0].Port) +} + +func TestJaegerExposeDefaultPorts(t *testing.T) { + // prepare + builder := NewJaegerReceiverParser(logger, "jaeger", map[interface{}]interface{}{ + "protocols": map[interface{}]interface{}{ + "grpc": map[interface{}]interface{}{}, + "thrift_http": map[interface{}]interface{}{}, + "thrift_compact": map[interface{}]interface{}{}, + "thrift_binary": map[interface{}]interface{}{}, + }, }) - It("should expose the default ports", func() { - // prepare - builder := parser.NewJaegerReceiverParser(logger, "jaeger", map[interface{}]interface{}{ - "protocols": map[interface{}]interface{}{ - "grpc": map[interface{}]interface{}{}, - "thrift_http": map[interface{}]interface{}{}, - "thrift_compact": map[interface{}]interface{}{}, - "thrift_binary": map[interface{}]interface{}{}, - }, - }) - - expectedResults := map[string]struct { - portNumber int32 - seen bool - }{ - "jaeger-grpc": {portNumber: 14250}, - "jaeger-thrift-http": {portNumber: 14268}, - "jaeger-thrift-compact": {portNumber: 6831}, - "jaeger-thrift-binary": {portNumber: 6832}, - } - - // test - ports, err := builder.Ports() - - // verify - Expect(err).ToNot(HaveOccurred()) - Expect(ports).To(HaveLen(4)) - - for _, port := range ports { - r := expectedResults[port.Name] - r.seen = true - expectedResults[port.Name] = r - Expect(port.Port).To(BeEquivalentTo(r.portNumber)) - } - for k, v := range expectedResults { - Expect(v.seen).To(BeTrue(), "the port %s wasn't included in the service ports", k) - } - }) -}) + expectedResults := map[string]struct { + portNumber int32 + seen bool + }{ + "jaeger-grpc": {portNumber: 14250}, + "jaeger-thrift-http": {portNumber: 14268}, + "jaeger-thrift-compact": {portNumber: 6831}, + "jaeger-thrift-binary": {portNumber: 6832}, + } + + // test + ports, err := builder.Ports() + + // verify + assert.NoError(t, err) + assert.Len(t, ports, 4) + + for _, port := range ports { + r := expectedResults[port.Name] + r.seen = true + expectedResults[port.Name] = r + assert.EqualValues(t, r.portNumber, port.Port) + } + for k, v := range expectedResults { + assert.True(t, v.seen, "the port %s wasn't included in the service ports", k) + } +} diff --git a/pkg/collector/parser/receiver_test.go b/pkg/collector/parser/receiver_test.go index e1497d8125..fa6030f3ef 100644 --- a/pkg/collector/parser/receiver_test.go +++ b/pkg/collector/parser/receiver_test.go @@ -1,91 +1,111 @@ package parser import ( + "testing" + "github.com/go-logr/logr" - . "github.com/onsi/ginkgo" - . "github.com/onsi/ginkgo/extensions/table" - . "github.com/onsi/gomega" + "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" logf "sigs.k8s.io/controller-runtime/pkg/log" ) -var _ = Describe("Receiver", func() { - logger := logf.Log.WithName("unit-tests") +var logger = logf.Log.WithName("unit-tests") + +func TestReceiverPortNames(t *testing.T) { + for _, tt := range []struct { + desc string + candidate string + port int + expected string + }{ + {"regular case", "my-receiver", 123, "my-receiver"}, + {"name too long", "long-name-long-name-long-name-long-name-long-name-long-name-long-name-long-name", 123, "port-123"}, + {"name with invalid chars", "my-🦄-receiver", 123, "port-123"}, + {"name starting with invalid char", "-my-receiver", 123, "port-123"}, + } { + t.Run(tt.desc, func(t *testing.T) { + assert.Equal(t, tt.expected, portName(tt.candidate, int32(tt.port))) + }) + } +} - DescribeTable("port names", - func(candidate string, port int, expected string) { +func TestReceiverType(t *testing.T) { + for _, tt := range []struct { + desc string + name string + expected string + }{ + {"regular case", "myreceiver", "myreceiver"}, + {"named instance", "myreceiver/custom", "myreceiver"}, + } { + t.Run(tt.desc, func(t *testing.T) { // test and verify - Expect(portName(candidate, int32(port))).To(Equal(expected)) - }, - Entry("regular case", "my-receiver", 123, "my-receiver"), - Entry("name too long", "long-name-long-name-long-name-long-name-long-name-long-name-long-name-long-name", 123, "port-123"), - Entry("name with invalid chars", "my-🦄-receiver", 123, "port-123"), - Entry("name starting with invalid char", "-my-receiver", 123, "port-123"), - ) - - DescribeTable("receiver type", - func(name string, expected string) { - // test and verify - Expect(receiverType(name)).To(Equal(expected)) - }, - Entry("regular case", "myreceiver", "myreceiver"), - Entry("named instance", "myreceiver/custom", "myreceiver"), - ) - - DescribeTable("parse port from endpoint", - func(endpoint string, expected int, errorExpected bool) { + assert.Equal(t, tt.expected, receiverType(tt.name)) + }) + } +} + +func TestReceiverParsePortFromEndpoint(t *testing.T) { + for _, tt := range []struct { + desc string + endpoint string + expected int + errorExpected bool + }{ + {"regular case", "http://localhost:1234", 1234, false}, + {"no protocol", "0.0.0.0:1234", 1234, false}, + {"just port", ":1234", 1234, false}, + {"no port at all", "http://localhost", 0, true}, + } { + t.Run(tt.desc, func(t *testing.T) { // test - val, err := portFromEndpoint(endpoint) - if errorExpected { - Expect(err).To(HaveOccurred()) + val, err := portFromEndpoint(tt.endpoint) + if tt.errorExpected { + assert.Error(t, err) } else { - Expect(err).ToNot(HaveOccurred()) + assert.NoError(t, err) } - Expect(val).To(BeEquivalentTo(expected), "wrong port from endpoint %s: %d", endpoint, val) - }, - Entry("regular case", "http://localhost:1234", 1234, false), - Entry("no protocol", "0.0.0.0:1234", 1234, false), - Entry("just port", ":1234", 1234, false), - Entry("no port at all", "http://localhost", 0, true), - ) - - It("should fail when the port isn't a string", func() { - // prepare - config := map[interface{}]interface{}{ - "endpoint": 123, - } - - // test - p := singlePortFromConfigEndpoint(logger, "myreceiver", config) - - // verify - Expect(p).To(BeNil()) - }) + assert.EqualValues(t, tt.expected, val, "wrong port from endpoint %s: %d", tt.endpoint, val) + }) + } +} - It("should fallback to generic parser when receiver isn't registered", func() { - // test - p := For(logger, "myreceiver", map[interface{}]interface{}{}) +func TestReceiverFailsWhenPortIsntString(t *testing.T) { + // prepare + config := map[interface{}]interface{}{ + "endpoint": 123, + } - // test - Expect(p.ParserName()).To(Equal("__generic")) - }) + // test + p := singlePortFromConfigEndpoint(logger, "myreceiver", config) - It("should find a registered parser", func() { - // prepare - builderCalled := false - Register("mock", func(logger logr.Logger, name string, config map[interface{}]interface{}) ReceiverParser { - builderCalled = true - return &mockParser{} - }) + // verify + assert.Nil(t, p) +} - // test - For(logger, "mock", map[interface{}]interface{}{}) +func TestReceiverFallbackWhenNotRegistered(t *testing.T) { + // test + p := For(logger, "myreceiver", map[interface{}]interface{}{}) - // verify - Expect(builderCalled).To(BeTrue()) + // test + assert.Equal(t, "__generic", p.ParserName()) +} + +func TestReceiverShouldFindRegisteredParser(t *testing.T) { + // prepare + builderCalled := false + Register("mock", func(logger logr.Logger, name string, config map[interface{}]interface{}) ReceiverParser { + builderCalled = true + return &mockParser{} }) -}) + + // test + For(logger, "mock", map[interface{}]interface{}{}) + + // verify + assert.True(t, builderCalled) +} type mockParser struct { } diff --git a/pkg/collector/serviceaccount_test.go b/pkg/collector/serviceaccount_test.go index 59513f6a10..5c8c7249e2 100644 --- a/pkg/collector/serviceaccount_test.go +++ b/pkg/collector/serviceaccount_test.go @@ -1,60 +1,44 @@ package collector_test import ( - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" + "testing" + + "github.com/stretchr/testify/assert" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/open-telemetry/opentelemetry-operator/api/v1alpha1" . "github.com/open-telemetry/opentelemetry-operator/pkg/collector" ) -var _ = Describe("Serviceaccount", func() { - It("should default to an instance-specific service account name", func() { - // prepare - otelcol := v1alpha1.OpenTelemetryCollector{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-instance", - }, - } - - // test - sa := ServiceAccountName(otelcol) - - // verify - Expect(sa).To(Equal("my-instance-collector")) - }) - - It("should allow the serviceaccount to be overridden", func() { - // prepare - otelcol := v1alpha1.OpenTelemetryCollector{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-instance", - }, - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - ServiceAccount: "my-special-sa", - }, - } - - // test - sa := ServiceAccountName(otelcol) - - // verify - Expect(sa).To(Equal("my-special-sa")) - }) - - It("should build a new default service account", func() { - // prepare - otelcol := v1alpha1.OpenTelemetryCollector{ - ObjectMeta: metav1.ObjectMeta{ - Name: "my-instance", - }, - } - - // test - sa := ServiceAccount(otelcol) - - // verify - Expect(sa.Name).To(Equal("my-instance-collector")) - }) -}) +func TestServiceAccountNewDefault(t *testing.T) { + // prepare + otelcol := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + }, + } + + // test + sa := ServiceAccountName(otelcol) + + // verify + assert.Equal(t, "my-instance-collector", sa) +} + +func TestServiceAccountOverride(t *testing.T) { + // prepare + otelcol := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "my-instance", + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + ServiceAccount: "my-special-sa", + }, + } + + // test + sa := ServiceAccountName(otelcol) + + // verify + assert.Equal(t, "my-special-sa", sa) +} diff --git a/pkg/collector/upgrade/suite_test.go b/pkg/collector/upgrade/suite_test.go new file mode 100644 index 0000000000..c8bf1e4ed0 --- /dev/null +++ b/pkg/collector/upgrade/suite_test.go @@ -0,0 +1,54 @@ +package upgrade_test + +import ( + "fmt" + "os" + "path/filepath" + "testing" + + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/client-go/kubernetes/scheme" + "sigs.k8s.io/controller-runtime/pkg/client" + "sigs.k8s.io/controller-runtime/pkg/envtest" + + "github.com/open-telemetry/opentelemetry-operator/api/v1alpha1" + // +kubebuilder:scaffold:imports +) + +var k8sClient client.Client +var testEnv *envtest.Environment +var testScheme *runtime.Scheme = scheme.Scheme + +func TestMain(m *testing.M) { + testEnv = &envtest.Environment{ + CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crd", "bases")}, + } + + cfg, err := testEnv.Start() + if err != nil { + fmt.Printf("failed to start testEnv: %v", err) + os.Exit(1) + } + + if err := v1alpha1.AddToScheme(testScheme); err != nil { + fmt.Printf("failed to register scheme: %v", err) + os.Exit(1) + } + // +kubebuilder:scaffold:scheme + + k8sClient, err = client.New(cfg, client.Options{Scheme: testScheme}) + if err != nil { + fmt.Printf("failed to setup a Kubernetes client: %v", err) + os.Exit(1) + } + + code := m.Run() + + err = testEnv.Stop() + if err != nil { + fmt.Printf("failed to stop testEnv: %v", err) + os.Exit(1) + } + + os.Exit(code) +} diff --git a/pkg/collector/upgrade/upgrade_suite_test.go b/pkg/collector/upgrade/upgrade_suite_test.go deleted file mode 100644 index c49710cfe8..0000000000 --- a/pkg/collector/upgrade/upgrade_suite_test.go +++ /dev/null @@ -1,57 +0,0 @@ -package upgrade_test - -import ( - "path/filepath" - "testing" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" - "k8s.io/client-go/rest" - "k8s.io/kubectl/pkg/scheme" - "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/envtest" - logf "sigs.k8s.io/controller-runtime/pkg/log" - "sigs.k8s.io/controller-runtime/pkg/log/zap" - - "github.com/open-telemetry/opentelemetry-operator/api/v1alpha1" -) - -var cfg *rest.Config -var k8sClient client.Client -var testEnv *envtest.Environment - -func TestUpgrade(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Upgrade Suite") -} - -var _ = BeforeSuite(func(done Done) { - logf.SetLogger(zap.LoggerTo(GinkgoWriter, true)) - - By("bootstrapping test environment") - testEnv = &envtest.Environment{ - CRDDirectoryPaths: []string{filepath.Join("..", "..", "..", "config", "crd", "bases")}, - } - - var err error - cfg, err = testEnv.Start() - Expect(err).ToNot(HaveOccurred()) - Expect(cfg).ToNot(BeNil()) - - err = v1alpha1.AddToScheme(scheme.Scheme) - Expect(err).NotTo(HaveOccurred()) - - // +kubebuilder:scaffold:scheme - - k8sClient, err = client.New(cfg, client.Options{Scheme: scheme.Scheme}) - Expect(err).ToNot(HaveOccurred()) - Expect(k8sClient).ToNot(BeNil()) - - close(done) -}, 60) - -var _ = AfterSuite(func() { - By("tearing down the test environment") - err := testEnv.Stop() - Expect(err).ToNot(HaveOccurred()) -}) diff --git a/pkg/collector/upgrade/upgrade_test.go b/pkg/collector/upgrade/upgrade_test.go index 85ab51d7d4..e2aac56391 100644 --- a/pkg/collector/upgrade/upgrade_test.go +++ b/pkg/collector/upgrade/upgrade_test.go @@ -2,10 +2,10 @@ package upgrade_test import ( "context" + "testing" - . "github.com/onsi/ginkgo" - . "github.com/onsi/ginkgo/extensions/table" - . "github.com/onsi/gomega" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" logf "sigs.k8s.io/controller-runtime/pkg/log" @@ -15,106 +15,112 @@ import ( "github.com/open-telemetry/opentelemetry-operator/pkg/collector/upgrade" ) -var _ = Describe("Upgrade", func() { - logger := logf.Log.WithName("unit-tests") - - It("should upgrade to the latest", func() { - // prepare - nsn := types.NamespacedName{Name: "my-instance", Namespace: "default"} - existing := v1alpha1.OpenTelemetryCollector{ - ObjectMeta: metav1.ObjectMeta{ - Name: nsn.Name, - Namespace: nsn.Namespace, - Labels: map[string]string{ - "app.kubernetes.io/managed-by": "opentelemetry-operator", - }, +var logger = logf.Log.WithName("unit-tests") + +func TestShouldUpgradeAllToLatest(t *testing.T) { + // prepare + nsn := types.NamespacedName{Name: "my-instance", Namespace: "default"} + existing := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: nsn.Name, + Namespace: nsn.Namespace, + Labels: map[string]string{ + "app.kubernetes.io/managed-by": "opentelemetry-operator", }, - } - existing.Status.Version = "0.0.1" // this is the first version we have an upgrade function - err := k8sClient.Create(context.Background(), &existing) - Expect(err).To(Succeed()) - - err = k8sClient.Status().Update(context.Background(), &existing) - Expect(err).To(Succeed()) - - currentV := version.Get() - currentV.OpenTelemetryCollector = upgrade.Latest.String() - - // sanity check - persisted := &v1alpha1.OpenTelemetryCollector{} - err = k8sClient.Get(context.Background(), nsn, persisted) - Expect(err).To(Succeed()) - Expect(persisted.Status.Version).To(Equal("0.0.1")) - - // test - err = upgrade.ManagedInstances(context.Background(), logger, currentV, k8sClient) - Expect(err).To(Succeed()) - - // verify - err = k8sClient.Get(context.Background(), nsn, persisted) - Expect(err).To(Succeed()) - Expect(persisted.Status.Version).To(Equal(upgrade.Latest.String())) - - // cleanup - Expect(k8sClient.Delete(context.Background(), &existing)) - }) - - It("should upgrade up to the latest known version", func() { - // prepare - nsn := types.NamespacedName{Name: "my-instance", Namespace: "default"} - existing := v1alpha1.OpenTelemetryCollector{ - ObjectMeta: metav1.ObjectMeta{ - Name: nsn.Name, - Namespace: nsn.Namespace, - Labels: map[string]string{ - "app.kubernetes.io/managed-by": "opentelemetry-operator", - }, + }, + } + existing.Status.Version = "0.0.1" // this is the first version we have an upgrade function + err := k8sClient.Create(context.Background(), &existing) + require.NoError(t, err) + + err = k8sClient.Status().Update(context.Background(), &existing) + require.NoError(t, err) + + currentV := version.Get() + currentV.OpenTelemetryCollector = upgrade.Latest.String() + + // sanity check + persisted := &v1alpha1.OpenTelemetryCollector{} + err = k8sClient.Get(context.Background(), nsn, persisted) + require.NoError(t, err) + require.Equal(t, "0.0.1", persisted.Status.Version) + + // test + err = upgrade.ManagedInstances(context.Background(), logger, currentV, k8sClient) + assert.NoError(t, err) + + // verify + err = k8sClient.Get(context.Background(), nsn, persisted) + assert.NoError(t, err) + assert.Equal(t, upgrade.Latest.String(), persisted.Status.Version) + + // cleanup + assert.NoError(t, k8sClient.Delete(context.Background(), &existing)) +} + +func TestUpgradeUpToLatestKnownVersion(t *testing.T) { + // prepare + nsn := types.NamespacedName{Name: "my-instance", Namespace: "default"} + existing := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: nsn.Name, + Namespace: nsn.Namespace, + Labels: map[string]string{ + "app.kubernetes.io/managed-by": "opentelemetry-operator", }, - } - existing.Status.Version = "0.8.0" - - currentV := version.Get() - currentV.OpenTelemetryCollector = "0.10.0" // we don't have a 0.10.0 upgrade, but we have a 0.9.0 - - // test - res, err := upgrade.ManagedInstance(context.Background(), logger, currentV, k8sClient, existing) - - // verify - Expect(err).To(Succeed()) - Expect(res.Status.Version).To(Equal("0.10.0")) - }) - - DescribeTable("versions should not be changed", func(v string, expectedV string, failureExpected bool) { - // prepare - nsn := types.NamespacedName{Name: "my-instance", Namespace: "default"} - existing := v1alpha1.OpenTelemetryCollector{ - ObjectMeta: metav1.ObjectMeta{ - Name: nsn.Name, - Namespace: nsn.Namespace, - Labels: map[string]string{ - "app.kubernetes.io/managed-by": "opentelemetry-operator", + }, + } + existing.Status.Version = "0.8.0" + + currentV := version.Get() + currentV.OpenTelemetryCollector = "0.10.0" // we don't have a 0.10.0 upgrade, but we have a 0.9.0 + + // test + res, err := upgrade.ManagedInstance(context.Background(), logger, currentV, k8sClient, existing) + + // verify + assert.NoError(t, err) + assert.Equal(t, "0.10.0", res.Status.Version) +} + +func TestVersionsShouldNotBeChanged(t *testing.T) { + for _, tt := range []struct { + desc string + v string + expectedV string + failureExpected bool + }{ + {"new-instance", "", "", false}, + {"newer-than-our-newest", "100.0.0", "100.0.0", false}, + {"unparseable", "unparseable", "unparseable", true}, + } { + t.Run(tt.desc, func(t *testing.T) { + // prepare + nsn := types.NamespacedName{Name: "my-instance", Namespace: "default"} + existing := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: nsn.Name, + Namespace: nsn.Namespace, + Labels: map[string]string{ + "app.kubernetes.io/managed-by": "opentelemetry-operator", + }, }, - }, - } - existing.Status.Version = v - - currentV := version.Get() - currentV.OpenTelemetryCollector = upgrade.Latest.String() - - // test - res, err := upgrade.ManagedInstance(context.Background(), logger, currentV, k8sClient, existing) - if failureExpected { - Expect(err).To(HaveOccurred()) - } else { - Expect(err).To(Succeed()) - } - - // verify - Expect(res.Status.Version).To(Equal(expectedV)) - }, - Entry("new-instance", "", "", false), - Entry("newer-than-our-newest", "100.0.0", "100.0.0", false), - Entry("unparseable", "unparseable", "unparseable", true), - ) - -}) + } + existing.Status.Version = tt.v + + currentV := version.Get() + currentV.OpenTelemetryCollector = upgrade.Latest.String() + + // test + res, err := upgrade.ManagedInstance(context.Background(), logger, currentV, k8sClient, existing) + if tt.failureExpected { + assert.Error(t, err) + } else { + assert.NoError(t, err) + } + + // verify + assert.Equal(t, tt.expectedV, res.Status.Version) + }) + } +} diff --git a/pkg/collector/upgrade/v0_9_0_test.go b/pkg/collector/upgrade/v0_9_0_test.go index 9ca6674050..bd70b66328 100644 --- a/pkg/collector/upgrade/v0_9_0_test.go +++ b/pkg/collector/upgrade/v0_9_0_test.go @@ -2,54 +2,50 @@ package upgrade_test import ( "context" + "testing" - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/types" - logf "sigs.k8s.io/controller-runtime/pkg/log" "github.com/open-telemetry/opentelemetry-operator/api/v1alpha1" "github.com/open-telemetry/opentelemetry-operator/internal/version" "github.com/open-telemetry/opentelemetry-operator/pkg/collector/upgrade" ) -var _ = Describe("Upgrade to v0.9.0", func() { - logger := logf.Log.WithName("unit-tests") - - It("should remove reconnection_delay", func() { - // prepare - nsn := types.NamespacedName{Name: "my-instance", Namespace: "default"} - existing := v1alpha1.OpenTelemetryCollector{ - ObjectMeta: metav1.ObjectMeta{ - Name: nsn.Name, - Namespace: nsn.Namespace, - Labels: map[string]string{ - "app.kubernetes.io/managed-by": "opentelemetry-operator", - }, +func TestRemoveConnectionDelay(t *testing.T) { + // prepare + nsn := types.NamespacedName{Name: "my-instance", Namespace: "default"} + existing := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: nsn.Name, + Namespace: nsn.Namespace, + Labels: map[string]string{ + "app.kubernetes.io/managed-by": "opentelemetry-operator", }, - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - Config: `exporters: + }, + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Config: `exporters: opencensus: compression: "on" reconnection_delay: 15 num_workers: 123`, - }, - } - existing.Status.Version = "0.8.0" - - // sanity check - Expect(existing.Spec.Config).To(ContainSubstring("reconnection_delay")) - - // test - res, err := upgrade.ManagedInstance(context.Background(), logger, version.Get(), nil, existing) - Expect(err).To(Succeed()) - - // verify - Expect(res.Spec.Config).To(ContainSubstring("opencensus:")) - Expect(res.Spec.Config).To(ContainSubstring(`compression: "on"`)) - Expect(res.Spec.Config).ToNot(ContainSubstring("reconnection_delay")) - Expect(res.Spec.Config).To(ContainSubstring("num_workers: 123")) - Expect(res.Status.Messages[0]).To(ContainSubstring("upgrade to v0.9.0 removed the property reconnection_delay for exporter")) - }) -}) + }, + } + existing.Status.Version = "0.8.0" + + // sanity check + require.Contains(t, existing.Spec.Config, "reconnection_delay") + + // test + res, err := upgrade.ManagedInstance(context.Background(), logger, version.Get(), nil, existing) + assert.NoError(t, err) + + // verify + assert.Contains(t, res.Spec.Config, "opencensus:") + assert.Contains(t, res.Spec.Config, `compression: "on"`) + assert.NotContains(t, res.Spec.Config, "reconnection_delay") + assert.Contains(t, res.Spec.Config, "num_workers: 123") + assert.Contains(t, res.Status.Messages[0], "upgrade to v0.9.0 removed the property reconnection_delay for exporter") +} diff --git a/pkg/collector/volume_test.go b/pkg/collector/volume_test.go index 03e244b12d..36b7dcc90b 100644 --- a/pkg/collector/volume_test.go +++ b/pkg/collector/volume_test.go @@ -1,8 +1,9 @@ package collector_test import ( - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" + "testing" + + "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" "github.com/open-telemetry/opentelemetry-operator/api/v1alpha1" @@ -11,41 +12,38 @@ import ( "github.com/open-telemetry/opentelemetry-operator/pkg/naming" ) -var _ = Describe("Volume", func() { - It("should build a new default volume", func() { - // prepare - otelcol := v1alpha1.OpenTelemetryCollector{} - cfg := config.New() - - // test - volumes := Volumes(cfg, otelcol) - - // verify - Expect(volumes).To(HaveLen(1)) - - // check that it's the otc-internal volume, with the config map - Expect(volumes[0].Name).To(Equal(naming.ConfigMapVolume())) - }) - - It("should allow more volumes to be added", func() { - // prepare - otelcol := v1alpha1.OpenTelemetryCollector{ - Spec: v1alpha1.OpenTelemetryCollectorSpec{ - Volumes: []corev1.Volume{{ - Name: "my-volume", - }}, - }, - } - cfg := config.New() - - // test - volumes := Volumes(cfg, otelcol) - - // verify - Expect(volumes).To(HaveLen(2)) - - // check that it's the otc-internal volume, with the config map - Expect(volumes[1].Name).To(Equal("my-volume")) - }) - -}) +func TestVolumeNewDefault(t *testing.T) { + // prepare + otelcol := v1alpha1.OpenTelemetryCollector{} + cfg := config.New() + + // test + volumes := Volumes(cfg, otelcol) + + // verify + assert.Len(t, volumes, 1) + + // check that it's the otc-internal volume, with the config map + assert.Equal(t, naming.ConfigMapVolume(), volumes[0].Name) +} + +func TestVolumeAllowsMoreToBeAdded(t *testing.T) { + // prepare + otelcol := v1alpha1.OpenTelemetryCollector{ + Spec: v1alpha1.OpenTelemetryCollectorSpec{ + Volumes: []corev1.Volume{{ + Name: "my-volume", + }}, + }, + } + cfg := config.New() + + // test + volumes := Volumes(cfg, otelcol) + + // verify + assert.Len(t, volumes, 2) + + // check that it's the otc-internal volume, with the config map + assert.Equal(t, "my-volume", volumes[1].Name) +} diff --git a/pkg/sidecar/annotation_test.go b/pkg/sidecar/annotation_test.go index 9b1a61f262..0603d4935d 100644 --- a/pkg/sidecar/annotation_test.go +++ b/pkg/sidecar/annotation_test.go @@ -15,27 +15,24 @@ package sidecar_test import ( - . "github.com/onsi/ginkgo" - . "github.com/onsi/ginkgo/extensions/table" - . "github.com/onsi/gomega" + "testing" + + "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "github.com/open-telemetry/opentelemetry-operator/pkg/sidecar" ) -var _ = Describe("Annotation", func() { - - DescribeTable("determine the right effective annotation value", - func(expected string, pod corev1.Pod, ns corev1.Namespace) { - // test - annValue := sidecar.AnnotationValue(ns, pod) - - // verify - Expect(annValue).To(Equal(expected)) - }, - - Entry("pod-true-overrides-ns", +func TestEffectiveAnnotationValue(t *testing.T) { + for _, tt := range []struct { + desc string + expected string + pod corev1.Pod + ns corev1.Namespace + }{ + { + "pod-true-overrides-ns", "true", corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ @@ -51,9 +48,10 @@ var _ = Describe("Annotation", func() { }, }, }, - ), + }, - Entry("ns-has-concrete-instance", + { + "ns-has-concrete-instance", "some-instance", corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ @@ -69,9 +67,10 @@ var _ = Describe("Annotation", func() { }, }, }, - ), + }, - Entry("pod-has-concrete-instance", + { + "pod-has-concrete-instance", "some-instance-from-pod", corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ @@ -87,9 +86,10 @@ var _ = Describe("Annotation", func() { }, }, }, - ), + }, - Entry("pod-has-explicit-false", + { + "pod-has-explicit-false", "false", corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ @@ -105,9 +105,10 @@ var _ = Describe("Annotation", func() { }, }, }, - ), + }, - Entry("pod-has-no-annotations", + { + "pod-has-no-annotations", "some-instance", corev1.Pod{}, corev1.Namespace{ @@ -117,9 +118,10 @@ var _ = Describe("Annotation", func() { }, }, }, - ), + }, - Entry("ns-has-no-annotations", + { + "ns-has-no-annotations", "true", corev1.Pod{ ObjectMeta: metav1.ObjectMeta{ @@ -129,7 +131,14 @@ var _ = Describe("Annotation", func() { }, }, corev1.Namespace{}, - ), - ) + }, + } { + t.Run(tt.desc, func(t *testing.T) { + // test + annValue := sidecar.AnnotationValue(tt.ns, tt.pod) -}) + // verify + assert.Equal(t, tt.expected, annValue) + }) + } +} diff --git a/pkg/sidecar/pod_test.go b/pkg/sidecar/pod_test.go index 950f11189e..d7c8795dc2 100644 --- a/pkg/sidecar/pod_test.go +++ b/pkg/sidecar/pod_test.go @@ -15,9 +15,9 @@ package sidecar_test import ( - . "github.com/onsi/ginkgo" - . "github.com/onsi/ginkgo/extensions/table" - . "github.com/onsi/gomega" + "testing" + + "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" logf "sigs.k8s.io/controller-runtime/pkg/log" @@ -28,114 +28,121 @@ import ( "github.com/open-telemetry/opentelemetry-operator/pkg/sidecar" ) -var _ = Describe("Pod", func() { - logger := logf.Log.WithName("unit-tests") +var logger = logf.Log.WithName("unit-tests") - It("should add sidecar when none exists", func() { - // prepare - pod := corev1.Pod{ - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - {Name: "my-app"}, - }, - // cross-test: the pod has a volume already, make sure we don't remove it - Volumes: []corev1.Volume{{}}, +func TestAddSidecarWhenNoSidecarExists(t *testing.T) { + // prepare + pod := corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + {Name: "my-app"}, }, - } - otelcol := v1alpha1.OpenTelemetryCollector{ - ObjectMeta: metav1.ObjectMeta{ - Name: "otelcol-sample", - Namespace: "some-app", + // cross-test: the pod has a volume already, make sure we don't remove it + Volumes: []corev1.Volume{{}}, + }, + } + otelcol := v1alpha1.OpenTelemetryCollector{ + ObjectMeta: metav1.ObjectMeta{ + Name: "otelcol-sample", + Namespace: "some-app", + }, + } + cfg := config.New(config.WithCollectorImage("some-default-image")) + + // test + changed, err := sidecar.Add(cfg, logger, otelcol, pod) + + // verify + assert.NoError(t, err) + assert.Len(t, changed.Spec.Containers, 2) + assert.Len(t, changed.Spec.Volumes, 2) + assert.Equal(t, "some-app.otelcol-sample", changed.Labels["sidecar.opentelemetry.io/injected"]) +} + +// this situation should never happen in the current code path, but it should not fail +// if it's asked to add a new sidecar. The caller is expected to have called ExistsIn before. +func TestAddSidecarWhenOneExistsAlready(t *testing.T) { + // prepare + pod := corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + {Name: "my-app"}, + {Name: naming.Container()}, }, - } - cfg := config.New(config.WithCollectorImage("some-default-image")) - - // test - changed, err := sidecar.Add(cfg, logger, otelcol, pod) - - // verify - Expect(err).ToNot(HaveOccurred()) - Expect(changed.Spec.Containers).To(HaveLen(2)) - Expect(changed.Spec.Volumes).To(HaveLen(2)) - Expect(changed.Labels["sidecar.opentelemetry.io/injected"]).To(Equal("some-app.otelcol-sample")) - }) - - // this situation should never happen in the current code path, but it should not fail - // if it's asked to add a new sidecar. The caller is expected to have called ExistsIn before. - It("should add sidecar, even when one exists", func() { - // prepare - pod := corev1.Pod{ - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - {Name: "my-app"}, - {Name: naming.Container()}, - }, - }, - } - otelcol := v1alpha1.OpenTelemetryCollector{} - cfg := config.New(config.WithCollectorImage("some-default-image")) - - // test - changed, err := sidecar.Add(cfg, logger, otelcol, pod) - - // verify - Expect(err).ToNot(HaveOccurred()) - Expect(changed.Spec.Containers).To(HaveLen(3)) - }) - - It("should remove the sidecar", func() { - // prepare - pod := corev1.Pod{ - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - {Name: "my-app"}, - {Name: naming.Container()}, - {Name: naming.Container()}, // two sidecars! should remove both - }, + }, + } + otelcol := v1alpha1.OpenTelemetryCollector{} + cfg := config.New(config.WithCollectorImage("some-default-image")) + + // test + changed, err := sidecar.Add(cfg, logger, otelcol, pod) + + // verify + assert.NoError(t, err) + assert.Len(t, changed.Spec.Containers, 3) +} + +func TestRemoveSidecar(t *testing.T) { + // prepare + pod := corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + {Name: "my-app"}, + {Name: naming.Container()}, + {Name: naming.Container()}, // two sidecars! should remove both }, - } - - // test - changed, err := sidecar.Remove(pod) - - // verify - Expect(err).ToNot(HaveOccurred()) - Expect(changed.Spec.Containers).To(HaveLen(1)) - }) - - It("should not fail to remove when sidecar doesn't exist", func() { - // prepare - pod := corev1.Pod{ - Spec: corev1.PodSpec{ - Containers: []corev1.Container{ - {Name: "my-app"}, - }, + }, + } + + // test + changed, err := sidecar.Remove(pod) + + // verify + assert.NoError(t, err) + assert.Len(t, changed.Spec.Containers, 1) +} + +func TestRemoveNonExistingSidecar(t *testing.T) { + // prepare + pod := corev1.Pod{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + {Name: "my-app"}, }, - } - - // test - changed, err := sidecar.Remove(pod) - - // verify - Expect(err).ToNot(HaveOccurred()) - Expect(changed.Spec.Containers).To(HaveLen(1)) - }) - - DescribeTable("determine whether the pod has a sidecar already", func(expected bool, pod corev1.Pod) { - Expect(sidecar.ExistsIn(pod)).To(Equal(expected)) - }, - Entry("has-sidecar", true, corev1.Pod{ + }, + } + + // test + changed, err := sidecar.Remove(pod) + + // verify + assert.NoError(t, err) + assert.Len(t, changed.Spec.Containers, 1) +} + +func TestExistsIn(t *testing.T) { + for _, tt := range []struct { + desc string + expected bool + pod corev1.Pod + }{ + {"has-sidecar", true, corev1.Pod{ Spec: corev1.PodSpec{ Containers: []corev1.Container{ {Name: "my-app"}, {Name: naming.Container()}, }, }, - }), - Entry("does-not-have-sidecar", false, corev1.Pod{ + }}, + + {"does-not-have-sidecar", false, corev1.Pod{ Spec: corev1.PodSpec{ Containers: []corev1.Container{}, }, - }), - ) -}) + }}, + } { + t.Run(tt.desc, func(t *testing.T) { + assert.Equal(t, tt.expected, sidecar.ExistsIn(tt.pod)) + }) + } +} diff --git a/pkg/sidecar/sidecar_suite_test.go b/pkg/sidecar/sidecar_suite_test.go deleted file mode 100644 index ec4acb41d5..0000000000 --- a/pkg/sidecar/sidecar_suite_test.go +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright The OpenTelemetry Authors -// -// 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 sidecar_test - -import ( - "testing" - - . "github.com/onsi/ginkgo" - . "github.com/onsi/gomega" -) - -func TestSidecar(t *testing.T) { - RegisterFailHandler(Fail) - RunSpecs(t, "Sidecar Suite") -}