@@ -23,10 +23,12 @@ import (
2323
2424 corev1 "k8s.io/api/core/v1"
2525 "k8s.io/apimachinery/pkg/runtime"
26+ "k8s.io/apimachinery/pkg/util/validation/field"
2627 "k8s.io/utils/pointer"
2728 ctrl "sigs.k8s.io/controller-runtime"
2829 logf "sigs.k8s.io/controller-runtime/pkg/log"
2930 "sigs.k8s.io/controller-runtime/pkg/webhook"
31+ "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
3032
3133 "github.com/project-codeflare/codeflare-operator/pkg/config"
3234)
@@ -40,16 +42,24 @@ func SetupRayClusterWebhookWithManager(mgr ctrl.Manager, cfg *config.KubeRayConf
4042 WithDefaulter (& rayClusterDefaulter {
4143 Config : cfg ,
4244 }).
45+ WithValidator (& rayClusterWebhook {
46+ Config : cfg ,
47+ }).
4348 Complete ()
4449}
4550
4651// +kubebuilder:webhook:path=/mutate-ray-io-v1-raycluster,mutating=true,failurePolicy=fail,sideEffects=None,groups=ray.io,resources=rayclusters,verbs=create;update,versions=v1,name=mraycluster.kb.io,admissionReviewVersions=v1
52+ // +kubebuilder:webhook:path=/validate-ray-io-v1-raycluster,mutating=false,failurePolicy=fail,sideEffects=None,groups=ray.io,resources=rayclusters,verbs=create;update,versions=v1,name=vraycluster.kb.io,admissionReviewVersions=v1
4753
4854type rayClusterDefaulter struct {
4955 Config * config.KubeRayConfiguration
5056}
57+ type rayClusterWebhook struct {
58+ Config * config.KubeRayConfiguration
59+ }
5160
5261var _ webhook.CustomDefaulter = & rayClusterDefaulter {}
62+ var _ webhook.CustomValidator = & rayClusterWebhook {}
5363
5464// Default implements webhook.Defaulter so a webhook will be registered for the type
5565func (r * rayClusterDefaulter ) Default (ctx context.Context , obj runtime.Object ) error {
@@ -132,3 +142,32 @@ func (r *rayClusterDefaulter) Default(ctx context.Context, obj runtime.Object) e
132142
133143 return nil
134144}
145+
146+ func (v * rayClusterWebhook ) ValidateCreate (ctx context.Context , obj runtime.Object ) (admission.Warnings , error ) {
147+ raycluster := obj .(* rayv1.RayCluster )
148+ var warnings admission.Warnings
149+ var allErrors field.ErrorList
150+ specPath := field .NewPath ("spec" )
151+
152+ if pointer .BoolDeref (raycluster .Spec .HeadGroupSpec .EnableIngress , false ) {
153+ rayclusterlog .Info ("Creating RayCluster resources with EnableIngress set to true or unspecified is not allowed" )
154+ allErrors = append (allErrors , field .Invalid (specPath .Child ("headGroupSpec" ).Child ("enableIngress" ), raycluster .Spec .HeadGroupSpec .EnableIngress , "creating RayCluster resources with EnableIngress set to true or unspecified is not allowed" ))
155+ }
156+
157+ return warnings , allErrors .ToAggregate ()
158+ }
159+
160+ func (v * rayClusterWebhook ) ValidateUpdate (ctx context.Context , oldObj , newObj runtime.Object ) (admission.Warnings , error ) {
161+ newRayCluster := newObj .(* rayv1.RayCluster )
162+ if ! newRayCluster .DeletionTimestamp .IsZero () {
163+ // Object is being deleted, skip validations
164+ return nil , nil
165+ }
166+ warnings , err := v .ValidateCreate (ctx , newRayCluster )
167+ return warnings , err
168+ }
169+
170+ func (v * rayClusterWebhook ) ValidateDelete (ctx context.Context , obj runtime.Object ) (admission.Warnings , error ) {
171+ // Optional: Add delete validation logic here
172+ return nil , nil
173+ }
0 commit comments