|
36 | 36 |
|
37 | 37 | @task |
38 | 38 | def trigger_oom_failure(info, pod_name: str): |
39 | | - """ |
40 | | - Triggers an OOM (Out-of-Memory) event on a specified TPU pod. |
41 | | -
|
42 | | - This task connects to the specified pod via kubectl exec and runs a |
43 | | - memory-intensive Python script (oomkill.py). The script is designed |
44 | | - to exhaust the container's memory limit, forcing a SIGKILL (Exit Code 137). |
45 | | - A connection error is expected and caught when the pod is killed. |
46 | | -
|
47 | | - Args: |
48 | | - info: Node pool and cluster information. |
49 | | - pod_name (str): The name of the target pod to be OOMKilled. |
50 | | - """ |
51 | | - with tempfile.NamedTemporaryFile() as temp_config_file: |
52 | | - env = os.environ.copy() |
53 | | - env["KUBECONFIG"] = temp_config_file.name |
54 | | - |
55 | | - cmd = " && ".join([ |
56 | | - jobset.Command.get_credentials_command(info), |
57 | | - f"kubectl exec {pod_name} -n default -- python3 oomkill.py", |
58 | | - ]) |
59 | | - |
60 | | - try: |
61 | | - print(f"Executing OOM script in {pod_name}...") |
62 | | - subprocess.run_exec(cmd, env=env) |
63 | | - except Exception as e: |
64 | | - print(f"Expectation: Connection closed due to OOMKilled. Info: {e}") |
| 39 | + """ |
| 40 | + Triggers an OOM (Out-of-Memory) event on a specified TPU pod. |
| 41 | +
|
| 42 | + This task connects to the specified pod via kubectl exec and runs a |
| 43 | + memory-intensive Python script (oomkill.py). The script is designed |
| 44 | + to exhaust the container's memory limit, forcing a SIGKILL (Exit Code 137). |
| 45 | + A connection error is expected and caught when the pod is killed. |
| 46 | +
|
| 47 | + Args: |
| 48 | + info: Node pool and cluster information. |
| 49 | + pod_name (str): The name of the target pod to be OOMKilled. |
| 50 | + """ |
| 51 | + with tempfile.NamedTemporaryFile() as temp_config_file: |
| 52 | + env = os.environ.copy() |
| 53 | + env["KUBECONFIG"] = temp_config_file.name |
| 54 | + |
| 55 | + cmd = " && ".join([ |
| 56 | + jobset.Command.get_credentials_command(info), |
| 57 | + f"kubectl exec {pod_name} -n default -- python3 oomkill.py", |
| 58 | + ]) |
| 59 | + |
| 60 | + try: |
| 61 | + print(f"Executing OOM script in {pod_name}...") |
| 62 | + subprocess.run_exec(cmd, env=env) |
| 63 | + except Exception as e: |
| 64 | + print(f"Expectation: Connection closed due to OOMKilled. Info: {e}") |
| 65 | + |
65 | 66 |
|
66 | 67 | @task |
67 | 68 | def pick_random_pod(pod_names: List[str]) -> str: |
68 | | - """ |
69 | | - Randomly selects one pod from a list of available JobSet pods. |
| 69 | + """ |
| 70 | + Randomly selects one pod from a list of available JobSet pods. |
| 71 | +
|
| 72 | + This ensures that the fault injection is performed on a single |
| 73 | + unit of the TPU slice, allowing the test to validate how the |
| 74 | + JobSet controller handles partial failures within a replica. |
70 | 75 |
|
71 | | - This ensures that the fault injection is performed on a single |
72 | | - unit of the TPU slice, allowing the test to validate how the |
73 | | - JobSet controller handles partial failures within a replica. |
| 76 | + Args: |
| 77 | + pod_names (List[str]): List of active pod names in the JobSet. |
74 | 78 |
|
75 | | - Args: |
76 | | - pod_names (List[str]): List of active pod names in the JobSet. |
| 79 | + Returns: |
| 80 | + str: The name of the randomly selected pod. |
| 81 | + """ |
| 82 | + if not pod_names: |
| 83 | + raise ValueError("No pods found to attack!") |
| 84 | + chosen_pod = random.choice(pod_names) |
| 85 | + print(f"Randomly selected pod for OOM test: {chosen_pod}") |
| 86 | + return chosen_pod |
77 | 87 |
|
78 | | - Returns: |
79 | | - str: The name of the randomly selected pod. |
80 | | - """ |
81 | | - if not pod_names: |
82 | | - raise ValueError("No pods found to attack!") |
83 | | - chosen_pod = random.choice(pod_names) |
84 | | - print(f"Randomly selected pod for OOM test: {chosen_pod}") |
85 | | - return chosen_pod |
86 | 88 |
|
87 | 89 | with models.DAG( |
88 | 90 | dag_id="jobset_ttr_pod_oom", |
@@ -146,7 +148,7 @@ def pick_random_pod(pod_names: List[str]) -> str: |
146 | 148 | raw_yaml = jobset_config.generate_yaml(workload_script="sleep infinity") |
147 | 149 | custom_yaml = raw_yaml.replace( |
148 | 150 | "google.com/tpu: 4", |
149 | | - "google.com/tpu: 4\n memory: \"4Gi\"" |
| 151 | + 'google.com/tpu: 4\n memory: "4Gi"', |
150 | 152 | ) |
151 | 153 | # Keyword arguments are generated dynamically at runtime (pylint does not |
152 | 154 | # know this signature). |
@@ -185,21 +187,20 @@ def pick_random_pod(pod_names: List[str]) -> str: |
185 | 187 | namespace=jobset_config.namespace, |
186 | 188 | ) |
187 | 189 |
|
188 | | - select_random_pod = pick_random_pod.override(task_id='select_random_pod')( |
189 | | - pod_names=pod_names |
| 190 | + select_random_pod = pick_random_pod.override(task_id="select_random_pod")( |
| 191 | + pod_names=pod_names |
190 | 192 | ) |
191 | 193 |
|
192 | | - trigger_oom_killed = trigger_oom_failure.override(task_id='trigger_oom_killed')( |
193 | | - info=cluster_info, |
194 | | - pod_name=select_random_pod |
195 | | - ) |
| 194 | + trigger_oom_killed = trigger_oom_failure.override( |
| 195 | + task_id="trigger_oom_killed" |
| 196 | + )(info=cluster_info, pod_name=select_random_pod) |
196 | 197 |
|
197 | 198 | wait_for_metric_upload = jobset.wait_for_jobset_ttr_to_be_found.override( |
198 | | - task_id="wait_for_jobset_ttr_to_be_found" |
199 | | - )( |
200 | | - node_pool=cluster_info, |
201 | | - jobset_name=jobset_config.jobset_name, |
202 | | - ) |
| 199 | + task_id="wait_for_jobset_ttr_to_be_found" |
| 200 | + )( |
| 201 | + node_pool=cluster_info, |
| 202 | + jobset_name=jobset_config.jobset_name, |
| 203 | + ) |
203 | 204 |
|
204 | 205 | cleanup_workload = jobset.end_workload.override( |
205 | 206 | task_id="cleanup_workload", trigger_rule=TriggerRule.ALL_DONE |
|
0 commit comments