@@ -61,88 +61,54 @@ var _ webhook.CustomValidator = &rayClusterWebhook{}
61
61
func (w * rayClusterWebhook ) Default (ctx context.Context , obj runtime.Object ) error {
62
62
rayCluster := obj .(* rayv1.RayCluster )
63
63
64
- if ! pointer .BoolDeref (w .Config .RayDashboardOAuthEnabled , true ) {
65
- return nil
64
+ oauthExists := false
65
+ initHeadExists := false
66
+ initWorkerExists := false
67
+
68
+ // Check for the create-cert Init Containers
69
+ for _ , container := range rayCluster .Spec .HeadGroupSpec .Template .Spec .InitContainers {
70
+ if container .Name == "create-cert" {
71
+ rayclusterlog .V (2 ).Info ("Head Init Containers already exist, no patch needed" )
72
+ initHeadExists = true
73
+ break // exits the for loop
74
+ }
75
+ }
76
+ // Check fot the create-cert Init Container WorkerGroupSpec
77
+ for _ , container := range rayCluster .Spec .WorkerGroupSpecs [0 ].Template .Spec .InitContainers {
78
+ if container .Name == "create-cert" {
79
+ rayclusterlog .V (2 ).Info ("Worker Init Containers already exist, no patch needed" )
80
+ initWorkerExists = true
81
+ break // exits the for loop
82
+ }
66
83
}
67
84
68
85
// Check and add OAuth proxy if it does not exist
69
86
for _ , container := range rayCluster .Spec .HeadGroupSpec .Template .Spec .Containers {
70
87
if container .Name == "oauth-proxy" {
71
88
rayclusterlog .V (2 ).Info ("OAuth sidecar already exists, no patch needed" )
72
- return nil
89
+ oauthExists = true
73
90
}
74
91
}
75
92
76
- rayclusterlog .V (2 ).Info ("Adding OAuth sidecar container" )
77
-
78
- newOAuthSidecar := corev1.Container {
79
- Name : "oauth-proxy" ,
80
- Image : "registry.redhat.io/openshift4/ose-oauth-proxy@sha256:1ea6a01bf3e63cdcf125c6064cbd4a4a270deaf0f157b3eabb78f60556840366" ,
81
- Ports : []corev1.ContainerPort {
82
- {ContainerPort : 8443 , Name : "oauth-proxy" },
83
- },
84
- Env : []corev1.EnvVar {
85
- {
86
- Name : "COOKIE_SECRET" ,
87
- ValueFrom : & corev1.EnvVarSource {
88
- SecretKeyRef : & corev1.SecretKeySelector {
89
- LocalObjectReference : corev1.LocalObjectReference {
90
- Name : rayCluster .Name + "-oauth-config" ,
91
- },
92
- Key : "cookie_secret" ,
93
- },
94
- },
95
- },
96
- },
97
- Args : []string {
98
- "--https-address=:8443" ,
99
- "--provider=openshift" ,
100
- "--openshift-service-account=" + rayCluster .Name + "-oauth-proxy" ,
101
- "--upstream=http://localhost:8265" ,
102
- "--tls-cert=/etc/tls/private/tls.crt" ,
103
- "--tls-key=/etc/tls/private/tls.key" ,
104
- "--cookie-secret=$(COOKIE_SECRET)" ,
105
- "--openshift-delegate-urls={\" /\" :{\" resource\" :\" pods\" ,\" namespace\" :\" default\" ,\" verb\" :\" get\" }}" ,
106
- },
107
- VolumeMounts : []corev1.VolumeMount {
108
- {
109
- Name : "proxy-tls-secret" ,
110
- MountPath : "/etc/tls/private" ,
111
- ReadOnly : true ,
112
- },
113
- },
114
- }
115
-
116
- rayCluster .Spec .HeadGroupSpec .Template .Spec .Containers = append (rayCluster .Spec .HeadGroupSpec .Template .Spec .Containers , newOAuthSidecar )
117
-
118
- tlsSecretVolume := corev1.Volume {
119
- Name : "proxy-tls-secret" ,
120
- VolumeSource : corev1.VolumeSource {
121
- Secret : & corev1.SecretVolumeSource {
122
- SecretName : rayCluster .Name + "-proxy-tls-secret" ,
123
- },
124
- },
93
+ if pointer .BoolDeref (w .Config .RayDashboardOAuthEnabled , true ) {
94
+ oauthPatch (rayCluster , oauthExists )
125
95
}
126
-
127
- rayCluster .Spec .HeadGroupSpec .Template .Spec .Volumes = append (rayCluster .Spec .HeadGroupSpec .Template .Spec .Volumes , tlsSecretVolume )
128
-
129
- // Ensure the service account is set
130
- if rayCluster .Spec .HeadGroupSpec .Template .Spec .ServiceAccountName == "" {
131
- rayCluster .Spec .HeadGroupSpec .Template .Spec .ServiceAccountName = rayCluster .Name + "-oauth-proxy"
96
+ if pointer .BoolDeref (w .Config .MTLSEnabled , true ) {
97
+ w .mTLSPatch (rayCluster , initHeadExists , initWorkerExists )
132
98
}
133
99
134
100
return nil
135
101
}
136
102
137
103
func (w * rayClusterWebhook ) ValidateCreate (ctx context.Context , obj runtime.Object ) (admission.Warnings , error ) {
138
- raycluster := obj .(* rayv1.RayCluster )
104
+ rayCluster := obj .(* rayv1.RayCluster )
139
105
var warnings admission.Warnings
140
106
var allErrors field.ErrorList
141
107
specPath := field .NewPath ("spec" )
142
108
143
- if pointer .BoolDeref (raycluster .Spec .HeadGroupSpec .EnableIngress , false ) {
109
+ if pointer .BoolDeref (rayCluster .Spec .HeadGroupSpec .EnableIngress , false ) {
144
110
rayclusterlog .Info ("Creating RayCluster resources with EnableIngress set to true or unspecified is not allowed" )
145
- 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" ))
111
+ 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" ))
146
112
}
147
113
148
114
return warnings , allErrors .ToAggregate ()
@@ -162,3 +128,184 @@ func (w *rayClusterWebhook) ValidateDelete(ctx context.Context, obj runtime.Obje
162
128
// Optional: Add delete validation logic here
163
129
return nil , nil
164
130
}
131
+ func oauthPatch (rayCluster * rayv1.RayCluster , oauthExists bool ) {
132
+ if ! oauthExists {
133
+ rayclusterlog .V (2 ).Info ("Adding OAuth sidecar container" )
134
+
135
+ newOAuthSidecar := corev1.Container {
136
+ Name : "oauth-proxy" ,
137
+ Image : "registry.redhat.io/openshift4/ose-oauth-proxy@sha256:1ea6a01bf3e63cdcf125c6064cbd4a4a270deaf0f157b3eabb78f60556840366" ,
138
+ Ports : []corev1.ContainerPort {
139
+ {ContainerPort : 8443 , Name : "oauth-proxy" },
140
+ },
141
+ Env : []corev1.EnvVar {
142
+ {
143
+ Name : "COOKIE_SECRET" ,
144
+ ValueFrom : & corev1.EnvVarSource {
145
+ SecretKeyRef : & corev1.SecretKeySelector {
146
+ LocalObjectReference : corev1.LocalObjectReference {
147
+ Name : rayCluster .Name + "-oauth-config" ,
148
+ },
149
+ Key : "cookie_secret" ,
150
+ },
151
+ },
152
+ },
153
+ },
154
+ Args : []string {
155
+ "--https-address=:8443" ,
156
+ "--provider=openshift" ,
157
+ "--openshift-service-account=" + rayCluster .Name + "-oauth-proxy" ,
158
+ "--upstream=http://localhost:8265" ,
159
+ "--tls-cert=/etc/tls/private/tls.crt" ,
160
+ "--tls-key=/etc/tls/private/tls.key" ,
161
+ "--cookie-secret=$(COOKIE_SECRET)" ,
162
+ "--openshift-delegate-urls={\" /\" :{\" resource\" :\" pods\" ,\" namespace\" :\" default\" ,\" verb\" :\" get\" }}" ,
163
+ },
164
+ VolumeMounts : []corev1.VolumeMount {
165
+ {
166
+ Name : "proxy-tls-secret" ,
167
+ MountPath : "/etc/tls/private" ,
168
+ ReadOnly : true ,
169
+ },
170
+ },
171
+ }
172
+
173
+ rayCluster .Spec .HeadGroupSpec .Template .Spec .Containers = append (rayCluster .Spec .HeadGroupSpec .Template .Spec .Containers , newOAuthSidecar )
174
+
175
+ tlsSecretVolume := corev1.Volume {
176
+ Name : "proxy-tls-secret" ,
177
+ VolumeSource : corev1.VolumeSource {
178
+ Secret : & corev1.SecretVolumeSource {
179
+ SecretName : rayCluster .Name + "-proxy-tls-secret" ,
180
+ },
181
+ },
182
+ }
183
+
184
+ rayCluster .Spec .HeadGroupSpec .Template .Spec .Volumes = append (rayCluster .Spec .HeadGroupSpec .Template .Spec .Volumes , tlsSecretVolume )
185
+
186
+ // Ensure the service account is set
187
+ if rayCluster .Spec .HeadGroupSpec .Template .Spec .ServiceAccountName == "" {
188
+ rayCluster .Spec .HeadGroupSpec .Template .Spec .ServiceAccountName = rayCluster .Name + "-oauth-proxy"
189
+ }
190
+ }
191
+ }
192
+
193
+ func (w * rayClusterWebhook ) mTLSPatch (rayCluster * rayv1.RayCluster , initHeadExists bool , initWorkerExists bool ) {
194
+ rayclusterlog .V (2 ).Info ("creating json patch for RayCluster initContainers" )
195
+
196
+ // Volume Mounts for the Init Containers
197
+ key_volumes := []corev1.VolumeMount {
198
+ {
199
+ Name : "ca-vol" ,
200
+ MountPath : "/home/ray/workspace/ca" ,
201
+ ReadOnly : true ,
202
+ },
203
+ {
204
+ Name : "server-cert" ,
205
+ MountPath : "/home/ray/workspace/tls" ,
206
+ ReadOnly : false ,
207
+ },
208
+ }
209
+
210
+ // Service name for basic interactive
211
+ svcDomain := rayCluster .Name + "-head-svc." + rayCluster .Namespace + ".svc"
212
+ // Ca Secret generated by the SDK
213
+ secretName := `ca-secret-` + rayCluster .Name
214
+
215
+ // Env variables for Worker & Head Containers
216
+ envList := []corev1.EnvVar {
217
+ {
218
+ Name : "MY_POD_IP" ,
219
+ ValueFrom : & corev1.EnvVarSource {
220
+ FieldRef : & corev1.ObjectFieldSelector {
221
+ FieldPath : "status.podIP" ,
222
+ },
223
+ },
224
+ },
225
+ {
226
+ Name : "RAY_USE_TLS" ,
227
+ Value : "1" ,
228
+ },
229
+ {
230
+ Name : "RAY_TLS_SERVER_CERT" ,
231
+ Value : "/home/ray/workspace/tls/server.crt" ,
232
+ },
233
+ {
234
+ Name : "RAY_TLS_SERVER_KEY" ,
235
+ Value : "/home/ray/workspace/tls/server.key" ,
236
+ },
237
+ {
238
+ Name : "RAY_TLS_CA_CERT" ,
239
+ Value : "/home/ray/workspace/tls/ca.crt" ,
240
+ },
241
+ }
242
+
243
+ // Volumes for the main container of Head and worker
244
+ caVolumes := []corev1.Volume {
245
+ {
246
+ Name : "ca-vol" ,
247
+ VolumeSource : corev1.VolumeSource {
248
+ Secret : & corev1.SecretVolumeSource {
249
+ SecretName : secretName ,
250
+ },
251
+ },
252
+ },
253
+ {
254
+ Name : "server-cert" ,
255
+ VolumeSource : corev1.VolumeSource {
256
+ EmptyDir : & corev1.EmptyDirVolumeSource {},
257
+ },
258
+ },
259
+ }
260
+
261
+ if ! initHeadExists {
262
+ rayClientRoute := "rayclient-" + rayCluster .Name + "-" + rayCluster .Namespace + "." + w .Config .IngressDomain
263
+ initContainerHead := corev1.Container {
264
+ Name : "create-cert" ,
265
+ Image : "quay.io/project-codeflare/ray:latest-py39-cu118" ,
266
+ Command : []string {
267
+ "sh" ,
268
+ "-c" ,
269
+ `cd /home/ray/workspace/tls && openssl req -nodes -newkey rsa:2048 -keyout server.key -out server.csr -subj '/CN=ray-head' && printf "authorityKeyIdentifier=keyid,issuer\nbasicConstraints=CA:FALSE\nsubjectAltName = @alt_names\n[alt_names]\nDNS.1 = 127.0.0.1\nDNS.2 = localhost\nDNS.3 = ${FQ_RAY_IP}\nDNS.4 = $(awk 'END{print $1}' /etc/hosts)\nDNS.5 = ` + rayClientRoute + `\nDNS.6 = ` + svcDomain + `">./domain.ext && cp /home/ray/workspace/ca/* . && openssl x509 -req -CA ca.crt -CAkey ca.key -in server.csr -out server.crt -days 365 -CAcreateserial -extfile domain.ext` ,
270
+ },
271
+ VolumeMounts : key_volumes ,
272
+ }
273
+
274
+ // Append the list of environment variables for the ray-head container
275
+ for index , container := range rayCluster .Spec .HeadGroupSpec .Template .Spec .Containers {
276
+ if container .Name == "ray-head" {
277
+ rayCluster .Spec .HeadGroupSpec .Template .Spec .Containers [index ].Env = append (rayCluster .Spec .HeadGroupSpec .Template .Spec .Containers [index ].Env , envList ... )
278
+ }
279
+ }
280
+
281
+ // Append the create-cert Init Container
282
+ rayCluster .Spec .HeadGroupSpec .Template .Spec .InitContainers = append (rayCluster .Spec .HeadGroupSpec .Template .Spec .InitContainers , initContainerHead )
283
+
284
+ // Append the CA volumes
285
+ rayCluster .Spec .HeadGroupSpec .Template .Spec .Volumes = append (rayCluster .Spec .HeadGroupSpec .Template .Spec .Volumes , caVolumes ... )
286
+ }
287
+
288
+ if ! initWorkerExists {
289
+ initContainerWorker := corev1.Container {
290
+ Name : "create-cert" ,
291
+ Image : "quay.io/project-codeflare/ray:latest-py39-cu118" ,
292
+ Command : []string {
293
+ "sh" ,
294
+ "-c" ,
295
+ `cd /home/ray/workspace/tls && openssl req -nodes -newkey rsa:2048 -keyout server.key -out server.csr -subj '/CN=ray-head' && printf "authorityKeyIdentifier=keyid,issuer\nbasicConstraints=CA:FALSE\nsubjectAltName = @alt_names\n[alt_names]\nDNS.1 = 127.0.0.1\nDNS.2 = localhost\nDNS.3 = ${FQ_RAY_IP}\nDNS.4 = $(awk 'END{print $1}' /etc/hosts)">./domain.ext && cp /home/ray/workspace/ca/* . && openssl x509 -req -CA ca.crt -CAkey ca.key -in server.csr -out server.crt -days 365 -CAcreateserial -extfile domain.ext` ,
296
+ },
297
+ VolumeMounts : key_volumes ,
298
+ }
299
+ // Append the CA volumes
300
+ rayCluster .Spec .WorkerGroupSpecs [0 ].Template .Spec .Volumes = append (rayCluster .Spec .WorkerGroupSpecs [0 ].Template .Spec .Volumes , caVolumes ... )
301
+ // Append the create-cert Init Container
302
+ rayCluster .Spec .WorkerGroupSpecs [0 ].Template .Spec .InitContainers = append (rayCluster .Spec .WorkerGroupSpecs [0 ].Template .Spec .InitContainers , initContainerWorker )
303
+
304
+ // Append the list of environment variables for the machine-learning container
305
+ for index , container := range rayCluster .Spec .WorkerGroupSpecs [0 ].Template .Spec .Containers {
306
+ if container .Name == "machine-learning" {
307
+ rayCluster .Spec .WorkerGroupSpecs [0 ].Template .Spec .Containers [index ].Env = append (rayCluster .Spec .WorkerGroupSpecs [0 ].Template .Spec .Containers [index ].Env , envList ... )
308
+ }
309
+ }
310
+ }
311
+ }
0 commit comments