Skip to content

Commit 8c7df68

Browse files
committed
Added support for ingress over routes on cluster creation
1 parent 830cbce commit 8c7df68

File tree

10 files changed

+521
-176
lines changed

10 files changed

+521
-176
lines changed

poetry.lock

Lines changed: 10 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/codeflare_sdk/cluster/cluster.py

Lines changed: 53 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,9 @@
2828

2929
from .auth import config_check, api_config_handler
3030
from ..utils import pretty_print
31-
from ..utils.generate_yaml import generate_appwrapper
31+
from ..utils.generate_yaml import (
32+
generate_appwrapper,
33+
)
3234
from ..utils.kube_api_helpers import _kube_api_error_handling
3335
from ..utils.openshift_oauth import (
3436
create_openshift_oauth_objects,
@@ -175,6 +177,8 @@ def create_app_wrapper(self):
175177
local_interactive = self.config.local_interactive
176178
image_pull_secrets = self.config.image_pull_secrets
177179
dispatch_priority = self.config.dispatch_priority
180+
ingress_domain = self.config.ingress_domain
181+
ingress_options = self.config.ingress_options
178182
return generate_appwrapper(
179183
name=name,
180184
namespace=namespace,
@@ -198,6 +202,8 @@ def create_app_wrapper(self):
198202
dispatch_priority=dispatch_priority,
199203
priority_val=priority_val,
200204
openshift_oauth=self.config.openshift_oauth,
205+
ingress_domain=ingress_domain,
206+
ingress_options=ingress_options,
201207
)
202208

203209
# creates a new cluster with the provided or default spec
@@ -399,27 +405,22 @@ def cluster_dashboard_uri(self) -> str:
399405
"""
400406
try:
401407
config_check()
402-
api_instance = client.CustomObjectsApi(api_config_handler())
403-
routes = api_instance.list_namespaced_custom_object(
404-
group="route.openshift.io",
405-
version="v1",
406-
namespace=self.config.namespace,
407-
plural="routes",
408-
)
409-
except Exception as e: # pragma: no cover
408+
api_instance = client.NetworkingV1Api(api_config_handler())
409+
ingresses = api_instance.list_namespaced_ingress(self.config.namespace)
410+
except Exception as e:
410411
return _kube_api_error_handling(e)
411412

412-
for route in routes["items"]:
413-
if route["metadata"][
414-
"name"
415-
] == f"ray-dashboard-{self.config.name}" or route["metadata"][
416-
"name"
417-
].startswith(
418-
f"{self.config.name}-ingress"
419-
):
420-
protocol = "https" if route["spec"].get("tls") else "http"
421-
return f"{protocol}://{route['spec']['host']}"
413+
for ingress in ingresses.items:
414+
annotations = ingress.metadata.annotations
415+
if ingress.metadata.name == f"ray-dashboard-{self.config.name}" or ingress.metadata.name.startswith(
416+
f"{self.config.name}-ingress" ):
417+
if annotations == None:
418+
protocol = "http"
419+
elif "route.openshift.io/termination" in annotations:
420+
protocol = "https"
421+
return f"{protocol}://{ingress.spec.rules[0].host}"
422422
return "Dashboard route not available yet, have you run cluster.up()?"
423+
423424

424425
def list_jobs(self) -> List:
425426
"""
@@ -498,8 +499,8 @@ def from_k8_cluster_object(rc, mcad=True):
498499

499500
def local_client_url(self):
500501
if self.config.local_interactive == True:
501-
ingress_domain = _get_ingress_domain()
502-
return f"ray://rayclient-{self.config.name}-{self.config.namespace}.{ingress_domain}"
502+
ingress_domain = _get_ingress_domain(self)
503+
return f"ray://{ingress_domain}"
503504
else:
504505
return "None"
505506

@@ -655,16 +656,23 @@ def _check_aw_exists(name: str, namespace: str) -> bool:
655656
return False
656657

657658

658-
def _get_ingress_domain():
659+
# Cant test this until get_current_namespace is fixed
660+
def _get_ingress_domain(self): # pragma: no cover
659661
try:
660662
config_check()
661-
api_client = client.CustomObjectsApi(api_config_handler())
662-
ingress = api_client.get_cluster_custom_object(
663-
"config.openshift.io", "v1", "ingresses", "cluster"
664-
)
665-
except Exception as e: # pragma: no cover
663+
api_client = client.NetworkingV1Api(api_config_handler())
664+
if self.config.namespace != None:
665+
namespace = self.config.namespace
666+
else:
667+
namespace = get_current_namespace()
668+
ingresses = api_client.list_namespaced_ingress(namespace)
669+
except Exception as e: # pragma: no cover
666670
return _kube_api_error_handling(e)
667-
return ingress["spec"]["domain"]
671+
domain = None
672+
for ingress in ingresses.items:
673+
if ingress.spec.rules[0].http.paths[0].backend.service.port.number == 10001:
674+
domain = ingress.spec.rules[0].host
675+
return domain
668676

669677

670678
def _app_wrapper_status(name, namespace="default") -> Optional[AppWrapper]:
@@ -756,27 +764,22 @@ def _map_to_ray_cluster(rc) -> Optional[RayCluster]:
756764
status = RayClusterStatus(rc["status"]["state"].lower())
757765
else:
758766
status = RayClusterStatus.UNKNOWN
759-
760-
config_check()
761-
api_instance = client.CustomObjectsApi(api_config_handler())
762-
# UPDATE THIS
763-
routes = api_instance.list_namespaced_custom_object(
764-
group="route.openshift.io",
765-
version="v1",
766-
namespace=rc["metadata"]["namespace"],
767-
plural="routes",
768-
)
769-
ray_route = None
770-
for route in routes["items"]:
771-
if route["metadata"][
772-
"name"
773-
] == f"ray-dashboard-{rc['metadata']['name']}" or route["metadata"][
774-
"name"
775-
].startswith(
776-
f"{rc['metadata']['name']}-ingress"
777-
):
778-
protocol = "https" if route["spec"].get("tls") else "http"
779-
ray_route = f"{protocol}://{route['spec']['host']}"
767+
try:
768+
config_check()
769+
api_instance = client.NetworkingV1Api(api_config_handler())
770+
ingresses = api_instance.list_namespaced_ingress(rc["metadata"]["namespace"])
771+
except Exception as e:
772+
return _kube_api_error_handling(e)
773+
ray_ingress = None
774+
for ingress in ingresses.items:
775+
annotations = ingress.metadata.annotations
776+
if ingress.metadata.name == f"ray-dashboard-{rc['metadata']['name']}" or ingress.metadata.name.startswith(
777+
f"{rc['metadata']['name']}-ingress" ):
778+
if annotations == None:
779+
protocol = "http"
780+
elif "route.openshift.io/termination" in annotations:
781+
protocol = "https"
782+
ray_ingress = f"{protocol}://{ingress.spec.rules[0].host}"
780783

781784
return RayCluster(
782785
name=rc["metadata"]["name"],
@@ -794,7 +797,6 @@ def _map_to_ray_cluster(rc) -> Optional[RayCluster]:
794797
]["resources"]["limits"]["cpu"],
795798
worker_gpu=0, # hard to detect currently how many gpus, can override it with what the user asked for
796799
namespace=rc["metadata"]["namespace"],
797-
dashboard=ray_route,
798800
head_cpus=rc["spec"]["headGroupSpec"]["template"]["spec"]["containers"][0][
799801
"resources"
800802
]["limits"]["cpu"],
@@ -804,6 +806,7 @@ def _map_to_ray_cluster(rc) -> Optional[RayCluster]:
804806
head_gpu=rc["spec"]["headGroupSpec"]["template"]["spec"]["containers"][0][
805807
"resources"
806808
]["limits"]["nvidia.com/gpu"],
809+
dashboard=ray_ingress,
807810
)
808811

