From ee6cc5998deca7ab56a560d8eb6cd4a7f197ee89 Mon Sep 17 00:00:00 2001 From: Frederiko Costa Date: Sun, 23 Mar 2025 18:53:40 -0700 Subject: [PATCH 1/3] Add minimum cluster size validation Signed-off-by: Frederiko Costa --- api/v1alpha1/etcdcluster_types.go | 1 + config/crd/bases/operator.etcd.io_etcdclusters.yaml | 3 ++- config/manager/kustomization.yaml | 8 +++++++- config/samples/operator_v1alpha1_etcdcluster.yaml | 3 ++- internal/controller/etcdcluster_controller.go | 2 -- 5 files changed, 12 insertions(+), 5 deletions(-) diff --git a/api/v1alpha1/etcdcluster_types.go b/api/v1alpha1/etcdcluster_types.go index c4245f1..77fa75a 100644 --- a/api/v1alpha1/etcdcluster_types.go +++ b/api/v1alpha1/etcdcluster_types.go @@ -33,6 +33,7 @@ type EtcdClusterSpec struct { // Important: Run "make" to regenerate code after modifying this file // Size is the expected size of the etcd cluster. + // +kubebuilder:validation:Minimum=0 Size int `json:"size"` // Version is the expected version of the etcd container image. Version string `json:"version"` diff --git a/config/crd/bases/operator.etcd.io_etcdclusters.yaml b/config/crd/bases/operator.etcd.io_etcdclusters.yaml index 925d43d..4dc00b5 100644 --- a/config/crd/bases/operator.etcd.io_etcdclusters.yaml +++ b/config/crd/bases/operator.etcd.io_etcdclusters.yaml @@ -3,7 +3,7 @@ apiVersion: apiextensions.k8s.io/v1 kind: CustomResourceDefinition metadata: annotations: - controller-gen.kubebuilder.io/version: v0.17.2 + controller-gen.kubebuilder.io/version: v0.17.3 name: etcdclusters.operator.etcd.io spec: group: operator.etcd.io @@ -48,6 +48,7 @@ spec: type: array size: description: Size is the expected size of the etcd cluster. + minimum: 0 type: integer storageSpec: description: StorageSpec is the name of the StorageSpec to use for diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index 2e6cc79..ad13e96 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -1,2 +1,8 @@ resources: -- manager.yaml \ No newline at end of file +- manager.yaml +apiVersion: kustomize.config.k8s.io/v1beta1 +kind: Kustomization +images: +- name: controller + newName: controller + newTag: latest diff --git a/config/samples/operator_v1alpha1_etcdcluster.yaml b/config/samples/operator_v1alpha1_etcdcluster.yaml index ea4d3bf..7780ec6 100644 --- a/config/samples/operator_v1alpha1_etcdcluster.yaml +++ b/config/samples/operator_v1alpha1_etcdcluster.yaml @@ -6,4 +6,5 @@ metadata: app.kubernetes.io/managed-by: kustomize name: etcdcluster-sample spec: - # TODO(user): Add fields here + version: v3.5.20 + size: 3 diff --git a/internal/controller/etcdcluster_controller.go b/internal/controller/etcdcluster_controller.go index 35cc43f..6850f58 100644 --- a/internal/controller/etcdcluster_controller.go +++ b/internal/controller/etcdcluster_controller.go @@ -83,8 +83,6 @@ func (r *EtcdClusterReconciler) Reconcile(ctx context.Context, req ctrl.Request) return ctrl.Result{}, nil } - // TODO: Implement finalizer logic here - logger.Info("Reconciling EtcdCluster", "spec", etcdCluster.Spec) // Get the statefulsets which has the same name as the EtcdCluster resource From f1a38d4b3513220de220c28630fcdcc7a653aff3 Mon Sep 17 00:00:00 2001 From: Frederiko Costa Date: Fri, 9 May 2025 16:44:04 -0700 Subject: [PATCH 2/3] Add e2e to check 0 size cluster Signed-off-by: Frederiko Costa --- config/manager/kustomization.yaml | 4 +-- test/e2e/e2e_test.go | 57 +++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/config/manager/kustomization.yaml b/config/manager/kustomization.yaml index ad13e96..19bca59 100644 --- a/config/manager/kustomization.yaml +++ b/config/manager/kustomization.yaml @@ -4,5 +4,5 @@ apiVersion: kustomize.config.k8s.io/v1beta1 kind: Kustomization images: - name: controller - newName: controller - newTag: latest + newName: etcd-operator + newTag: v0.1 diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go index 2d1c024..be05ad5 100644 --- a/test/e2e/e2e_test.go +++ b/test/e2e/e2e_test.go @@ -28,8 +28,65 @@ import ( appsv1 "k8s.io/api/apps/v1" corev1 "k8s.io/api/core/v1" + "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + + ecv1alpha1 "go.etcd.io/etcd-operator/api/v1alpha1" ) +// TestZeroMemberCluster tests if zero member Etcd Cluster does not create its StatefulSet +func TestZeroMemberCluster(t *testing.T) { + feature := features.New("zero-member-cluster") + etcdClusterName := "etcd-cluster-zero" + size := 0 + + etcdClusterSpec := &ecv1alpha1.EtcdCluster{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "operator.etcd.io/v1alpha1", + Kind: "EtcdCluster", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: etcdClusterName, + Namespace: namespace, + }, + Spec: ecv1alpha1.EtcdClusterSpec{ + Size: size, + Version: "v3.5.18", + }, + } + + feature.Setup(func(ctx context.Context, t *testing.T, c *envconf.Config) context.Context { + + if err := c.Client().Resources().Create(ctx, etcdClusterSpec); err != nil { + t.Fatalf("fail to create Etcd cluster: %s", err) + } + + return ctx + }) + + feature.Assess("statefulSet is not created when etcdCluster.Spec.Size is 0", + func(ctx context.Context, t *testing.T, c *envconf.Config) context.Context { + + var etcdCluster ecv1alpha1.EtcdCluster + if err := c.Client().Resources().Get(ctx, etcdClusterName, namespace, &etcdCluster); err != nil { + t.Fatalf("unable to fetch Etcd cluster: %s", err) + } + + var sts appsv1.StatefulSet + err := c.Client().Resources().Get(ctx, etcdClusterName, namespace, &sts) + + if !errors.IsNotFound(err) { + t.Fatalf("statefulSet found when Etcd Cluster size is zero: %s", err) + } + + return ctx + }, + ) + + _ = testEnv.Test(t, feature.Feature()) + +} + func TestClusterHealthy(t *testing.T) { feature := features.New("etcd-operator-controller") From 4b2835e5f005c2b62ac6cabfce3496f3afcbb478 Mon Sep 17 00:00:00 2001 From: Frederiko Costa Date: Mon, 12 May 2025 09:33:28 -0700 Subject: [PATCH 3/3] Add test for negative cluster creation Signed-off-by: Frederiko Costa --- test/e2e/e2e_test.go | 53 +++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 52 insertions(+), 1 deletion(-) diff --git a/test/e2e/e2e_test.go b/test/e2e/e2e_test.go index be05ad5..019e3f0 100644 --- a/test/e2e/e2e_test.go +++ b/test/e2e/e2e_test.go @@ -18,6 +18,7 @@ package e2e import ( "context" + "os" "testing" "time" @@ -34,7 +35,10 @@ import ( ecv1alpha1 "go.etcd.io/etcd-operator/api/v1alpha1" ) +var etcdVersion = os.Getenv("ETCD_VERSION") + // TestZeroMemberCluster tests if zero member Etcd Cluster does not create its StatefulSet +// TODO: update this test once https://github.com/etcd-io/etcd-operator/issues/125 is addressed func TestZeroMemberCluster(t *testing.T) { feature := features.New("zero-member-cluster") etcdClusterName := "etcd-cluster-zero" @@ -51,7 +55,7 @@ func TestZeroMemberCluster(t *testing.T) { }, Spec: ecv1alpha1.EtcdClusterSpec{ Size: size, - Version: "v3.5.18", + Version: etcdVersion, }, } @@ -87,6 +91,53 @@ func TestZeroMemberCluster(t *testing.T) { } +// TestNegativeClusterSize tests negative membership cluster creation +func TestNegativeClusterSize(t *testing.T) { + feature := features.New("negative-member-cluster") + etcdClusterName := "etcd-cluster-negative" + size := -1 + + etcdClusterSpec := &ecv1alpha1.EtcdCluster{ + TypeMeta: metav1.TypeMeta{ + APIVersion: "operator.etcd.io/v1alpha1", + Kind: "EtcdCluster", + }, + ObjectMeta: metav1.ObjectMeta{ + Name: etcdClusterName, + Namespace: namespace, + }, + Spec: ecv1alpha1.EtcdClusterSpec{ + Size: size, + Version: etcdVersion, + }, + } + + feature.Setup(func(ctx context.Context, t *testing.T, c *envconf.Config) context.Context { + + err := c.Client().Resources().Create(ctx, etcdClusterSpec) + if !errors.IsInvalid(err) { + t.Fatalf("etcdCluster with negative size failed with unexpected error: %s", err) + } + + return ctx + }) + + feature.Assess("etcdCluster resource should not be created when etcdCluster.Spec.Size is negative", + func(ctx context.Context, t *testing.T, c *envconf.Config) context.Context { + + var etcdCluster ecv1alpha1.EtcdCluster + err := c.Client().Resources().Get(ctx, etcdClusterName, namespace, &etcdCluster) + if !errors.IsNotFound(err) { + t.Fatalf("found etcdCluster resource with negative size: %s", err) + } + + return ctx + }, + ) + + _ = testEnv.Test(t, feature.Feature()) +} + func TestClusterHealthy(t *testing.T) { feature := features.New("etcd-operator-controller")