diff --git a/internal/controller/thanosreceive_controller_test.go b/internal/controller/thanosreceive_controller_test.go index c97a3f53..9bc73718 100644 --- a/internal/controller/thanosreceive_controller_test.go +++ b/internal/controller/thanosreceive_controller_test.go @@ -18,7 +18,6 @@ package controller import ( "context" - "fmt" "time" . "github.com/onsi/ginkgo/v2" @@ -29,7 +28,6 @@ import ( "github.com/thanos-community/thanos-operator/internal/pkg/manifests/receive" "github.com/thanos-community/thanos-operator/test/utils" - appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" discoveryv1 "k8s.io/api/discovery/v1" "k8s.io/apimachinery/pkg/api/errors" @@ -336,50 +334,3 @@ config: }) }) - -type expectApiResource string - -const ( - expectApiResourceDeployment expectApiResource = "Deployment" - expectApiResourceStatefulSet expectApiResource = "StatefulSet" -) - -func validateExistenceOfRequiredNamedResources(expectResource expectApiResource, name, ns string) error { - sa := &corev1.ServiceAccount{} - if err := k8sClient.Get(ctx, types.NamespacedName{ - Name: name, - Namespace: ns, - }, sa); err != nil { - return err - } - - svc := &corev1.Service{} - if err := k8sClient.Get(ctx, types.NamespacedName{ - Name: name, - Namespace: ns, - }, svc); err != nil { - return err - } - - switch expectResource { - case expectApiResourceDeployment: - dep := &appsv1.Deployment{} - if err := k8sClient.Get(ctx, types.NamespacedName{ - Name: name, - Namespace: ns, - }, dep); err != nil { - return err - } - case expectApiResourceStatefulSet: - sts := &appsv1.StatefulSet{} - if err := k8sClient.Get(ctx, types.NamespacedName{ - Name: name, - Namespace: ns, - }, sts); err != nil { - return err - } - default: - return fmt.Errorf("unexpected resource type") - } - return nil -} diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go index f4e76f14..9ad04eed 100644 --- a/test/e2e/e2e_test.go +++ b/test/e2e/e2e_test.go @@ -212,7 +212,7 @@ var _ = Describe("controller", Ordered, func() { stsName := receive.IngesterNameFromParent(receiveName, hashringName) Eventually(func() bool { - return utils.VerifyStsReplicasRunning(c, 1, stsName, namespace) + return utils.VerifyStatefulSetReplicasRunning(c, 1, stsName, namespace) }, time.Minute*5, time.Second*10).Should(BeTrue()) }) @@ -259,7 +259,7 @@ var _ = Describe("controller", Ordered, func() { defer cancelFn() Eventually(func() error { - return utils.RemoteWrite(utils.DefaultRemoteWriteRequest()) + return utils.RemoteWrite(utils.DefaultRemoteWriteRequest(), nil, nil) }, time.Minute*1, time.Second*5).Should(Succeed()) }) diff --git a/test/utils/utils.go b/test/utils/utils.go index c53cce7d..409f940c 100644 --- a/test/utils/utils.go +++ b/test/utils/utils.go @@ -205,7 +205,7 @@ func VerifyServiceAccountExists(c client.Client, name string, namespace string) return err == nil } -func VerifyStsReplicasRunning(c client.Client, expect int, name string, namespace string) bool { +func VerifyStatefulSetReplicasRunning(c client.Client, expect int, name string, namespace string) bool { sts := &appsv1.StatefulSet{} err := c.Get(context.Background(), client.ObjectKey{ Name: name, @@ -264,6 +264,18 @@ func VerifyDeploymentArgs(c client.Client, name string, namespace string, contai return slices.Contains(deployment.Spec.Template.Spec.Containers[0].Args, containsArg) } +func VerifyStatefulSetExists(c client.Client, name string, namespace string) bool { + sts := &appsv1.StatefulSet{} + err := c.Get(context.Background(), client.ObjectKey{ + Name: name, + Namespace: namespace, + }, sts) + if err != nil { + return false + } + return true +} + func VerifyConfigMapContents(c client.Client, name, namespace, key, expect string) bool { cm := &corev1.ConfigMap{} if err := c.Get(context.Background(), types.NamespacedName{ @@ -414,3 +426,36 @@ func (s *setHeadersTransport) RoundTrip(req *http.Request) (*http.Response, erro } return s.RoundTripper.RoundTrip(req) } + +type ExpectApiResource string + +const ( + ExpectApiResourceDeployment ExpectApiResource = "Deployment" + ExpectApiResourceStatefulSet ExpectApiResource = "StatefulSet" +) + +// VerifyExistenceOfRequiredNamedResources checks if the required resources exist in the cluster. +// This is a named Service, ServiceAccount, and either a Deployment or StatefulSet. +func VerifyExistenceOfRequiredNamedResources(c client.Client, expectResource ExpectApiResource, name, ns string) bool { + if VerifyServiceAccountExists(c, name, ns) == false { + return false + } + + if VerifyServiceExists(c, name, ns) == false { + return false + } + + switch expectResource { + case ExpectApiResourceDeployment: + if VerifyDeploymentExists(c, name, ns) == false { + return false + } + case ExpectApiResourceStatefulSet: + if VerifyStatefulSetExists(c, name, ns) == false { + return false + } + default: + return false + } + return true +} diff --git a/test/utils/utils_test.go b/test/utils/utils_test.go index 898c3fcf..0902bb00 100644 --- a/test/utils/utils_test.go +++ b/test/utils/utils_test.go @@ -1,18 +1,114 @@ package utils import ( + "net/http" "testing" - corev1 "k8s.io/api/core/v1" - v1 "k8s.io/api/apps/v1" + corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/utils/ptr" "sigs.k8s.io/controller-runtime/pkg/client/fake" ) -func TestVerifyStsReplicasRunning(t *testing.T) { +func TestVerifyServiceExists(t *testing.T) { + const ( + name = "test" + ns = "test" + ) + + svc := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ns, + }, + } + + ok := VerifyServiceExists(fake.NewClientBuilder().WithObjects(svc).Build(), name, ns) + if !ok { + t.Errorf("expected service") + } + + ok = VerifyServiceExists(fake.NewClientBuilder().Build(), name, ns) + if ok { + t.Errorf("unexpected service") + } +} + +func TestVerifyServiceAccountExists(t *testing.T) { + const ( + name = "test" + ns = "test" + ) + + sa := &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ns, + }, + } + + ok := VerifyServiceAccountExists(fake.NewClientBuilder().WithObjects(sa).Build(), name, ns) + if !ok { + t.Errorf("expected serviceaccount") + } + + ok = VerifyServiceAccountExists(fake.NewClientBuilder().Build(), name, ns) + if ok { + t.Errorf("unexpected serviceaccount") + } +} + +func TestVerifyDeploymentExists(t *testing.T) { + const ( + name = "test" + ns = "test" + ) + + d := &v1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ns, + }, + } + + ok := VerifyDeploymentExists(fake.NewClientBuilder().WithObjects(d).Build(), name, ns) + if !ok { + t.Errorf("expected deployment") + } + + ok = VerifyDeploymentExists(fake.NewClientBuilder().Build(), name, ns) + if ok { + t.Errorf("unexpected deployment") + } +} + +func TestVerifyStatefulSetExists(t *testing.T) { + const ( + name = "test" + ns = "test" + ) + + sts := &v1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ns, + }, + } + + ok := VerifyStatefulSetExists(fake.NewClientBuilder().WithObjects(sts).Build(), name, ns) + if !ok { + t.Errorf("expected statefulset") + } + + ok = VerifyStatefulSetExists(fake.NewClientBuilder().Build(), name, ns) + if ok { + t.Errorf("unexpected statefulset") + } +} + +func TestVerifyStatefulSetReplicasRunning(t *testing.T) { const ( name = "test" ns = "test" @@ -39,7 +135,7 @@ func TestVerifyStsReplicasRunning(t *testing.T) { fake.NewClientBuilder().WithObjects(notReady).Build() - ready := VerifyStsReplicasRunning(fake.NewClientBuilder().WithObjects(notReady).Build(), 3, name, ns) + ready := VerifyStatefulSetReplicasRunning(fake.NewClientBuilder().WithObjects(notReady).Build(), 3, name, ns) if ready { t.Errorf("expected not ready statefulset") } @@ -47,12 +143,51 @@ func TestVerifyStsReplicasRunning(t *testing.T) { notReady.Status.ReadyReplicas = 3 notReady.Status.AvailableReplicas = 3 readySts := notReady.DeepCopy() - ready = VerifyStsReplicasRunning(fake.NewClientBuilder().WithObjects(readySts).Build(), 3, name, ns) + ready = VerifyStatefulSetReplicasRunning(fake.NewClientBuilder().WithObjects(readySts).Build(), 3, name, ns) if !ready { t.Errorf("expected ready statefulset") } } +func TestVerifyDeploymentReplicasRunning(t *testing.T) { + const ( + name = "test" + ns = "test" + ) + + notReady := &v1.Deployment{ + TypeMeta: metav1.TypeMeta{ + Kind: "Deployment", + APIVersion: "apps/v1", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ns, + }, + Spec: v1.DeploymentSpec{ + Replicas: ptr.To(int32(3)), + }, + Status: v1.DeploymentStatus{ + Replicas: 3, + ReadyReplicas: 2, + }, + } + + fake.NewClientBuilder().WithObjects(notReady).Build() + + ready := VerifyDeploymentReplicasRunning(fake.NewClientBuilder().WithObjects(notReady).Build(), 3, name, ns) + if ready { + t.Errorf("expected not ready deployment") + } + + notReady.Status.ReadyReplicas = 3 + readyDeployment := notReady.DeepCopy() + ready = VerifyDeploymentReplicasRunning(fake.NewClientBuilder().WithObjects(readyDeployment).Build(), 3, name, ns) + if !ready { + t.Errorf("expected ready deployment") + } +} + func TestVerifyConfigMapContents(t *testing.T) { const ( name = "test" @@ -79,3 +214,125 @@ func TestVerifyConfigMapContents(t *testing.T) { t.Errorf("expected not ready configmap") } } + +func TestVerifyDeploymentArgs(t *testing.T) { + const ( + name = "test" + ns = "test" + ) + + d := &v1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ns, + }, + Spec: v1.DeploymentSpec{ + Template: corev1.PodTemplateSpec{ + Spec: corev1.PodSpec{ + Containers: []corev1.Container{ + { + Name: "test", + Image: "test", + Args: []string{"arg1", "arg2"}, + }, + }, + }, + }, + }, + } + + ok := VerifyDeploymentArgs(fake.NewClientBuilder().WithObjects(d).Build(), name, ns, "arg1") + if !ok { + t.Errorf("expected arg") + } + + ok = VerifyDeploymentArgs(fake.NewClientBuilder().WithObjects(d).Build(), name, ns, "na") + if ok { + t.Errorf("unexpected arg") + } +} + +func TestVerifyExistenceOfRequiredNamedResources(t *testing.T) { + const ( + name = "test" + ns = "test" + ) + + svc := &corev1.Service{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ns, + }, + } + + sa := &corev1.ServiceAccount{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ns, + }, + } + + cm := &corev1.ConfigMap{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ns, + }, + } + + deployment := &v1.Deployment{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ns, + }, + } + + sts := &v1.StatefulSet{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Namespace: ns, + }, + } + + ok := VerifyExistenceOfRequiredNamedResources( + fake.NewClientBuilder().WithObjects(svc, sa, cm, deployment, sts).Build(), + ExpectApiResourceDeployment, + name, ns) + if !ok { + t.Errorf("expected resources") + } + + ok = VerifyExistenceOfRequiredNamedResources( + fake.NewClientBuilder().WithObjects(svc, sa, cm, deployment).Build(), + ExpectApiResourceStatefulSet, + name, ns) + if ok { + t.Errorf("unexpected resources") + } + +} + +func TestRemoteWrite(t *testing.T) { + err := RemoteWrite(DefaultRemoteWriteRequest(), roundTripFunc(func(r *http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: http.StatusBadGateway, + }, nil + }), nil) + if err == nil { + t.Errorf("expected error") + } + + err = RemoteWrite(DefaultRemoteWriteRequest(), roundTripFunc(func(r *http.Request) (*http.Response, error) { + return &http.Response{ + StatusCode: http.StatusOK, + }, nil + }), nil) + if err != nil { + t.Errorf("expected no error") + } +} + +type roundTripFunc func(r *http.Request) (*http.Response, error) + +func (s roundTripFunc) RoundTrip(r *http.Request) (*http.Response, error) { + return s(r) +}