809812

src/codeflare_sdk/cluster/config.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,5 @@ class ClusterConfiguration:
5353
image_pull_secrets: list = field(default_factory=list)
5454
dispatch_priority: str = None
5555
openshift_oauth: bool = False # NOTE: to use the user must have permission to create a RoleBinding for system:auth-delegator
56+
ingress_options: dict = field(default_factory=dict)
57+
ingress_domain: str = None

src/codeflare_sdk/templates/base-template.yaml

Lines changed: 34 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ spec:
4141
kind: RayCluster
4242
metadata:
4343
labels:
44-
appwrapper.mcad.ibm.com: "aw-kuberay"
44+
appwrapper.workload.codeflare.dev: "aw-kuberay"
4545
controller-tools.k8s.io: "1.0"
4646
# A unique identifier for the head node and workers of this cluster.
4747
name: kuberay-cluster
@@ -289,38 +289,50 @@ spec:
289289
emptyDir: {}
290290
- replicas: 1
291291
generictemplate:
292-
kind: Route
293-
apiVersion: route.openshift.io/v1
292+
apiVersion: networking.k8s.io/v1
293+
kind: Ingress
294294
metadata:
295-
name: ray-dashboard-deployment-name
295+
name: ray-dashboard-raytest
296296
namespace: default
297-
labels:
298-
# allows me to return name of service that Ray operator creates
299-
odh-ray-cluster-service: deployment-name-head-svc
297+
annotations:
298+
annotations-example:annotations-example
300299
spec:
301-
to:
302-
kind: Service
303-
name: deployment-name-head-svc
304-
port:
305-
targetPort: dashboard
300+
ingressClassName: nginx
301+
rules:
302+
- http:
303+
paths:
304+
- backend:
305+
service:
306+
name: raytest-head-svc
307+
port:
308+
number: 8265
309+
pathType: Prefix
310+
path: /
311+
host: ray-dashboard-raytest.<ingress-domain>
306312
- replicas: 1
307313
generictemplate:
308-
apiVersion: route.openshift.io/v1
309-
kind: Route
314+
apiVersion: networking.k8s.io/v1
315+
kind: Ingress
310316
metadata:
311317
name: rayclient-deployment-name
312318
namespace: default
319+
annotations:
320+
annotations-example:annotations-example
313321
labels:
314-
# allows me to return name of service that Ray operator creates
315322
odh-ray-cluster-service: deployment-name-head-svc
316323
spec:
317-
port:
318-
targetPort: client
319-
tls:
320-
termination: passthrough
321-
to:
322-
kind: Service
323-
name: deployment-name-head-svc
324+
ingressClassName: nginx
325+
rules:
326+
- http:
327+
paths:
328+
- backend:
329+
service:
330+
name: deployment-name-head-svc
331+
port:
332+
number: 10001
333+
path: ''
334+
pathType: ImplementationSpecific
335+
host: rayclient-raytest.<ingress-domain>
324336
- replicas: 1
325337
generictemplate:
326338
apiVersion: v1

0 commit comments

Comments
 (0)