Skip to content

Commit b167626

Browse files
openstack_test: pass leak-cluster matchers via file
Shell-splitting multi-word matcher argv pulled accidental tests into the serial coda (leak_cluster=7). Write one matcher per line and invoke the splitter with a matchers file path instead. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 2fdd30c commit b167626

3 files changed

Lines changed: 55 additions & 29 deletions

File tree

‎collection/stages/roles/openstack_test/defaults/main.yml‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,8 @@ openstack_test_ote_run_serial: false
1919
# Keeps the bulk suite in batch to limit wall time. No-ops when the matchers
2020
# are absent (e.g. lb_tests allowlists). Requires OTE filtered run path.
2121
openstack_test_machines_leak_cluster_serial: true
22-
# Ordered substrings matched against list_of_tests_to_run.txt. For the
22+
# Ordered substrings matched against list_of_tests_to_run.txt (one phrase
23+
# each; written to a matchers file so shell never splits them). For the
2324
# MachineSet replica matcher, ControlPlane variants are skipped.
2425
openstack_test_machines_leak_cluster_matchers:
2526
- "Bugfix bz_2073398: [Serial] MachineSet scale-in does not leak OpenStack ports"

‎collection/stages/roles/openstack_test/files/split_machines_leak_cluster.py‎

Lines changed: 33 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,50 +1,63 @@
11
#!/usr/bin/env python3
22
"""Split machines leak-cluster tests out of an OTE test list for serial coda.
33
4-
Reads the filtered list_of_tests_to_run.txt, writes:
4+
Reads the filtered list_of_tests_to_run.txt and an ordered matchers file
5+
(one substring per line), writes:
56
- leak_cluster_serial.txt: ordered matches for Option B serial run
67
- list_of_tests_to_run.txt: remaining tests for the batch suite
78
8-
Matchers are plain substrings. The MachineSet replica matcher skips any line
9-
that also contains "ControlPlane".
9+
Matchers are plain substrings (one full phrase per line). The MachineSet
10+
replica matcher skips any line that also contains "ControlPlane".
1011
"""
1112
from __future__ import annotations
1213

1314
import argparse
1415
import sys
1516

1617

18+
def load_matchers(path: str) -> list[str]:
19+
with open(path, "r", encoding="utf-8") as f:
20+
return [ln.strip() for ln in f if ln.strip() and not ln.strip().startswith("#")]
21+
22+
23+
def pick(remaining: list[str], matcher: str) -> str | None:
24+
for ln in remaining:
25+
if not ln.strip():
26+
continue
27+
if matcher not in ln:
28+
continue
29+
# Avoid ControlPlane MachineSet when matching worker MachineSet replica.
30+
if "MachineSet replica number corresponds to the number of Machines" in matcher:
31+
if "ControlPlane" in ln:
32+
continue
33+
return ln
34+
return None
35+
36+
1737
def main() -> int:
1838
parser = argparse.ArgumentParser()
1939
parser.add_argument("tests_to_run_path")
2040
parser.add_argument("leak_cluster_path")
2141
parser.add_argument(
22-
"matchers",
23-
nargs="+",
24-
help="Ordered substrings to pull into the serial leak-cluster list",
42+
"matchers_file",
43+
help="File with one matcher substring per line (ordered)",
2544
)
2645
args = parser.parse_args()
2746

47+
matchers = load_matchers(args.matchers_file)
48+
if not matchers:
49+
print("leak_cluster=0 batch_remaining=unchanged (empty matchers file)")
50+
open(args.leak_cluster_path, "w", encoding="utf-8").close()
51+
return 0
52+
2853
with open(args.tests_to_run_path, "r", encoding="utf-8") as f:
2954
lines = [ln.rstrip("\n") for ln in f]
3055

3156
remaining = list(lines)
3257
selected: list[str] = []
3358

34-
for matcher in args.matchers:
35-
found = None
36-
for ln in remaining:
37-
if not ln.strip():
38-
continue
39-
if matcher not in ln:
40-
continue
41-
if matcher.startswith("MachineSet replica") and "ControlPlane" in ln:
42-
continue
43-
if "MachineSet replica number corresponds to the number of Machines" in matcher:
44-
if "ControlPlane" in ln:
45-
continue
46-
found = ln
47-
break
59+
for matcher in matchers:
60+
found = pick(remaining, matcher)
4861
if found is None:
4962
continue
5063
selected.append(found)

‎collection/stages/roles/openstack_test/tasks/run_openstack_test.yml‎

Lines changed: 20 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -146,20 +146,32 @@
146146
ansible.builtin.set_fact:
147147
openstack_test_leak_cluster_path: >-
148148
{{ openstack_test_results_dir }}/machines_leak_cluster_serial.txt
149+
openstack_test_leak_cluster_matchers_path: >-
150+
{{ openstack_test_results_dir }}/machines_leak_cluster_matchers.txt
151+
when:
152+
- openstack_test_use_ote | bool
153+
- openstack_test_machines_leak_cluster_serial | bool
154+
- openstack_test_filtering | bool
155+
156+
# Write matchers one-per-line so multi-word phrases are never shell-split.
157+
- name: Write machines leak-cluster matchers file
158+
ansible.builtin.copy:
159+
dest: "{{ openstack_test_leak_cluster_matchers_path }}"
160+
mode: "0644"
161+
content: "{{ openstack_test_machines_leak_cluster_matchers | map('trim') | join('\n') }}\n"
149162
when:
150163
- openstack_test_use_ote | bool
151164
- openstack_test_machines_leak_cluster_serial | bool
152165
- openstack_test_filtering | bool
153166

154167
- name: Split machines leak-cluster tests from batch list for serial coda
155-
ansible.builtin.shell: |
156-
set -o pipefail
157-
python3 "{{ role_path }}/files/split_machines_leak_cluster.py" \
158-
"{{ tests_to_run_path }}" \
159-
"{{ openstack_test_leak_cluster_path }}" \
160-
{% for m in openstack_test_machines_leak_cluster_matchers %}
161-
{{ m | quote }}{% if not loop.last %} \{% endif %}
162-
{% endfor %}
168+
ansible.builtin.command:
169+
argv:
170+
- python3
171+
- "{{ role_path }}/files/split_machines_leak_cluster.py"
172+
- "{{ tests_to_run_path }}"
173+
- "{{ openstack_test_leak_cluster_path }}"
174+
- "{{ openstack_test_leak_cluster_matchers_path }}"
163175
register: openstack_test_leak_cluster_split
164176
changed_when: true
165177
when:

0 commit comments

Comments
 (0)