Skip to content

Commit 0feab0f

Browse files
Fixed get_cluster for ingress_options
1 parent bece39c commit 0feab0f

File tree

7 files changed

+157
-26
lines changed

7 files changed

+157
-26
lines changed

src/codeflare_sdk/cluster/cluster.py

Lines changed: 78 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -492,7 +492,7 @@ def torchx_config(
492492
to_return["requirements"] = requirements
493493
return to_return
494494

495-
def from_k8_cluster_object(rc, mcad=True, ingress_domain=None):
495+
def from_k8_cluster_object(rc, mcad=True, ingress_domain=None, ingress_options={}):
496496
machine_types = (
497497
rc["metadata"]["labels"]["orderedinstance"].split("_")
498498
if "orderedinstance" in rc["metadata"]["labels"]
@@ -502,6 +502,10 @@ def from_k8_cluster_object(rc, mcad=True, ingress_domain=None):
502502
"volumeMounts"
503503
in rc["spec"]["workerGroupSpecs"][0]["template"]["spec"]["containers"][0]
504504
)
505+
if local_interactive:
506+
ingress_domain = get_ingress_domain_from_client(
507+
rc["metadata"]["name"], rc["metadata"]["namespace"]
508+
)
505509
cluster_config = ClusterConfiguration(
506510
name=rc["metadata"]["name"],
507511
namespace=rc["metadata"]["namespace"],
@@ -533,6 +537,7 @@ def from_k8_cluster_object(rc, mcad=True, ingress_domain=None):
533537
local_interactive=local_interactive,
534538
mcad=mcad,
535539
ingress_domain=ingress_domain,
540+
ingress_options=ingress_options,
536541
)
537542
return Cluster(cluster_config)
538543

@@ -692,27 +697,55 @@ def get_cluster(cluster_name: str, namespace: str = "default"):
692697
api_instance = client.NetworkingV1Api(api_config_handler())
693698
ingresses = api_instance.list_namespaced_ingress(namespace)
694699
ingress_host = None
695-
if mcad == True:
696-
for ingress in ingresses.items:
697-
# Search for ingress with AppWrapper name as the owner
698-
if cluster_name == ingress.metadata.owner_references[0].name:
699-
ingress_host = ingress.spec.rules[0].host
700-
else:
701-
for ingress in ingresses.items:
702-
# Search for the ingress with the ingress-owner label
703-
if ingress.metadata.labels["ingress-owner"] == cluster_name:
704-
ingress_host = ingress.spec.rules[0].host
700+
ingress_options = {}
701+
for ingress in ingresses.items:
702+
# Search for ingress with AppWrapper name as the owner
703+
if (
704+
"ingress-owner" in ingress.metadata.labels
705+
and ingress.metadata.labels["ingress-owner"] == cluster_name
706+
):
707+
ingress_host = ingress.spec.rules[0].host
708+
if (
709+
"ingress-options" in ingress.metadata.labels
710+
and ingress.metadata.labels["ingress-options"] == "true"
711+
):
712+
ingress_name = ingress.metadata.name
713+
port = (
714+
ingress.spec.rules[0]
715+
.http.paths[0]
716+
.backend.service.port.number
717+
)
718+
annotations = ingress.metadata.annotations
719+
path = ingress.spec.rules[0].http.paths[0].path
720+
ingress_class_name = ingress.spec.ingress_class_name
721+
path_type = ingress.spec.rules[0].http.paths[0].path_type
722+
723+
ingress_options = {
724+
"ingresses": [
725+
{
726+
"ingressName": ingress_name,
727+
"port": port,
728+
"annotations": annotations,
729+
"ingressClassName": ingress_class_name,
730+
"pathType": path_type,
731+
"path": path,
732+
"host": ingress_host,
733+
}
734+
]
735+
}
705736
except Exception as e:
706737
return _kube_api_error_handling(e)
707-
708738
# We gather the ingress domain from the host
709-
if ingress_host is not None:
739+
if ingress_host is not None and ingress_options == {}:
710740
ingress_domain = ingress_host.split(".", 1)[1]
711741
else:
712742
ingress_domain = None
713743

714744
return Cluster.from_k8_cluster_object(
715-
rc, mcad=mcad, ingress_domain=ingress_domain
745+
rc,
746+
mcad=mcad,
747+
ingress_domain=ingress_domain,
748+
ingress_options=ingress_options,
716749
)
717750
raise FileNotFoundError(
718751
f"Cluster {cluster_name} is not found in {namespace} namespace"
@@ -762,7 +795,10 @@ def _get_ingress_domain(self): # pragma: no cover
762795
return _kube_api_error_handling(e)
763796

764797
for route in routes["items"]:
765-
if route["spec"]["port"]["targetPort"] == "client":
798+
if (
799+
route["spec"]["port"]["targetPort"] == "client"
800+
or route["spec"]["port"]["targetPort"] == 10001
801+
):
766802
domain = route["spec"]["host"]
767803
else:
768804
try:
@@ -949,3 +985,30 @@ def _copy_to_ray(cluster: Cluster) -> RayCluster:
949985
if ray.status == CodeFlareClusterStatus.READY:
950986
ray.status = RayClusterStatus.READY
951987
return ray
988+
989+
990+
def get_ingress_domain_from_client(cluster_name: str, namespace: str = "default"):
991+
if is_openshift_cluster():
992+
try:
993+
config_check()
994+
api_instance = client.CustomObjectsApi(api_config_handler())
995+
route = api_instance.get_namespaced_custom_object(
996+
group="route.openshift.io",
997+
version="v1",
998+
namespace=namespace,
999+
plural="routes",
1000+
name=f"rayclient-{cluster_name}",
1001+
)
1002+
return route["spec"]["host"].split(".", 1)[1]
1003+
except Exception as e: # pragma no cover
1004+
return _kube_api_error_handling(e)
1005+
else:
1006+
try:
1007+
config_check()
1008+
api_instance = client.NetworkingV1Api(api_config_handler())
1009+
ingress = api_instance.read_namespaced_ingress(
1010+
f"rayclient-{cluster_name}", namespace
1011+
)
1012+
return ingress.spec.rules[0].host.split(".", 1)[1]
1013+
except Exception as e: # pragma no cover
1014+
return _kube_api_error_handling(e)

src/codeflare_sdk/templates/base-template.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,6 +294,7 @@ spec:
294294
annotations:
295295
annotations-example:annotations-example
296296
labels:
297+
ingress-options: "false"
297298
ingress-owner: appwrapper-name
298299
spec:
299300
ingressClassName: nginx

src/codeflare_sdk/utils/generate_yaml.py

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -133,25 +133,41 @@ def update_dashboard_ingress(
133133
metadata["name"] = ingress_option["ingressName"]
134134
metadata["namespace"] = namespace
135135
metadata["labels"]["ingress-owner"] = cluster_name
136-
if "annotations" not in ingress_option.keys():
136+
metadata["labels"]["ingress-options"] = "true"
137+
if (
138+
"annotations" not in ingress_option.keys()
139+
or ingress_option["annotations"] is None
140+
):
137141
del metadata["annotations"]
138142
else:
139143
metadata["annotations"] = ingress_option["annotations"]
140-
if "path" not in ingress_option.keys():
144+
if (
145+
"path" not in ingress_option.keys()
146+
or ingress_option["path"] is None
147+
):
141148
del spec["rules"][0]["http"]["paths"][0]["path"]
142149
else:
143150
spec["rules"][0]["http"]["paths"][0]["path"] = ingress_option[
144151
"path"
145152
]
146-
if "pathType" not in ingress_option.keys():
153+
if (
154+
"pathType" not in ingress_option.keys()
155+
or ingress_option["pathType"] is None
156+
):
147157
spec["rules"][0]["http"]["paths"][0][
148158
"pathType"
149159
] = "ImplementationSpecific"
150-
if "host" not in ingress_option.keys():
160+
if (
161+
"host" not in ingress_option.keys()
162+
or ingress_option["host"] is None
163+
):
151164
del spec["rules"][0]["host"]
152165
else:
153166
spec["rules"][0]["host"] = ingress_option["host"]
154-
if "ingressClassName" not in ingress_option.keys():
167+
if (
168+
"ingressClassName" not in ingress_option.keys()
169+
or ingress_option["ingressClassName"] is None
170+
):
155171
del spec["ingressClassName"]
156172
else:
157173
spec["ingressClassName"] = ingress_option["ingressClassName"]

tests/test-case-no-mcad.yamls

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ apiVersion: networking.k8s.io/v1
143143
kind: Ingress
144144
metadata:
145145
labels:
146+
ingress-options: 'false'
146147
ingress-owner: unit-test-cluster-ray
147148
name: ray-dashboard-unit-test-cluster-ray
148149
namespace: ns

tests/test-case-prio.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,7 @@ spec:
176176
kind: Ingress
177177
metadata:
178178
labels:
179+
ingress-options: 'false'
179180
ingress-owner: prio-test-cluster
180181
name: ray-dashboard-prio-test-cluster
181182
namespace: ns

tests/test-case.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ spec:
173173
kind: Ingress
174174
metadata:
175175
labels:
176+
ingress-options: 'false'
176177
ingress-owner: unit-test-cluster
177178
name: ray-dashboard-unit-test-cluster
178179
namespace: ns

tests/unit_test.py

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
_app_wrapper_status,
4040
_ray_cluster_status,
4141
_get_ingress_domain,
42+
get_ingress_domain_from_client,
4243
)
4344
from codeflare_sdk.cluster.auth import (
4445
TokenAuthentication,
@@ -616,25 +617,27 @@ def ray_addr(self, *args):
616617
return self._address
617618

618619

619-
def ingress_retrieval(port, annotations=None):
620+
def ingress_retrieval(port, annotations=None, cluster_name="unit-test-cluster"):
621+
labels = {"ingress-owner": cluster_name, "ingress-options": "false"}
620622
if port == 10001:
621623
serviceName = "client"
622624
else:
623625
serviceName = "dashboard"
624626
mock_ingress = client.V1Ingress(
625627
metadata=client.V1ObjectMeta(
626-
name=f"ray-{serviceName}-unit-test-cluster",
628+
name=f"ray-{serviceName}-{cluster_name}",
627629
annotations=annotations,
630+
labels=labels,
628631
owner_references=[
629632
client.V1OwnerReference(
630-
api_version="v1", kind="Ingress", name="quicktest", uid="unique-id"
633+
api_version="v1", kind="Ingress", name=cluster_name, uid="unique-id"
631634
)
632635
],
633636
),
634637
spec=client.V1IngressSpec(
635638
rules=[
636639
client.V1IngressRule(
637-
host=f"ray-{serviceName}-unit-test-cluster-ns.apps.cluster.awsroute.org",
640+
host=f"ray-{serviceName}-{cluster_name}-ns.apps.cluster.awsroute.org",
638641
http=client.V1HTTPIngressRuleValue(
639642
paths=[
640643
client.V1HTTPIngressPath(
@@ -1417,7 +1420,10 @@ def get_aw_obj(group, version, namespace, plural):
14171420
"apiVersion": "networking.k8s.io/v1",
14181421
"kind": "Ingress",
14191422
"metadata": {
1420-
"labels": {"ingress-owner": "appwrapper-name"},
1423+
"labels": {
1424+
"ingress-owner": "appwrapper-name",
1425+
"ingress-options": "false",
1426+
},
14211427
"name": "ray-dashboard-quicktest",
14221428
"namespace": "default",
14231429
},
@@ -1817,7 +1823,7 @@ def test_get_cluster(mocker):
18171823
)
18181824
mocker.patch(
18191825
"kubernetes.client.NetworkingV1Api.list_namespaced_ingress",
1820-
return_value=ingress_retrieval(port=8265),
1826+
return_value=ingress_retrieval(port=8265, cluster_name="quicktest"),
18211827
)
18221828
cluster = get_cluster("quicktest")
18231829
cluster_config = cluster.config
@@ -1837,6 +1843,48 @@ def test_get_cluster(mocker):
18371843
assert cluster_config.num_workers == 1
18381844

18391845

1846+
def test_get_ingress_domain_from_client(mocker):
1847+
mocker.patch("kubernetes.config.load_kube_config")
1848+
mocker.patch("kubernetes.client.ApisApi.get_api_versions")
1849+
mocker.patch(
1850+
"kubernetes.client.NetworkingV1Api.read_namespaced_ingress",
1851+
return_value=ingress_retrieval(
1852+
port=8265, cluster_name="unit-test-cluster"
1853+
).items[0],
1854+
)
1855+
1856+
ingress_domain = get_ingress_domain_from_client("unit-test-cluster", "ns")
1857+
assert ingress_domain == "apps.cluster.awsroute.org"
1858+
1859+
mocker.patch(
1860+
"codeflare_sdk.utils.generate_yaml.is_openshift_cluster", return_value=True
1861+
)
1862+
mocker.patch(
1863+
"kubernetes.client.CustomObjectsApi.get_namespaced_custom_object",
1864+
side_effect=route_retrieval,
1865+
)
1866+
ingress_domain = get_ingress_domain_from_client("unit-test-cluster", "ns")
1867+
assert ingress_domain == "apps.cluster.awsroute.org"
1868+
1869+
1870+
def route_retrieval(group, version, namespace, plural, name):
1871+
assert group == "route.openshift.io"
1872+
assert version == "v1"
1873+
assert namespace == "ns"
1874+
assert plural == "routes"
1875+
assert name == "ray-dashboard-unit-test-cluster"
1876+
return {
1877+
"items": [
1878+
{
1879+
"metadata": {"name": "ray-dashboard-unit-test-cluster"},
1880+
"spec": {
1881+
"host": "ray-dashboard-unit-test-cluster-ns.apps.cluster.awsroute.org"
1882+
},
1883+
}
1884+
]
1885+
}
1886+
1887+
18401888
def test_list_clusters(mocker, capsys):
18411889
mocker.patch("kubernetes.config.load_kube_config", return_value="ignore")
18421890
mocker.patch(

0 commit comments

Comments
 (0)