@@ -217,7 +217,6 @@ def test_create_basic_cluster(self, cluster_with_basic_executor, mock_k8s_client
217217 assert body ["metadata" ]["name" ] == "testuser-test-cluster-raycluster"
218218 assert body ["metadata" ]["namespace" ] == "test-namespace"
219219 assert body ["spec" ]["rayVersion" ] == "2.43.0"
220-
221220 def test_create_advanced_cluster (self , cluster_with_advanced_executor , mock_k8s_clients ):
222221 """Test creating an advanced Ray cluster with volumes and custom settings."""
223222 mock_api , _ = mock_k8s_clients
@@ -748,11 +747,9 @@ class TestKubeRayExecutorUtilityFunctions:
748747 @pytest .fixture
749748 def basic_cluster_dict (self ):
750749 """Create a basic cluster dictionary for testing."""
751- from nemo_run .core .execution .kuberay import (
752- populate_meta ,
753- populate_ray_head ,
754- populate_worker_group ,
755- )
750+ from nemo_run .core .execution .kuberay import (populate_meta ,
751+ populate_ray_head ,
752+ populate_worker_group )
756753
757754 cluster = {}
758755 cluster = populate_meta (cluster , "test-cluster" , "default" , {}, "2.43.0" )
@@ -866,7 +863,8 @@ def test_populate_ray_head_missing_spec(self):
866863
867864 def test_populate_ray_head_without_dashboard_host (self ):
868865 """Test populate_ray_head automatically adds dashboard-host."""
869- from nemo_run .core .execution .kuberay import populate_meta , populate_ray_head
866+ from nemo_run .core .execution .kuberay import (populate_meta ,
867+ populate_ray_head )
870868
871869 cluster = populate_meta ({}, "test-cluster" , "default" , {}, "2.43.0" )
872870 ray_start_params = {}
@@ -893,7 +891,8 @@ def test_populate_ray_head_without_dashboard_host(self):
893891
894892 def test_update_worker_group_replicas_success (self , basic_cluster_dict ):
895893 """Test successfully updating worker group replicas."""
896- from nemo_run .core .execution .kuberay import update_worker_group_replicas
894+ from nemo_run .core .execution .kuberay import \
895+ update_worker_group_replicas
897896
898897 cluster , success = update_worker_group_replicas (
899898 basic_cluster_dict , "workers" , max_replicas = 5 , min_replicas = 1 , replicas = 3
@@ -907,7 +906,8 @@ def test_update_worker_group_replicas_success(self, basic_cluster_dict):
907906
908907 def test_update_worker_group_replicas_not_found (self , basic_cluster_dict ):
909908 """Test updating non-existent worker group replicas."""
910- from nemo_run .core .execution .kuberay import update_worker_group_replicas
909+ from nemo_run .core .execution .kuberay import \
910+ update_worker_group_replicas
911911
912912 cluster , success = update_worker_group_replicas (
913913 basic_cluster_dict , "nonexistent" , max_replicas = 5 , min_replicas = 1 , replicas = 3
@@ -917,7 +917,8 @@ def test_update_worker_group_replicas_not_found(self, basic_cluster_dict):
917917
918918 def test_update_worker_group_resources_success (self , basic_cluster_dict ):
919919 """Test successfully updating worker group resources."""
920- from nemo_run .core .execution .kuberay import update_worker_group_resources
920+ from nemo_run .core .execution .kuberay import \
921+ update_worker_group_resources
921922
922923 cluster , success = update_worker_group_resources (
923924 basic_cluster_dict ,
@@ -937,7 +938,8 @@ def test_update_worker_group_resources_success(self, basic_cluster_dict):
937938
938939 def test_update_worker_group_resources_all_containers (self , basic_cluster_dict ):
939940 """Test updating resources for all containers in a worker group."""
940- from nemo_run .core .execution .kuberay import update_worker_group_resources
941+ from nemo_run .core .execution .kuberay import \
942+ update_worker_group_resources
941943
942944 # Add another container to test "all_containers" functionality
943945 basic_cluster_dict ["spec" ]["workerGroupSpecs" ][0 ]["template" ]["spec" ]["containers" ].append (
@@ -966,7 +968,8 @@ def test_update_worker_group_resources_all_containers(self, basic_cluster_dict):
966968
967969 def test_update_worker_group_resources_specific_container (self , basic_cluster_dict ):
968970 """Test updating resources for a specific container."""
969- from nemo_run .core .execution .kuberay import update_worker_group_resources
971+ from nemo_run .core .execution .kuberay import \
972+ update_worker_group_resources
970973
971974 # Add another container
972975 basic_cluster_dict ["spec" ]["workerGroupSpecs" ][0 ]["template" ]["spec" ]["containers" ].append (
@@ -993,7 +996,8 @@ def test_update_worker_group_resources_specific_container(self, basic_cluster_di
993996
994997 def test_update_worker_group_resources_no_containers (self ):
995998 """Test updating resources when worker group has no containers."""
996- from nemo_run .core .execution .kuberay import update_worker_group_resources
999+ from nemo_run .core .execution .kuberay import \
1000+ update_worker_group_resources
9971001
9981002 cluster = {
9991003 "spec" : {
@@ -1063,7 +1067,8 @@ def test_delete_worker_group_not_found(self, basic_cluster_dict):
10631067
10641068 def test_worker_group_with_labels_and_annotations (self ):
10651069 """Test creating worker group with labels and annotations."""
1066- from nemo_run .core .execution .kuberay import populate_meta , populate_worker_group
1070+ from nemo_run .core .execution .kuberay import (populate_meta ,
1071+ populate_worker_group )
10671072
10681073 cluster = populate_meta ({}, "test-cluster" , "default" , {}, "2.43.0" )
10691074 cluster ["spec" ]["workerGroupSpecs" ] = []
@@ -2074,3 +2079,170 @@ def test_cluster_create_without_lifecycle_kwargs(self, mock_k8s_clients):
20742079 # Should create lifecycle_kwargs and succeed
20752080 assert hasattr (executor , "lifecycle_kwargs" )
20762081 assert mock_api .create_namespaced_custom_object .called
2082+
2083+
2084+ class TestKubeConfigLoading :
2085+ """Test kube config loading fallback scenarios."""
2086+
2087+ def test_kuberay_cluster_load_kube_config_success (self ):
2088+ """Test successful loading of kube config for KubeRayCluster."""
2089+ with patch ("nemo_run.run.ray.kuberay.config.load_kube_config" ) as mock_load_kube :
2090+ with patch ("nemo_run.run.ray.kuberay.client.CustomObjectsApi" ):
2091+ with patch ("nemo_run.run.ray.kuberay.client.CoreV1Api" ):
2092+ with patch ("nemo_run.run.ray.kuberay.get_user" , return_value = "testuser" ):
2093+ executor = KubeRayExecutor (namespace = "test-namespace" )
2094+ cluster = KubeRayCluster (name = "test-cluster" , executor = executor )
2095+
2096+ # Verify load_kube_config was called and succeeded
2097+ mock_load_kube .assert_called_once ()
2098+
2099+ def test_kuberay_cluster_fallback_to_incluster_config (self ):
2100+ """Test fallback to incluster config when kube config fails for KubeRayCluster."""
2101+ with patch (
2102+ "nemo_run.run.ray.kuberay.config.load_kube_config" ,
2103+ side_effect = Exception ("Kube config not found" ),
2104+ ) as mock_load_kube :
2105+ with patch (
2106+ "nemo_run.run.ray.kuberay.config.load_incluster_config"
2107+ ) as mock_load_incluster :
2108+ with patch ("nemo_run.run.ray.kuberay.client.CustomObjectsApi" ):
2109+ with patch ("nemo_run.run.ray.kuberay.client.CoreV1Api" ):
2110+ with patch ("nemo_run.run.ray.kuberay.get_user" , return_value = "testuser" ):
2111+ executor = KubeRayExecutor (namespace = "test-namespace" )
2112+ cluster = KubeRayCluster (name = "test-cluster" , executor = executor )
2113+
2114+ # Verify fallback to incluster config
2115+ mock_load_kube .assert_called_once ()
2116+ mock_load_incluster .assert_called_once ()
2117+
2118+ def test_kuberay_cluster_both_configs_fail (self ):
2119+ """Test when both kube config and incluster config fail for KubeRayCluster."""
2120+ kube_config_error = Exception ("Kube config not found" )
2121+ incluster_config_error = Exception ("Not running in cluster" )
2122+
2123+ with patch (
2124+ "nemo_run.run.ray.kuberay.config.load_kube_config" , side_effect = kube_config_error
2125+ ):
2126+ with patch (
2127+ "nemo_run.run.ray.kuberay.config.load_incluster_config" ,
2128+ side_effect = incluster_config_error ,
2129+ ):
2130+ with pytest .raises (Exception ) as exc_info :
2131+ with patch ("nemo_run.run.ray.kuberay.get_user" , return_value = "testuser" ):
2132+ executor = KubeRayExecutor (namespace = "test-namespace" )
2133+ KubeRayCluster (name = "test-cluster" , executor = executor )
2134+
2135+ # Should raise the original kube config error
2136+ assert exc_info .value == kube_config_error
2137+
2138+ def test_kuberay_job_load_kube_config_success (self ):
2139+ """Test successful loading of kube config for KubeRayJob."""
2140+ with patch ("nemo_run.run.ray.kuberay.config.load_kube_config" ) as mock_load_kube :
2141+ with patch ("nemo_run.run.ray.kuberay.client.CustomObjectsApi" ):
2142+ with patch ("nemo_run.run.ray.kuberay.client.CoreV1Api" ):
2143+ with patch ("nemo_run.run.ray.kuberay.get_user" , return_value = "testuser" ):
2144+ executor = KubeRayExecutor (namespace = "test-namespace" )
2145+ job = KubeRayJob (name = "test-job" , executor = executor )
2146+
2147+ # Verify load_kube_config was called and succeeded
2148+ mock_load_kube .assert_called ()
2149+
2150+ def test_kuberay_job_fallback_to_incluster_config (self ):
2151+ """Test fallback to incluster config when kube config fails for KubeRayJob."""
2152+ with patch (
2153+ "nemo_run.run.ray.kuberay.config.load_kube_config" ,
2154+ side_effect = Exception ("Kube config not found" ),
2155+ ) as mock_load_kube :
2156+ with patch (
2157+ "nemo_run.run.ray.kuberay.config.load_incluster_config"
2158+ ) as mock_load_incluster :
2159+ with patch ("nemo_run.run.ray.kuberay.client.CustomObjectsApi" ):
2160+ with patch ("nemo_run.run.ray.kuberay.client.CoreV1Api" ):
2161+ with patch ("nemo_run.run.ray.kuberay.get_user" , return_value = "testuser" ):
2162+ executor = KubeRayExecutor (namespace = "test-namespace" )
2163+ job = KubeRayJob (name = "test-job" , executor = executor )
2164+
2165+ # Verify fallback to incluster config
2166+ # Called twice: once for executor, once for job
2167+ assert mock_load_kube .call_count >= 1
2168+ assert mock_load_incluster .call_count >= 1
2169+
2170+ def test_kuberay_job_both_configs_fail (self ):
2171+ """Test when both kube config and incluster config fail for KubeRayJob."""
2172+ kube_config_error = Exception ("Kube config not found" )
2173+ incluster_config_error = Exception ("Not running in cluster" )
2174+
2175+ with patch (
2176+ "nemo_run.run.ray.kuberay.config.load_kube_config" , side_effect = kube_config_error
2177+ ):
2178+ with patch (
2179+ "nemo_run.run.ray.kuberay.config.load_incluster_config" ,
2180+ side_effect = incluster_config_error ,
2181+ ):
2182+ with pytest .raises (Exception ) as exc_info :
2183+ with patch ("nemo_run.run.ray.kuberay.get_user" , return_value = "testuser" ):
2184+ executor = KubeRayExecutor (namespace = "test-namespace" )
2185+ KubeRayJob (name = "test-job" , executor = executor )
2186+
2187+ # Should raise the original kube config error
2188+ assert exc_info .value == kube_config_error
2189+
2190+ def test_executor_load_kube_config_success (self ):
2191+ """Test successful loading of kube config for KubeRayExecutor."""
2192+ with patch (
2193+ "nemo_run.core.execution.kuberay.config.load_kube_config"
2194+ ) as mock_load_kube :
2195+ with patch ("nemo_run.core.execution.kuberay.client.CustomObjectsApi" ):
2196+ with patch ("nemo_run.core.execution.kuberay.client.CoreV1Api" ):
2197+ executor = KubeRayExecutor (namespace = "test-namespace" )
2198+
2199+ # Verify load_kube_config was called and succeeded
2200+ mock_load_kube .assert_called_once ()
2201+
2202+ def test_executor_fallback_to_incluster_config (self ):
2203+ """Test fallback to incluster config when kube config fails for KubeRayExecutor."""
2204+ with patch (
2205+ "nemo_run.core.execution.kuberay.config.load_kube_config" ,
2206+ side_effect = Exception ("Kube config not found" ),
2207+ ) as mock_load_kube :
2208+ with patch (
2209+ "nemo_run.core.execution.kuberay.config.load_incluster_config"
2210+ ) as mock_load_incluster :
2211+ with patch ("nemo_run.core.execution.kuberay.client.CustomObjectsApi" ):
2212+ with patch ("nemo_run.core.execution.kuberay.client.CoreV1Api" ):
2213+ executor = KubeRayExecutor (namespace = "test-namespace" )
2214+
2215+ # Verify fallback to incluster config
2216+ mock_load_kube .assert_called_once ()
2217+ mock_load_incluster .assert_called_once ()
2218+
2219+ def test_executor_both_configs_fail (self ):
2220+ """Test when both kube config and incluster config fail for KubeRayExecutor."""
2221+ kube_config_error = Exception ("Kube config not found" )
2222+ incluster_config_error = Exception ("Not running in cluster" )
2223+
2224+ with patch (
2225+ "nemo_run.core.execution.kuberay.config.load_kube_config" ,
2226+ side_effect = kube_config_error ,
2227+ ):
2228+ with patch (
2229+ "nemo_run.core.execution.kuberay.config.load_incluster_config" ,
2230+ side_effect = incluster_config_error ,
2231+ ):
2232+ with pytest .raises (Exception ) as exc_info :
2233+ KubeRayExecutor (namespace = "test-namespace" )
2234+
2235+ # Should raise the original kube config error
2236+ assert exc_info .value == kube_config_error
2237+ ):
2238+ with pytest .raises (Exception ) as exc_info :
2239+ KubeRayExecutor (namespace = "test-namespace" )
2240+
2241+ # Should raise the original kube config error
2242+ assert exc_info .value == kube_config_error
2243+ ):
2244+ with pytest .raises (Exception ) as exc_info :
2245+ KubeRayExecutor (namespace = "test-namespace" )
2246+
2247+ # Should raise the original kube config error
2248+ assert exc_info .value == kube_config_error
0 commit comments