Skip to content

Commit 554d7f2

Browse files
committed
Add e2e tests for apply
1 parent dae1d8c commit 554d7f2

File tree

3 files changed

+158
-1
lines changed

3 files changed

+158
-1
lines changed

CONTRIBUTING.md

+1-1
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ pytest -v src/codeflare_sdk
7676

7777
### Local e2e Testing
7878

79-
- Please follow the [e2e documentation](https://github.com/project-codeflare/codeflare-sdk/blob/main/docs/e2e.md)
79+
- Please follow the [e2e documentation](https://github.com/project-codeflare/codeflare-sdk/blob/main/docs/sphinx/user-docs/e2e.rst)
8080

8181
#### Code Coverage
8282

src/codeflare_sdk/ray/cluster/cluster.py

+2
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,8 @@ def up(self):
143143
Applies the Cluster yaml, pushing the resource request onto
144144
the Kueue localqueue.
145145
"""
146+
# TODO: Add deprecation message in favor of apply()
147+
# print( "WARNING: The up() is planned for deprecation in favor of apply().")
146148

147149
# check if RayCluster CustomResourceDefinition exists if not throw RuntimeError
148150
self._throw_for_no_raycluster()

tests/e2e/cluster_apply_test.py

+155
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
from codeflare_sdk import Cluster, ClusterConfiguration
2+
import pytest
3+
from kubernetes import client
4+
5+
from support import (
6+
initialize_kubernetes_client,
7+
create_namespace,
8+
delete_namespace,
9+
get_ray_cluster,
10+
)
11+
12+
13+
@pytest.mark.kind
14+
class TestRayClusterApply:
15+
def setup_method(self):
16+
initialize_kubernetes_client(self)
17+
18+
def teardown_method(self):
19+
delete_namespace(self)
20+
21+
def test_cluster_apply(self):
22+
self.setup_method()
23+
create_namespace(self)
24+
25+
cluster_name = "test-cluster-apply"
26+
namespace = self.namespace
27+
28+
# Initial configuration with 1 worker
29+
initial_config = ClusterConfiguration(
30+
name=cluster_name,
31+
namespace=namespace,
32+
num_workers=1,
33+
head_cpu_requests="500m",
34+
head_cpu_limits="1",
35+
head_memory_requests="1Gi",
36+
head_memory_limits="2Gi",
37+
worker_cpu_requests="500m",
38+
worker_cpu_limits="1",
39+
worker_memory_requests="1Gi",
40+
worker_memory_limits="2Gi",
41+
write_to_file=True,
42+
verify_tls=False,
43+
)
44+
45+
# Create the cluster
46+
cluster = Cluster(initial_config)
47+
cluster.apply()
48+
49+
# Wait for the cluster to be ready
50+
cluster.wait_ready()
51+
status = cluster.status()
52+
assert status["ready"], f"Cluster {cluster_name} is not ready: {status}"
53+
54+
# Verify the cluster is created
55+
ray_cluster = get_ray_cluster(cluster_name, namespace)
56+
assert ray_cluster is not None, "Cluster was not created successfully"
57+
assert (
58+
ray_cluster["spec"]["workerGroupSpecs"][0]["replicas"] == 1
59+
), "Initial worker count does not match"
60+
61+
# Update configuration with 3 workers
62+
updated_config = ClusterConfiguration(
63+
name=cluster_name,
64+
namespace=namespace,
65+
num_workers=3,
66+
head_cpu_requests="500m",
67+
head_cpu_limits="1",
68+
head_memory_requests="1Gi",
69+
head_memory_limits="2Gi",
70+
worker_cpu_requests="500m",
71+
worker_cpu_limits="1",
72+
worker_memory_requests="1Gi",
73+
worker_memory_limits="2Gi",
74+
write_to_file=True,
75+
verify_tls=False,
76+
)
77+
78+
# Apply the updated configuration
79+
cluster.config = updated_config
80+
cluster.apply()
81+
82+
# Wait for the updated cluster to be ready
83+
cluster.wait_ready()
84+
updated_status = cluster.status()
85+
assert updated_status["ready"], f"Cluster {cluster_name} is not ready after update: {updated_status}"
86+
87+
# Verify the cluster is updated
88+
updated_ray_cluster = get_ray_cluster(cluster_name, namespace)
89+
assert (
90+
updated_ray_cluster["spec"]["workerGroupSpecs"][0]["replicas"] == 3
91+
), "Worker count was not updated"
92+
93+
# Clean up
94+
cluster.down()
95+
ray_cluster = get_ray_cluster(cluster_name, namespace)
96+
assert ray_cluster is None, "Cluster was not deleted successfully"
97+
98+
def test_apply_invalid_update(self):
99+
self.setup_method()
100+
create_namespace(self)
101+
102+
cluster_name = "test-cluster-apply-invalid"
103+
namespace = self.namespace
104+
105+
# Initial configuration
106+
initial_config = ClusterConfiguration(
107+
name=cluster_name,
108+
namespace=namespace,
109+
num_workers=1,
110+
head_cpu_requests="500m",
111+
head_cpu_limits="1",
112+
head_memory_requests="1Gi",
113+
head_memory_limits="2Gi",
114+
worker_cpu_requests="500m",
115+
worker_cpu_limits="1",
116+
worker_memory_requests="1Gi",
117+
worker_memory_limits="2Gi",
118+
write_to_file=True,
119+
verify_tls=False,
120+
)
121+
122+
# Create the cluster
123+
cluster = Cluster(initial_config)
124+
cluster.apply()
125+
126+
# Wait for the cluster to be ready
127+
cluster.wait_ready()
128+
status = cluster.status()
129+
assert status["ready"], f"Cluster {cluster_name} is not ready: {status}"
130+
131+
# Update with an invalid configuration (e.g., immutable field change)
132+
invalid_config = ClusterConfiguration(
133+
name=cluster_name,
134+
namespace=namespace,
135+
num_workers=2,
136+
head_cpu_requests="1",
137+
head_cpu_limits="2", # Changing CPU limits (immutable)
138+
head_memory_requests="1Gi",
139+
head_memory_limits="2Gi",
140+
worker_cpu_requests="500m",
141+
worker_cpu_limits="1",
142+
worker_memory_requests="1Gi",
143+
worker_memory_limits="2Gi",
144+
write_to_file=True,
145+
verify_tls=False,
146+
)
147+
148+
# Try to apply the invalid configuration and expect failure
149+
cluster.config = invalid_config
150+
with pytest.raises(RuntimeError, match="Immutable fields detected"):
151+
cluster.apply()
152+
153+
# Clean up
154+
cluster.down()
155+

0 commit comments

Comments
 (0)