@@ -23,10 +23,12 @@ import (
23
23
24
24
corev1 "k8s.io/api/core/v1"
25
25
"k8s.io/apimachinery/pkg/runtime"
26
+ "k8s.io/apimachinery/pkg/util/validation/field"
26
27
"k8s.io/utils/pointer"
27
28
ctrl "sigs.k8s.io/controller-runtime"
28
29
logf "sigs.k8s.io/controller-runtime/pkg/log"
29
30
"sigs.k8s.io/controller-runtime/pkg/webhook"
31
+ "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
30
32
31
33
"github.com/project-codeflare/codeflare-operator/pkg/config"
32
34
)
@@ -40,16 +42,24 @@ func SetupRayClusterWebhookWithManager(mgr ctrl.Manager, cfg *config.KubeRayConf
40
42
WithDefaulter (& rayClusterDefaulter {
41
43
Config : cfg ,
42
44
}).
45
+ WithValidator (& rayClusterWebhook {
46
+ Config : cfg ,
47
+ }).
43
48
Complete ()
44
49
}
45
50
46
51
// +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
47
53
48
54
type rayClusterDefaulter struct {
49
55
Config * config.KubeRayConfiguration
50
56
}
57
+ type rayClusterWebhook struct {
58
+ Config * config.KubeRayConfiguration
59
+ }
51
60
52
61
var _ webhook.CustomDefaulter = & rayClusterDefaulter {}
62
+ var _ webhook.CustomValidator = & rayClusterWebhook {}
53
63
54
64
// Default implements webhook.Defaulter so a webhook will be registered for the type
55
65
func (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
132
142
133
143
return nil
134
144
}
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