Skip to content

Commit fefd300

Browse files
authored
feat: Implement RedisCluster clusterSize validation webhook (#1458)
Signed-off-by: yangw <[email protected]>
1 parent cc0c9b0 commit fefd300

File tree

4 files changed

+148
-16
lines changed

4 files changed

+148
-16
lines changed

api/rediscluster/v1beta2/rediscluster_webhook.go

Lines changed: 71 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,15 +17,84 @@ limitations under the License.
1717
package v1beta2
1818

1919
import (
20+
apierrors "k8s.io/apimachinery/pkg/api/errors"
21+
"k8s.io/apimachinery/pkg/runtime"
22+
"k8s.io/apimachinery/pkg/runtime/schema"
23+
"k8s.io/apimachinery/pkg/util/validation/field"
2024
ctrl "sigs.k8s.io/controller-runtime"
25+
logf "sigs.k8s.io/controller-runtime/pkg/log"
26+
"sigs.k8s.io/controller-runtime/pkg/webhook"
27+
"sigs.k8s.io/controller-runtime/pkg/webhook/admission"
2128
)
2229

23-
//+kubebuilder:webhook:path=/mutate-core-v1-pod,mutating=true,failurePolicy=fail,sideEffects=None,groups=core,resources=pods,verbs=create,versions=v1,name=ot-mutate-pod.opstree.com,admissionReviewVersions=v1
30+
const (
31+
webhookPath = "/validate-redis-redis-opstreelabs-in-v1beta2-rediscluster"
32+
)
33+
34+
// log is for logging in this package.
35+
var redisclusterlog = logf.Log.WithName("rediscluster-v1beta2-validation")
36+
37+
// +kubebuilder:webhook:path=/validate-redis-redis-opstreelabs-in-v1beta2-rediscluster,mutating=false,failurePolicy=fail,sideEffects=None,groups=redis.redis.opstreelabs.in,resources=redisclusters,verbs=create;update,versions=v1beta2,name=validate-rediscluster.redis.opstreelabs.in,admissionReviewVersions=v1
2438

39+
// SetupWebhookWithManager will setup the manager
2540
func (r *RedisCluster) SetupWebhookWithManager(mgr ctrl.Manager) error {
2641
return ctrl.NewWebhookManagedBy(mgr).
2742
For(r).
2843
Complete()
2944
}
3045

31-
// TODO(user): EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN!
46+
var _ webhook.Validator = &RedisCluster{}
47+
48+
// ValidateCreate implements webhook.Validator so a webhook will be registered for the type
49+
func (r *RedisCluster) ValidateCreate() (admission.Warnings, error) {
50+
redisclusterlog.Info("validate create", "name", r.Name)
51+
52+
return r.validate(nil)
53+
}
54+
55+
// ValidateUpdate implements webhook.Validator so a webhook will be registered for the type
56+
func (r *RedisCluster) ValidateUpdate(old runtime.Object) (admission.Warnings, error) {
57+
redisclusterlog.Info("validate update", "name", r.Name)
58+
59+
return r.validate(old.(*RedisCluster))
60+
}
61+
62+
// ValidateDelete implements webhook.Validator so a webhook will be registered for the type
63+
func (r *RedisCluster) ValidateDelete() (admission.Warnings, error) {
64+
redisclusterlog.Info("validate delete", "name", r.Name)
65+
66+
return nil, nil
67+
}
68+
69+
// validate validates the Redis Cluster CR
70+
func (r *RedisCluster) validate(_ *RedisCluster) (admission.Warnings, error) {
71+
var errors field.ErrorList
72+
var warnings admission.Warnings
73+
74+
if r.Spec.Size == nil {
75+
return warnings, nil
76+
}
77+
78+
// Check if the Size is at least 3 for proper cluster operation
79+
if *r.Spec.Size < 3 {
80+
errors = append(errors, field.Invalid(
81+
field.NewPath("spec").Child("clusterSize"),
82+
*r.Spec.Size,
83+
"Redis cluster must have at least 3 shards",
84+
))
85+
}
86+
87+
if len(errors) == 0 {
88+
return nil, nil
89+
}
90+
91+
return nil, apierrors.NewInvalid(
92+
schema.GroupKind{Group: "redis.redis.opstreelabs.in", Kind: "RedisCluster"},
93+
r.Name,
94+
errors,
95+
)
96+
}
97+
98+
func (r *RedisCluster) WebhookPath() string {
99+
return webhookPath
100+
}
Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
package v1beta2_test
2+
3+
import (
4+
"encoding/json"
5+
"fmt"
6+
"testing"
7+
8+
v1beta2 "github.com/OT-CONTAINER-KIT/redis-operator/api/rediscluster/v1beta2"
9+
"github.com/OT-CONTAINER-KIT/redis-operator/internal/testutil/webhook"
10+
"github.com/stretchr/testify/require"
11+
admissionv1beta1 "k8s.io/api/admission/v1beta1"
12+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
13+
"k8s.io/apimachinery/pkg/types"
14+
"k8s.io/utils/ptr"
15+
)
16+
17+
func TestRedisClusterWebhook(t *testing.T) {
18+
cases := []webhook.ValidationWebhookTestCase{
19+
{
20+
Name: "success-create-v1beta2-rediscluster-validate-clusterSize-3",
21+
Operation: admissionv1beta1.Create,
22+
Object: func(t *testing.T, uid string) []byte {
23+
t.Helper()
24+
cluster := mkRedisCluster(uid)
25+
cluster.Spec.Size = ptr.To(int32(3))
26+
return marshal(t, cluster)
27+
},
28+
Check: webhook.ValidationWebhookSucceeded,
29+
},
30+
{
31+
Name: "failed-create-v1beta2-rediscluster-validate-clusterSize-2",
32+
Operation: admissionv1beta1.Create,
33+
Object: func(t *testing.T, uid string) []byte {
34+
t.Helper()
35+
cluster := mkRedisCluster(uid)
36+
cluster.Spec.Size = ptr.To(int32(2))
37+
return marshal(t, cluster)
38+
},
39+
Check: webhook.ValidationWebhookFailed("Redis cluster must have at least 3 shards"),
40+
},
41+
}
42+
43+
gvk := metav1.GroupVersionKind{
44+
Group: "redis.redis.opstreelabs.in",
45+
Version: "v1beta2",
46+
Kind: "RedisCluster",
47+
}
48+
49+
cluster := &v1beta2.RedisCluster{}
50+
webhook.RunValidationWebhookTests(t, gvk, cluster, cases...)
51+
}
52+
53+
func mkRedisCluster(uid string) *v1beta2.RedisCluster {
54+
return &v1beta2.RedisCluster{
55+
ObjectMeta: metav1.ObjectMeta{
56+
Name: fmt.Sprintf("test-%s", uid),
57+
UID: types.UID(fmt.Sprintf("test-%s", uid)),
58+
},
59+
Spec: v1beta2.RedisClusterSpec{},
60+
}
61+
}
62+
63+
func marshal(t *testing.T, obj interface{}) []byte {
64+
t.Helper()
65+
bytes, err := json.Marshal(obj)
66+
require.NoError(t, err)
67+
return bytes
68+
}

api/rediscluster/v1beta2/zz_generated.deepcopy.go

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

config/webhook/manifests.yaml

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,29 @@
11
---
22
apiVersion: admissionregistration.k8s.io/v1
3-
kind: MutatingWebhookConfiguration
3+
kind: ValidatingWebhookConfiguration
44
metadata:
5-
name: mutating-webhook-configuration
5+
name: validating-webhook-configuration
66
webhooks:
77
- admissionReviewVersions:
88
- v1
99
clientConfig:
1010
service:
1111
name: webhook-service
1212
namespace: system
13-
path: /mutate-core-v1-pod
13+
path: /validate-redis-redis-opstreelabs-in-v1beta2-rediscluster
1414
failurePolicy: Fail
15-
name: ot-mutate-pod.opstree.com
15+
name: validate-rediscluster.redis.opstreelabs.in
1616
rules:
1717
- apiGroups:
18-
- ""
18+
- redis.redis.opstreelabs.in
1919
apiVersions:
20-
- v1
20+
- v1beta2
2121
operations:
2222
- CREATE
23+
- UPDATE
2324
resources:
24-
- pods
25+
- redisclusters
2526
sideEffects: None
26-
---
27-
apiVersion: admissionregistration.k8s.io/v1
28-
kind: ValidatingWebhookConfiguration
29-
metadata:
30-
name: validating-webhook-configuration
31-
webhooks:
3227
- admissionReviewVersions:
3328
- v1
3429
clientConfig:

0 commit comments

Comments
 (0)