Skip to content

Commit 2e25f81

Browse files
authored
Add validation minimum and change type to *int32 for compatibility (#57)
- `Replicas` field type changed to *int32 according to k8s api guidelines - remove unnecessary validation (everything is validated using OpenAPI scheme) - allow creation of cluster with 0 replicas fixes #51
1 parent dfd4c74 commit 2e25f81

File tree

7 files changed

+33
-33
lines changed

7 files changed

+33
-33
lines changed

api/v1alpha1/etcdcluster_types.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,8 @@ type EtcdClusterSpec struct {
3131
// Replicas is the count of etcd instances in cluster.
3232
// +optional
3333
// +kubebuilder:default:=3
34-
Replicas uint `json:"replicas,omitempty"`
34+
// +kubebuilder:validation:Minimum:=0
35+
Replicas *int32 `json:"replicas,omitempty"`
3536
Storage Storage `json:"storage,omitempty"`
3637
}
3738

api/v1alpha1/etcdcluster_webhook.go

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -42,9 +42,6 @@ var _ webhook.Defaulter = &EtcdCluster{}
4242
// Default implements webhook.Defaulter so a webhook will be registered for the type
4343
func (r *EtcdCluster) Default() {
4444
etcdclusterlog.Info("default", "name", r.Name)
45-
if r.Spec.Replicas == 0 {
46-
r.Spec.Replicas = 3
47-
}
4845
if r.Spec.Storage.Size.IsZero() {
4946
r.Spec.Storage.Size = resource.MustParse("4Gi")
5047
}
@@ -67,11 +64,7 @@ func (r *EtcdCluster) ValidateUpdate(old runtime.Object) (admission.Warnings, er
6764
if old.(*EtcdCluster).Spec.Replicas != r.Spec.Replicas {
6865
warnings = append(warnings, "cluster resize is not currently supported")
6966
}
70-
71-
if len(warnings) > 0 {
72-
return warnings, nil
73-
}
74-
return nil, nil
67+
return warnings, nil
7568
}
7669

7770
// ValidateDelete implements webhook.Validator so a webhook will be registered for the type

api/v1alpha1/etcdcluster_webhook_test.go

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import (
2020
. "github.com/onsi/ginkgo/v2"
2121
"github.com/onsi/gomega"
2222
"k8s.io/apimachinery/pkg/api/resource"
23+
"k8s.io/utils/ptr"
2324
)
2425

2526
var _ = Describe("EtcdCluster Webhook", func() {
@@ -28,40 +29,37 @@ var _ = Describe("EtcdCluster Webhook", func() {
2829
It("Should fill in the default value if a required field is empty", func() {
2930
etcdCluster := &EtcdCluster{}
3031
etcdCluster.Default()
31-
gomega.Expect(etcdCluster.Spec.Replicas).To(gomega.Equal(uint(3)))
32+
gomega.Expect(etcdCluster.Spec.Replicas).To(gomega.BeNil(), "User should have an opportunity to create cluster with 0 replicas")
3233
gomega.Expect(etcdCluster.Spec.Storage.Size).To(gomega.Equal(resource.MustParse("4Gi")))
3334
})
3435

3536
It("Should not override fields with default values if not empty", func() {
3637
etcdCluster := &EtcdCluster{
3738
Spec: EtcdClusterSpec{
38-
Replicas: 5,
39+
Replicas: ptr.To(int32(5)),
3940
Storage: Storage{
4041
StorageClass: "local-path",
4142
Size: resource.MustParse("10Gi"),
4243
},
4344
},
4445
}
4546
etcdCluster.Default()
46-
gomega.Expect(etcdCluster.Spec.Replicas).To(gomega.Equal(uint(5)))
47+
gomega.Expect(*etcdCluster.Spec.Replicas).To(gomega.Equal(int32(5)))
4748
gomega.Expect(etcdCluster.Spec.Storage.Size).To(gomega.Equal(resource.MustParse("10Gi")))
4849
})
4950
})
5051

51-
// Not yet applicable as currently all fields are optional.
52-
53-
//Context("When creating EtcdCluster under Validating Webhook", func() {
54-
// It("Should deny if a required field is empty", func() {
55-
//
56-
// // TODO(user): Add your logic here
57-
//
58-
// })
59-
//
60-
// It("Should admit if all required fields are provided", func() {
61-
//
62-
// // TODO(user): Add your logic here
63-
//
64-
// })
65-
//})
52+
Context("When creating EtcdCluster under Validating Webhook", func() {
53+
It("Should admit if all required fields are provided", func() {
54+
etcdCluster := &EtcdCluster{
55+
Spec: EtcdClusterSpec{
56+
Replicas: ptr.To(int32(1)),
57+
},
58+
}
59+
w, err := etcdCluster.ValidateCreate()
60+
gomega.Expect(err).To(gomega.Succeed())
61+
gomega.Expect(w).To(gomega.BeEmpty())
62+
})
63+
})
6664

6765
})

api/v1alpha1/zz_generated.deepcopy.go

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/crd/bases/etcd.aenix.io_etcdclusters.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ spec:
4242
replicas:
4343
default: 3
4444
description: Replicas is the count of etcd instances in cluster.
45+
format: int32
46+
minimum: 0
4547
type: integer
4648
storage:
4749
properties:

internal/controller/etcdcluster_controller.go

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -265,7 +265,7 @@ func (r *EtcdClusterReconciler) ensureClusterStateConfigMap(
265265
// configmap does not exist, create with cluster state "new"
266266
if errors.IsNotFound(err) {
267267
initialCluster := ""
268-
for i := uint(0); i < cluster.Spec.Replicas; i++ {
268+
for i := int32(0); i < *cluster.Spec.Replicas; i++ {
269269
if i > 0 {
270270
initialCluster += ","
271271
}
@@ -319,7 +319,7 @@ func (r *EtcdClusterReconciler) ensureClusterStatefulSet(
319319
},
320320
Spec: appsv1.StatefulSetSpec{
321321
// initialize static fields that cannot be changed across updates.
322-
Replicas: new(int32),
322+
Replicas: cluster.Spec.Replicas,
323323
ServiceName: cluster.Name,
324324
PodManagementPolicy: appsv1.ParallelPodManagement,
325325
Selector: &metav1.LabelSelector{
@@ -439,7 +439,6 @@ func (r *EtcdClusterReconciler) ensureClusterStatefulSet(
439439
},
440440
},
441441
}
442-
*statefulSet.Spec.Replicas = int32(cluster.Spec.Replicas)
443442
if err := ctrl.SetControllerReference(cluster, statefulSet, r.Scheme); err != nil {
444443
return fmt.Errorf("cannot set controller reference: %w", err)
445444
}

internal/controller/etcdcluster_controller_test.go

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ package controller
1919
import (
2020
"context"
2121

22+
"k8s.io/utils/ptr"
23+
2224
appsv1 "k8s.io/api/apps/v1"
2325
v1 "k8s.io/api/core/v1"
2426
resource2 "k8s.io/apimachinery/pkg/api/resource"
@@ -56,7 +58,7 @@ var _ = Describe("EtcdCluster Controller", func() {
5658
Namespace: "default",
5759
},
5860
Spec: etcdaenixiov1alpha1.EtcdClusterSpec{
59-
Replicas: 3,
61+
Replicas: ptr.To(int32(3)),
6062
Storage: etcdaenixiov1alpha1.Storage{
6163
Size: resource2.MustParse("1Gi"),
6264
},
@@ -142,8 +144,8 @@ var _ = Describe("EtcdCluster Controller", func() {
142144
err = k8sClient.Get(ctx, typeNamespacedName, sts)
143145
Expect(err).NotTo(HaveOccurred(), "cluster statefulset should exist")
144146
// mark sts as ready
145-
sts.Status.ReadyReplicas = int32(etcdcluster.Spec.Replicas)
146-
sts.Status.Replicas = int32(etcdcluster.Spec.Replicas)
147+
sts.Status.ReadyReplicas = *etcdcluster.Spec.Replicas
148+
sts.Status.Replicas = *etcdcluster.Spec.Replicas
147149
Expect(k8sClient.Status().Update(ctx, sts)).To(Succeed())
148150
// reconcile and check EtcdCluster status
149151
_, err = controllerReconciler.Reconcile(ctx, reconcile.Request{

0 commit comments

Comments
 (0)