@@ -18,6 +18,7 @@ package controllers
18
18
19
19
import (
20
20
"context"
21
+
21
22
rayv1 "github.com/ray-project/kuberay/ray-operator/apis/ray/v1"
22
23
corev1 "k8s.io/api/core/v1"
23
24
"k8s.io/apimachinery/pkg/runtime"
@@ -26,8 +27,12 @@ import (
26
27
"sigs.k8s.io/controller-runtime/pkg/webhook"
27
28
)
28
29
29
- // log is for logging in this package.
30
- var rayclusterlog = logf .Log .WithName ("raycluster-resource" )
30
+ var (
31
+ NameConsoleLink string = "console"
32
+ NamespaceConsoleLink string = "openshift-console"
33
+ // log is for logging in this package.
34
+ rayclusterlog = logf .Log .WithName ("raycluster-resource" )
35
+ )
31
36
32
37
func (r * RayClusterDefaulter ) SetupWebhookWithManager (mgr ctrl.Manager ) error {
33
38
return ctrl .NewWebhookManagedBy (mgr ).
@@ -47,17 +52,36 @@ func (r *RayClusterDefaulter) Default(ctx context.Context, obj runtime.Object) e
47
52
raycluster := obj .(* rayv1.RayCluster )
48
53
49
54
rayclusterlog .Info ("default" , "name" , raycluster .Name )
55
+ oauthExists := false
56
+ initHeadExists := false
57
+ workerHeadExists := false
50
58
// Check and add OAuth proxy if it does not exist.
51
- alreadyExists := false
52
59
for _ , container := range raycluster .Spec .HeadGroupSpec .Template .Spec .Containers {
53
60
if container .Name == "oauth-proxy" {
54
61
rayclusterlog .Info ("OAuth sidecar already exists, no patch needed" )
55
- alreadyExists = true
62
+ oauthExists = true
63
+ break // exits the for loop
64
+ }
65
+ }
66
+
67
+ // Check for the create-cert Init Containers
68
+ for _ , container := range raycluster .Spec .HeadGroupSpec .Template .Spec .InitContainers {
69
+ if container .Name == "create-cert" {
70
+ rayclusterlog .Info ("Head Init Containers already exist, no patch needed" )
71
+ initHeadExists = true
72
+ break // exits the for loop
73
+ }
74
+ }
75
+ // Check fot the create-cert Init Container WorkerGroupSpec
76
+ for _ , container := range raycluster .Spec .WorkerGroupSpecs [0 ].Template .Spec .InitContainers {
77
+ if container .Name == "create-cert" {
78
+ rayclusterlog .Info ("Worker Init Containers already exist, no patch needed" )
79
+ workerHeadExists = true
56
80
break // exits the for loop
57
81
}
58
82
}
59
83
60
- if ! alreadyExists {
84
+ if ! oauthExists {
61
85
rayclusterlog .Info ("Adding OAuth sidecar container" )
62
86
// definition of the new container
63
87
newOAuthSidecar := corev1.Container {
@@ -75,7 +99,6 @@ func (r *RayClusterDefaulter) Default(ctx context.Context, obj runtime.Object) e
75
99
"--tls-key=/etc/tls/private/tls.key" ,
76
100
"--cookie-secret=$(COOKIE_SECRET)" ,
77
101
"--openshift-delegate-urls={\" /\" :{\" resource\" :\" pods\" ,\" namespace\" :\" default\" ,\" verb\" :\" get\" }}" ,
78
- "--added-label=True" ,
79
102
},
80
103
Env : []corev1.EnvVar {
81
104
{
@@ -118,5 +141,142 @@ func (r *RayClusterDefaulter) Default(ctx context.Context, obj runtime.Object) e
118
141
raycluster .Spec .HeadGroupSpec .Template .Spec .ServiceAccountName = raycluster .Name + "-oauth-proxy"
119
142
}
120
143
}
144
+ mtlsPatch (raycluster , initHeadExists , workerHeadExists )
121
145
return nil
122
146
}
147
+
148
+ func mtlsPatch (raycluster * rayv1.RayCluster , initHeadExists bool , workerHeadExists bool ) {
149
+
150
+ rayclusterlog .Info ("creating json patch for RayCluster initContainers" )
151
+
152
+ // Volume Mounts for the Init Containers
153
+ key_volumes := []corev1.VolumeMount {
154
+ {
155
+ Name : "ca-vol" ,
156
+ MountPath : "/home/ray/workspace/ca" ,
157
+ ReadOnly : true ,
158
+ },
159
+ {
160
+ Name : "server-cert" ,
161
+ MountPath : "/home/ray/workspace/tls" ,
162
+ ReadOnly : false ,
163
+ },
164
+ }
165
+
166
+ // Service name for basic interactive
167
+ svcDomain := raycluster .Name + "-head-svc." + raycluster .Namespace + ".svc"
168
+ // Ca Secret generated by the SDK
169
+ secretName := `ca-secret-` + raycluster .Name
170
+
171
+ // Env variables for Worker & Head Containers
172
+ envList := []corev1.EnvVar {
173
+ {
174
+ Name : "MY_POD_IP" ,
175
+ ValueFrom : & corev1.EnvVarSource {
176
+ FieldRef : & corev1.ObjectFieldSelector {
177
+ FieldPath : "status.podIP" ,
178
+ },
179
+ },
180
+ },
181
+ {
182
+ Name : "RAY_USE_TLS" ,
183
+ Value : "1" ,
184
+ },
185
+ {
186
+ Name : "RAY_TLS_SERVER_CERT" ,
187
+ Value : "/home/ray/workspace/tls/server.crt" ,
188
+ },
189
+ {
190
+ Name : "RAY_TLS_SERVER_KEY" ,
191
+ Value : "/home/ray/workspace/tls/server.key" ,
192
+ },
193
+ {
194
+ Name : "RAY_TLS_CA_CERT" ,
195
+ Value : "/home/ray/workspace/tls/ca.crt" ,
196
+ },
197
+ }
198
+
199
+ // Volumes for the main container of Head and worker
200
+ caVolumes := []corev1.Volume {
201
+ {
202
+ Name : "ca-vol" ,
203
+ VolumeSource : corev1.VolumeSource {
204
+ Secret : & corev1.SecretVolumeSource {
205
+ SecretName : secretName ,
206
+ },
207
+ },
208
+ },
209
+ {
210
+ Name : "server-cert" ,
211
+ VolumeSource : corev1.VolumeSource {
212
+ EmptyDir : & corev1.EmptyDirVolumeSource {},
213
+ },
214
+ },
215
+ }
216
+
217
+ if ! initHeadExists {
218
+ domain := "random.com" // getDomainName()
219
+
220
+ rayClientRoute := "rayclient-" + raycluster .Name + "-" + raycluster .Namespace + "." + domain
221
+ initContainerHead := corev1.Container {
222
+ Name : "create-cert" ,
223
+ Image : "quay.io/project-codeflare/ray:latest-py39-cu118" ,
224
+ Command : []string {
225
+ "sh" ,
226
+ "-c" ,
227
+ `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` ,
228
+ },
229
+ VolumeMounts : key_volumes ,
230
+ }
231
+
232
+ // Append the list of environment variables for the ray-head container
233
+ for index , container := range raycluster .Spec .HeadGroupSpec .Template .Spec .Containers {
234
+ if container .Name == "ray-head" {
235
+ raycluster .Spec .HeadGroupSpec .Template .Spec .Containers [index ].Env = append (raycluster .Spec .HeadGroupSpec .Template .Spec .Containers [index ].Env , envList ... )
236
+ }
237
+ }
238
+
239
+ // Append the create-cert Init Container
240
+ raycluster .Spec .HeadGroupSpec .Template .Spec .InitContainers = append (raycluster .Spec .HeadGroupSpec .Template .Spec .InitContainers , initContainerHead )
241
+
242
+ // Append the CA volumes
243
+ raycluster .Spec .HeadGroupSpec .Template .Spec .Volumes = append (raycluster .Spec .HeadGroupSpec .Template .Spec .Volumes , caVolumes ... )
244
+ }
245
+
246
+ if ! workerHeadExists {
247
+ initContainerWorker := corev1.Container {
248
+ Name : "create-cert" ,
249
+ Image : "quay.io/project-codeflare/ray:latest-py39-cu118" ,
250
+ Command : []string {
251
+ "sh" ,
252
+ "-c" ,
253
+ `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` ,
254
+ },
255
+ VolumeMounts : key_volumes ,
256
+ }
257
+ // Append the CA volumes
258
+ raycluster .Spec .WorkerGroupSpecs [0 ].Template .Spec .Volumes = append (raycluster .Spec .WorkerGroupSpecs [0 ].Template .Spec .Volumes , caVolumes ... )
259
+ // Append the create-cert Init Container
260
+ raycluster .Spec .WorkerGroupSpecs [0 ].Template .Spec .InitContainers = append (raycluster .Spec .WorkerGroupSpecs [0 ].Template .Spec .InitContainers , initContainerWorker )
261
+
262
+ // Append the list of environment variables for the machine-learning container
263
+ for index , container := range raycluster .Spec .WorkerGroupSpecs [0 ].Template .Spec .Containers {
264
+ if container .Name == "machine-learning" {
265
+ raycluster .Spec .WorkerGroupSpecs [0 ].Template .Spec .Containers [index ].Env = append (raycluster .Spec .WorkerGroupSpecs [0 ].Template .Spec .Containers [index ].Env , envList ... )
266
+ }
267
+ }
268
+ }
269
+ }
270
+
271
+ /*
272
+ func getDomainName() (string, error) {
273
+ consoleRoute := &routev1.Route{}
274
+
275
+ if err := k8Client.Get(context.TODO(), types.NamespacedName{Name: NameConsoleLink, Namespace: NamespaceConsoleLink}, consoleRoute); err != nil {
276
+ return "error getting console route URL", err
277
+ }
278
+ domainIndex := strings.Index(consoleRoute.Spec.Host, ".")
279
+ consoleLinkDomain := consoleRoute.Spec.Host[domainIndex+1:]
280
+ return consoleLinkDomain, nil
281
+ }
282
+ */
0 commit comments