@@ -18,6 +18,7 @@ package controllers
18
18
19
19
import (
20
20
"context"
21
+ "strconv"
21
22
22
23
rayv1 "github.com/ray-project/kuberay/ray-operator/apis/ray/v1"
23
24
@@ -36,6 +37,7 @@ import (
36
37
const (
37
38
oauthProxyContainerName = "oauth-proxy"
38
39
oauthProxyVolumeName = "proxy-tls-secret"
40
+ initContainerName = "create-cert"
39
41
)
40
42
41
43
// log is for logging in this package.
@@ -66,17 +68,51 @@ var _ webhook.CustomValidator = &rayClusterWebhook{}
66
68
func (w * rayClusterWebhook ) Default (ctx context.Context , obj runtime.Object ) error {
67
69
rayCluster := obj .(* rayv1.RayCluster )
68
70
69
- if ! pointer .BoolDeref (w .Config .RayDashboardOAuthEnabled , true ) {
70
- return nil
71
- }
72
-
73
71
rayclusterlog .V (2 ).Info ("Adding OAuth sidecar container" )
74
72
75
- rayCluster .Spec .HeadGroupSpec .Template .Spec .Containers = upsert (rayCluster .Spec .HeadGroupSpec .Template .Spec .Containers , oauthProxyContainer (rayCluster ), withContainerName (oauthProxyContainerName ))
73
+ if pointer .BoolDeref (w .Config .RayDashboardOAuthEnabled , true ) {
74
+ rayCluster .Spec .HeadGroupSpec .Template .Spec .Containers = upsert (rayCluster .Spec .HeadGroupSpec .Template .Spec .Containers , oauthProxyContainer (rayCluster ), withContainerName (oauthProxyContainerName ))
76
75
77
- rayCluster .Spec .HeadGroupSpec .Template .Spec .Volumes = upsert (rayCluster .Spec .HeadGroupSpec .Template .Spec .Volumes , oauthProxyTLSSecretVolume (rayCluster ), withVolumeName (oauthProxyVolumeName ))
76
+ rayCluster .Spec .HeadGroupSpec .Template .Spec .Volumes = upsert (rayCluster .Spec .HeadGroupSpec .Template .Spec .Volumes , oauthProxyTLSSecretVolume (rayCluster ), withVolumeName (oauthProxyVolumeName ))
78
77
79
- rayCluster .Spec .HeadGroupSpec .Template .Spec .ServiceAccountName = rayCluster .Name + "-oauth-proxy"
78
+ rayCluster .Spec .HeadGroupSpec .Template .Spec .ServiceAccountName = rayCluster .Name + "-oauth-proxy"
79
+ }
80
+
81
+ if pointer .BoolDeref (w .Config .MTLSEnabled , true ) {
82
+ // HeadGroupSpec //
83
+ // Append the list of environment variables for the ray-head container
84
+ for index , container := range rayCluster .Spec .HeadGroupSpec .Template .Spec .Containers {
85
+ if container .Name == "ray-head" {
86
+ for _ , envVar := range envVarList () {
87
+ rayCluster .Spec .HeadGroupSpec .Template .Spec .Containers [index ].Env = upsert (rayCluster .Spec .HeadGroupSpec .Template .Spec .Containers [index ].Env , envVar , withEnvVarName (envVar .Name ))
88
+ }
89
+ }
90
+ }
91
+
92
+ // Append the create-cert Init Container
93
+ rayCluster .Spec .HeadGroupSpec .Template .Spec .InitContainers = upsert (rayCluster .Spec .HeadGroupSpec .Template .Spec .InitContainers , w .rayHeadInitContainer (rayCluster ), withContainerName (initContainerName ))
94
+
95
+ // Append the CA volumes
96
+ for _ , caVol := range caVolumes (rayCluster ) {
97
+ rayCluster .Spec .HeadGroupSpec .Template .Spec .Volumes = upsert (rayCluster .Spec .HeadGroupSpec .Template .Spec .Volumes , caVol , withVolumeName (caVol .Name ))
98
+ }
99
+ // WorkerGroupSpec //
100
+ // Append the list of environment variables for the machine-learning container
101
+ for index , container := range rayCluster .Spec .WorkerGroupSpecs [0 ].Template .Spec .Containers {
102
+ if container .Name == "machine-learning" {
103
+ for _ , envVar := range envVarList () {
104
+ rayCluster .Spec .WorkerGroupSpecs [0 ].Template .Spec .Containers [index ].Env = upsert (rayCluster .Spec .WorkerGroupSpecs [0 ].Template .Spec .Containers [index ].Env , envVar , withEnvVarName (envVar .Name ))
105
+ }
106
+ }
107
+ }
108
+ // Append the CA volumes
109
+ for _ , caVol := range caVolumes (rayCluster ) {
110
+ rayCluster .Spec .WorkerGroupSpecs [0 ].Template .Spec .Volumes = upsert (rayCluster .Spec .WorkerGroupSpecs [0 ].Template .Spec .Volumes , caVol , withVolumeName (caVol .Name ))
111
+ }
112
+ // Append the create-cert Init Container
113
+ rayCluster .Spec .WorkerGroupSpecs [0 ].Template .Spec .InitContainers = upsert (rayCluster .Spec .WorkerGroupSpecs [0 ].Template .Spec .InitContainers , rayWorkerInitContainer (rayCluster ), withContainerName (initContainerName ))
114
+
115
+ }
80
116
81
117
return nil
82
118
}
@@ -216,3 +252,181 @@ func oauthProxyTLSSecretVolume(rayCluster *rayv1.RayCluster) corev1.Volume {
216
252
},
217
253
}
218
254
}
255
+
256
+ func initCaVolumeMounts () []corev1.VolumeMount {
257
+ keyVolumes := []corev1.VolumeMount {
258
+ {
259
+ Name : "ca-vol" ,
260
+ MountPath : "/home/ray/workspace/ca" ,
261
+ ReadOnly : true ,
262
+ },
263
+ {
264
+ Name : "server-cert" ,
265
+ MountPath : "/home/ray/workspace/tls" ,
266
+ ReadOnly : false ,
267
+ },
268
+ }
269
+ return keyVolumes
270
+ }
271
+
272
+ func envVarList () []corev1.EnvVar {
273
+ envList := []corev1.EnvVar {
274
+ {
275
+ Name : "MY_POD_IP" ,
276
+ ValueFrom : & corev1.EnvVarSource {
277
+ FieldRef : & corev1.ObjectFieldSelector {
278
+ FieldPath : "status.podIP" ,
279
+ },
280
+ },
281
+ },
282
+ {
283
+ Name : "RAY_USE_TLS" ,
284
+ Value : "1" ,
285
+ },
286
+ {
287
+ Name : "RAY_TLS_SERVER_CERT" ,
288
+ Value : "/home/ray/workspace/tls/server.crt" ,
289
+ },
290
+ {
291
+ Name : "RAY_TLS_SERVER_KEY" ,
292
+ Value : "/home/ray/workspace/tls/server.key" ,
293
+ },
294
+ {
295
+ Name : "RAY_TLS_CA_CERT" ,
296
+ Value : "/home/ray/workspace/tls/ca.crt" ,
297
+ },
298
+ }
299
+ return envList
300
+ }
301
+
302
+ func caVolumes (rayCluster * rayv1.RayCluster ) []corev1.Volume {
303
+ secretName := `ca-secret-` + rayCluster .Name
304
+ caVolumes := []corev1.Volume {
305
+ {
306
+ Name : "ca-vol" ,
307
+ VolumeSource : corev1.VolumeSource {
308
+ Secret : & corev1.SecretVolumeSource {
309
+ SecretName : secretName ,
310
+ },
311
+ },
312
+ },
313
+ {
314
+ Name : "server-cert" ,
315
+ VolumeSource : corev1.VolumeSource {
316
+ EmptyDir : & corev1.EmptyDirVolumeSource {},
317
+ },
318
+ },
319
+ }
320
+ return caVolumes
321
+ }
322
+
323
+ func (w * rayClusterWebhook ) rayHeadInitContainer (rayCluster * rayv1.RayCluster ) corev1.Container {
324
+ rayClientRoute := "rayclient-" + rayCluster .Name + "-" + rayCluster .Namespace + "." + w .Config .IngressDomain
325
+ // Service name for basic interactive
326
+ svcDomain := rayCluster .Name + "-head-svc." + rayCluster .Namespace + ".svc"
327
+
328
+ initContainerHead := corev1.Container {
329
+ Name : "create-cert" ,
330
+ Image : "quay.io/project-codeflare/ray:latest-py39-cu118" ,
331
+ Command : []string {
332
+ "sh" ,
333
+ "-c" ,
334
+ `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` ,
335
+ },
336
+ VolumeMounts : initCaVolumeMounts (),
337
+ }
338
+ return initContainerHead
339
+ }
340
+
341
+ func rayWorkerInitContainer (rayCluster * rayv1.RayCluster ) corev1.Container {
342
+ initContainerWorker := corev1.Container {
343
+ Name : "create-cert" ,
344
+ Image : "quay.io/project-codeflare/ray:latest-py39-cu118" ,
345
+ Command : []string {
346
+ "sh" ,
347
+ "-c" ,
348
+ `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` ,
349
+ },
350
+ VolumeMounts : initCaVolumeMounts (),
351
+ }
352
+ return initContainerWorker
353
+ }
354
+
355
+ func (w * rayClusterWebhook ) validateHeadInitContainer (rayCluster * rayv1.RayCluster ) field.ErrorList {
356
+ var allErrors field.ErrorList
357
+
358
+ if err := contains (rayCluster .Spec .HeadGroupSpec .Template .Spec .InitContainers , w .rayHeadInitContainer (rayCluster ), byContainerName ,
359
+ field .NewPath ("spec" , "headGroupSpec" , "template" , "spec" , "initContainers" ),
360
+ "create-cert Init Container is immutable" ); err != nil {
361
+ allErrors = append (allErrors , err )
362
+ }
363
+
364
+ return allErrors
365
+ }
366
+
367
+ func (w * rayClusterWebhook ) validateWorkerInitContainer (rayCluster * rayv1.RayCluster ) field.ErrorList {
368
+ var allErrors field.ErrorList
369
+
370
+ if err := contains (rayCluster .Spec .WorkerGroupSpecs [0 ].Template .Spec .InitContainers , rayWorkerInitContainer (rayCluster ), byContainerName ,
371
+ field .NewPath ("spec" , "workerGroupSpecs" , "0" , "template" , "spec" , "initContainers" ),
372
+ "create-cert Init Container is immutable" ); err != nil {
373
+ allErrors = append (allErrors , err )
374
+ }
375
+
376
+ return allErrors
377
+ }
378
+
379
+ func validateCaVolumes (rayCluster * rayv1.RayCluster ) field.ErrorList {
380
+ var allErrors field.ErrorList
381
+
382
+ for _ , caVol := range caVolumes (rayCluster ) {
383
+ if err := contains (rayCluster .Spec .HeadGroupSpec .Template .Spec .Volumes , caVol , byVolumeName ,
384
+ field .NewPath ("spec" , "headGroupSpec" , "template" , "spec" , "volumes" ),
385
+ "ca-vol and server-cert Secret volumes are immutable" ); err != nil {
386
+ allErrors = append (allErrors , err )
387
+ }
388
+ if err := contains (rayCluster .Spec .WorkerGroupSpecs [0 ].Template .Spec .Volumes , caVol , byVolumeName ,
389
+ field .NewPath ("spec" , "workerGroupSpecs" , "0" , "template" , "spec" , "volumes" ),
390
+ "ca-vol and server-cert Secret volumes are immutable" ); err != nil {
391
+ allErrors = append (allErrors , err )
392
+ }
393
+ }
394
+
395
+ return allErrors
396
+ }
397
+
398
+ func validateEnvVars (rayCluster * rayv1.RayCluster ) field.ErrorList {
399
+ var allErrors field.ErrorList
400
+ item := 0
401
+ for index , container := range rayCluster .Spec .HeadGroupSpec .Template .Spec .Containers {
402
+ if container .Name == "ray-head" {
403
+ item = index
404
+ break
405
+ }
406
+ }
407
+
408
+ for _ , envVar := range envVarList () {
409
+ if err := contains (rayCluster .Spec .HeadGroupSpec .Template .Spec .Containers [item ].Env , envVar , byEnvVarName ,
410
+ field .NewPath ("spec" , "headGroupSpec" , "template" , "spec" , "containers" , strconv .Itoa (item ), "env" ),
411
+ "RAY_TLS environment variables are immutable" ); err != nil {
412
+ allErrors = append (allErrors , err )
413
+ }
414
+ }
415
+
416
+ for index , container := range rayCluster .Spec .WorkerGroupSpecs [0 ].Template .Spec .Containers {
417
+ if container .Name == "machine-learning" {
418
+ item = index
419
+ break
420
+ }
421
+ }
422
+
423
+ for _ , envVar := range envVarList () {
424
+ if err := contains (rayCluster .Spec .WorkerGroupSpecs [0 ].Template .Spec .Containers [item ].Env , envVar , byEnvVarName ,
425
+ field .NewPath ("spec" , "workerGroupSpecs" , "0" , "template" , "spec" , "containers" , strconv .Itoa (item ), "env" ),
426
+ "RAY_TLS environment variables are immutable" ); err != nil {
427
+ allErrors = append (allErrors , err )
428
+ }
429
+ }
430
+
431
+ return allErrors
432
+ }
0 commit comments