diff --git a/src/codeflare_sdk/cluster/cluster.py b/src/codeflare_sdk/cluster/cluster.py index 295332ae4..c3e1d33ae 100644 --- a/src/codeflare_sdk/cluster/cluster.py +++ b/src/codeflare_sdk/cluster/cluster.py @@ -46,6 +46,7 @@ import requests from kubernetes import config +from kubernetes.client.rest import ApiException class Cluster: @@ -216,6 +217,10 @@ def up(self): Applies the AppWrapper yaml, pushing the resource request onto the MCAD queue. """ + + # check if RayCluster CustomResourceDefinition exists if not throw RuntimeError + self._throw_for_no_raycluster() + namespace = self.config.namespace try: @@ -246,12 +251,32 @@ def up(self): except Exception as e: # pragma: no cover return _kube_api_error_handling(e) + def _throw_for_no_raycluster(self): + api_instance = client.CustomObjectsApi(api_config_handler()) + try: + api_instance.list_namespaced_custom_object( + group="ray.io", + version="v1", + namespace=self.config.namespace, + plural="rayclusters", + ) + except ApiException as e: + if e.status == 404: + raise RuntimeError( + "RayCluster CustomResourceDefinition unavailable contact your administrator." + ) + else: + raise RuntimeError( + "Failed to get RayCluster CustomResourceDefinition: " + str(e) + ) + def down(self): """ Deletes the AppWrapper yaml, scaling-down and deleting all resources associated with the cluster. """ namespace = self.config.namespace + self._throw_for_no_raycluster() try: config_check() api_instance = client.CustomObjectsApi(api_config_handler()) diff --git a/tests/unit_test.py b/tests/unit_test.py index 322449fbf..49ebf8e26 100644 --- a/tests/unit_test.py +++ b/tests/unit_test.py @@ -508,6 +508,7 @@ def arg_check_del_effect(group, version, namespace, plural, name, *args): def test_cluster_up_down(mocker): mocker.patch("kubernetes.client.ApisApi.get_api_versions") mocker.patch("kubernetes.config.load_kube_config", return_value="ignore") + mocker.patch("codeflare_sdk.cluster.cluster.Cluster._throw_for_no_raycluster") mocker.patch( "kubernetes.client.CustomObjectsApi.get_cluster_custom_object", return_value={"spec": {"domain": ""}}, @@ -530,6 +531,7 @@ def test_cluster_up_down(mocker): def test_cluster_up_down_no_mcad(mocker): + mocker.patch("codeflare_sdk.cluster.cluster.Cluster._throw_for_no_raycluster") mocker.patch("kubernetes.config.load_kube_config", return_value="ignore") mocker.patch("kubernetes.client.ApisApi.get_api_versions") mocker.patch( @@ -3000,6 +3002,37 @@ def test_export_env(): ) +def test_cluster_throw_for_no_raycluster(mocker: MockerFixture): + mocker.patch("kubernetes.client.ApisApi.get_api_versions") + mocker.patch( + "codeflare_sdk.cluster.cluster.get_current_namespace", + return_value="opendatahub", + ) + mocker.patch( + "codeflare_sdk.utils.generate_yaml.get_default_kueue_name", + return_value="default", + ) + + def throw_if_getting_raycluster(group, version, namespace, plural): + if plural == "rayclusters": + raise client.ApiException(status=404) + return + + mocker.patch( + "kubernetes.client.CustomObjectsApi.list_namespaced_custom_object", + side_effect=throw_if_getting_raycluster, + ) + cluster = Cluster( + ClusterConfiguration( + "test_cluster", + image="quay.io/project-codeflare/ray:latest-py39-cu118", + write_to_file=False, + ) + ) + with pytest.raises(RuntimeError): + cluster.up() + + """ Ray Jobs tests """