@@ -18,6 +18,7 @@ package controllers
1818
1919import (
2020 "context"
21+ "strconv"
2122
2223 rayv1 "github.com/ray-project/kuberay/ray-operator/apis/ray/v1"
2324
@@ -36,6 +37,7 @@ import (
3637const (
3738 oauthProxyContainerName = "oauth-proxy"
3839 oauthProxyVolumeName = "proxy-tls-secret"
40+ initContainerName = "create-cert"
3941)
4042
4143// log is for logging in this package.
@@ -66,17 +68,51 @@ var _ webhook.CustomValidator = &rayClusterWebhook{}
6668func (w * rayClusterWebhook ) Default (ctx context.Context , obj runtime.Object ) error {
6769 rayCluster := obj .(* rayv1.RayCluster )
6870
69- if ! pointer .BoolDeref (w .Config .RayDashboardOAuthEnabled , true ) {
70- return nil
71- }
72-
7371 rayclusterlog .V (2 ).Info ("Adding OAuth sidecar container" )
7472
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 ))
7675
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 ))
7877
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+ }
80116
81117 return nil
82118}
@@ -216,3 +252,181 @@ func oauthProxyTLSSecretVolume(rayCluster *rayv1.RayCluster) corev1.Volume {
216252 },
217253 }
218254}
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