Skip to content

Commit ec46f4e

Browse files
Adapt openstack_test stage to OTE after legacy Makefile drop
Upstream openstack-test removed make openstack-tests on release-4.20+ (PR #303 and backports). Use make extension / run-suite for those releases, keep the legacy path for older branches, and preserve run-test/junit exit codes for filtered runs. Co-authored-by: Cursor <cursoragent@cursor.com>
1 parent 54d97d5 commit ec46f4e

2 files changed

Lines changed: 230 additions & 10 deletions

File tree

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,12 @@
33
openstack_test_name: openstack-test
44
openstack_testsuite_name: openstack
55
openstack_test_dir: "{{ artifacts_dir }}/{{ openstack_test_name }}"
6+
# Installed binary path is set in run_openstack_test.yml (OTE vs legacy).
67
openstack_test_executable: "{{ home_dir }}/openstack-tests"
78
openstack_test_results_dir: "{{ artifacts_dir }}/{{ openstack_test_name }}-results"
89
openstack_tests_allowlist_file: ""
910
openstack_tests_blocklist_file: ""
1011
openstack_tests_go_version: "{{ tests.default_go_version_target }}"
12+
# OTE (OpenShift Tests Extension) — used when discovered_openshift_release >= 4.20
13+
openstack_test_suite: openstack-test/all
14+
openstack_test_ote_binary_relpath: bin/openstack-test-tests-ext

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

Lines changed: 226 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
- name: Set artifact paths
2+
- name: Set artifact paths and OTE vs legacy mode
33
ansible.builtin.set_fact:
44
all_tests_path: >-
55
{{ openstack_test_results_dir }}/openstack_tests.txt
@@ -9,35 +9,87 @@
99
{{ openstack_tests_blocklist_file | ternary(openstack_test_results_dir + '/blocklist.txt', '') }}
1010
tests_to_run_path: >-
1111
{{ openstack_test_results_dir }}/list_of_tests_to_run.txt
12+
openstack_test_log_path: >-
13+
{{ openstack_test_results_dir }}/{{ openstack_test_name }}.log
14+
openstack_test_junit_path: >-
15+
{{ openstack_test_results_dir }}/junit_e2e_openstack.xml
16+
# Upstream dropped make openstack-tests on release-4.20+ (OTE migration,
17+
# openshift/openstack-test#303 / OSASINFRA-4375; backports #306/#307/#308).
18+
# Older release branches still ship the legacy target.
19+
openstack_test_use_ote: "{{ discovered_openshift_release is version('4.20', '>=') }}"
1220

13-
- name: Build the openstack-tests for tests in the "openstack-test" repository
21+
- name: Set openstack-test executable path for OTE
22+
ansible.builtin.set_fact:
23+
openstack_test_executable: "{{ home_dir }}/openstack-test-tests-ext"
24+
when: openstack_test_use_ote | bool
25+
26+
- name: Set openstack-test executable path for legacy openshift-tests
27+
ansible.builtin.set_fact:
28+
openstack_test_executable: "{{ home_dir }}/openstack-tests"
29+
when: not (openstack_test_use_ote | bool)
30+
31+
# --- Build ---
32+
# OTE: make extension → bin/openstack-test-tests-ext (openshift/openstack-test#303)
33+
- name: Build the OTE openstack-test extension binary
34+
ansible.builtin.shell: |
35+
source {{ home_dir }}/.bashrc
36+
make extension
37+
args:
38+
chdir: "{{ openstack_test_dir }}"
39+
changed_when: true
40+
when: openstack_test_use_ote | bool
41+
42+
- name: Build the legacy openstack-tests binary
1443
ansible.builtin.shell: |
1544
source {{ home_dir }}/.bashrc
1645
make openstack-tests
1746
args:
1847
chdir: "{{ openstack_test_dir }}"
1948
changed_when: true
49+
when: not (openstack_test_use_ote | bool)
2050

21-
- name: Copy openstack-test executable
51+
- name: Copy OTE openstack-test executable
52+
ansible.builtin.copy:
53+
src: "{{ openstack_test_dir }}/{{ openstack_test_ote_binary_relpath }}"
54+
dest: "{{ openstack_test_executable }}"
55+
mode: u=rwx,g=rwx,o=rwx
56+
remote_src: yes
57+
when: openstack_test_use_ote | bool
58+
59+
- name: Copy legacy openstack-test executable
2260
ansible.builtin.copy:
2361
src: "{{ openstack_test_dir }}/openstack-tests"
2462
dest: "{{ openstack_test_executable }}"
2563
mode: u=rwx,g=rwx,o=rwx
2664
remote_src: yes
65+
when: not (openstack_test_use_ote | bool)
2766

2867
- name: Remove source directory once compilation succeeded
2968
ansible.builtin.file:
3069
path: "{{ openstack_test_dir }}"
3170
state: absent
3271

33-
- name: Prepare {{ all_tests_path }} file with all the tests that can be run
72+
# --- List tests ---
73+
- name: Prepare {{ all_tests_path }} with OTE list (names)
74+
ansible.builtin.shell: >
75+
{{ openstack_test_executable }} list
76+
--suite {{ openstack_test_suite }}
77+
-o names > {{ all_tests_path }}
78+
environment:
79+
OS_CLOUD: "{{ user_cloud }}"
80+
KUBECONFIG: "{{ kubeconfig }}"
81+
changed_when: true
82+
when: openstack_test_use_ote | bool
83+
84+
- name: Prepare {{ all_tests_path }} with legacy dry-run
3485
ansible.builtin.shell: >
3586
{{ openstack_test_executable }} run openshift/openstack
3687
--dry-run > {{ all_tests_path }}
3788
environment:
3889
OS_CLOUD: "{{ user_cloud }}"
3990
KUBECONFIG: "{{ kubeconfig }}"
4091
changed_when: true
92+
when: not (openstack_test_use_ote | bool)
4193

4294
- name: Convert the allowlist YAML files to TXT
4395
ansible.builtin.include_role:
@@ -67,13 +119,104 @@
67119
blocklist_file: "{{ blocklist_path }}"
68120
output_file: "{{ tests_to_run_path }}"
69121

122+
- name: Detect whether allow or block list filtering is active
123+
ansible.builtin.set_fact:
124+
openstack_test_filtering: "{{ (openstack_tests_allowlist_file | length > 0) or (openstack_tests_blocklist_file | length > 0) }}"
125+
70126
- name: Run the openshift tests
71127
block:
72-
- name: Run openstack tests
128+
- name: Run OTE suite (no allow/block filter)
129+
ansible.builtin.shell: >
130+
{{ openstack_test_executable }} run-suite {{ openstack_test_suite }}
131+
--junit-path {{ openstack_test_junit_path }}
132+
> {{ openstack_test_log_path }}
133+
environment:
134+
# RHOSO_KUBECONFIG and SHIFTSTACK_PASS_FILE are required for the observability test
135+
OS_CLOUD: "{{ user_cloud }}"
136+
KUBECONFIG: "{{ kubeconfig }}"
137+
RHOSO_KUBECONFIG: "{{ rhoso_kubeconfig }}"
138+
SHIFTSTACK_PASS_FILE: "{{ kubeadmin_password }}"
139+
changed_when: true
140+
when:
141+
- openstack_test_use_ote | bool
142+
- not (openstack_test_filtering | bool)
143+
144+
# OTE run-test has no --junit-path; pipe filtered names via stdin and
145+
# synthesize junit_e2e_*.xml from JSON results for post_openshift_tests.
146+
- name: Run OTE filtered tests via run-test and write junit
147+
ansible.builtin.shell: |
148+
set -o pipefail
149+
cat {{ tests_to_run_path }} | {{ openstack_test_executable }} run-test \
150+
> {{ openstack_test_log_path }} || run_exit=$?
151+
python3 - {{ openstack_test_log_path }} {{ openstack_test_junit_path }} <<'PY'
152+
import json
153+
import sys
154+
import xml.etree.ElementTree as ET
155+
156+
log_path, junit_path = sys.argv[1], sys.argv[2]
157+
with open(log_path, encoding="utf-8") as f:
158+
raw = f.read().strip()
159+
if not raw:
160+
results = []
161+
else:
162+
try:
163+
data = json.loads(raw)
164+
results = data if isinstance(data, list) else [data]
165+
except json.JSONDecodeError:
166+
results = []
167+
for line in raw.splitlines():
168+
line = line.strip()
169+
if not line:
170+
continue
171+
try:
172+
results.append(json.loads(line))
173+
except json.JSONDecodeError:
174+
continue
175+
176+
suite = ET.Element("testsuite", name="openstack-test")
177+
for r in results:
178+
name = r.get("name", "unknown")
179+
duration_ms = r.get("duration") or 0
180+
try:
181+
time_s = f"{float(duration_ms) / 1000.0:.3f}"
182+
except (TypeError, ValueError):
183+
time_s = "0"
184+
case = ET.SubElement(suite, "testcase", name=name, time=time_s)
185+
result = (r.get("result") or "").lower()
186+
if result == "failed":
187+
fail = ET.SubElement(case, "failure")
188+
fail.text = r.get("error") or r.get("output") or "failed"
189+
elif result == "skipped":
190+
skip = ET.SubElement(case, "skipped")
191+
skip.text = r.get("output") or "skipped"
192+
suite.set("tests", str(len(results)))
193+
suite.set(
194+
"failures",
195+
str(sum(1 for r in results if (r.get("result") or "").lower() == "failed")),
196+
)
197+
suite.set(
198+
"skipped",
199+
str(sum(1 for r in results if (r.get("result") or "").lower() == "skipped")),
200+
)
201+
ET.ElementTree(suite).write(junit_path, encoding="utf-8", xml_declaration=True)
202+
PY
203+
py_exit=$?
204+
exit ${run_exit:-${py_exit:-0}}
205+
environment:
206+
OS_CLOUD: "{{ user_cloud }}"
207+
KUBECONFIG: "{{ kubeconfig }}"
208+
RHOSO_KUBECONFIG: "{{ rhoso_kubeconfig }}"
209+
SHIFTSTACK_PASS_FILE: "{{ kubeadmin_password }}"
210+
changed_when: true
211+
when:
212+
- openstack_test_use_ote | bool
213+
- openstack_test_filtering | bool
214+
215+
- name: Run legacy openstack tests
73216
ansible.builtin.shell: >
74217
{{ openstack_test_executable }} run openshift/openstack
75218
-f {{ tests_to_run_path }}
76-
--output-file {{ openstack_test_results_dir }}/{{ openstack_test_name }}.log
219+
--output-file {{ openstack_test_log_path }}
77220
--junit-dir={{ openstack_test_results_dir }} > /dev/null
78221
environment:
79222
# RHOSO_KUBECONFIG and SHIFTSTACK_PASS_FILE are required for the observability test
@@ -82,6 +225,7 @@
82225
RHOSO_KUBECONFIG: "{{ rhoso_kubeconfig }}"
83226
SHIFTSTACK_PASS_FILE: "{{ kubeadmin_password }}"
84227
changed_when: true
228+
when: not (openstack_test_use_ote | bool)
85229

86230
rescue:
87231
- name: Mark the openshift tests as UNSTABLE
@@ -92,21 +236,93 @@
92236
unstable_msg: >-
93237
The openshift test Suite failed.
94238
95-
- name: Get the number of failed openstack tests
239+
- name: Get the number of failed openstack tests (OTE JSON log)
240+
ansible.builtin.shell: |
241+
set -o pipefail
242+
python3 - {{ openstack_test_log_path }} failed <<'PY' || true
243+
import json
244+
import sys
245+
path, want = sys.argv[1], sys.argv[2]
246+
try:
247+
with open(path, encoding="utf-8") as f:
248+
raw = f.read().strip()
249+
except OSError:
250+
print(0)
251+
raise SystemExit
252+
if not raw:
253+
print(0)
254+
raise SystemExit
255+
results = []
256+
try:
257+
data = json.loads(raw)
258+
results = data if isinstance(data, list) else [data]
259+
except json.JSONDecodeError:
260+
for line in raw.splitlines():
261+
line = line.strip()
262+
if not line:
263+
continue
264+
try:
265+
results.append(json.loads(line))
266+
except json.JSONDecodeError:
267+
continue
268+
print(sum(1 for r in results if (r.get("result") or "").lower() == want))
269+
PY
270+
register: number_of_failed_tests
271+
changed_when: false
272+
when: openstack_test_use_ote | bool
273+
274+
- name: Get the number of passed openstack tests (OTE JSON log)
275+
ansible.builtin.shell: |
276+
set -o pipefail
277+
python3 - {{ openstack_test_log_path }} passed <<'PY' || true
278+
import json
279+
import sys
280+
path, want = sys.argv[1], sys.argv[2]
281+
try:
282+
with open(path, encoding="utf-8") as f:
283+
raw = f.read().strip()
284+
except OSError:
285+
print(0)
286+
raise SystemExit
287+
if not raw:
288+
print(0)
289+
raise SystemExit
290+
results = []
291+
try:
292+
data = json.loads(raw)
293+
results = data if isinstance(data, list) else [data]
294+
except json.JSONDecodeError:
295+
for line in raw.splitlines():
296+
line = line.strip()
297+
if not line:
298+
continue
299+
try:
300+
results.append(json.loads(line))
301+
except json.JSONDecodeError:
302+
continue
303+
print(sum(1 for r in results if (r.get("result") or "").lower() == want))
304+
PY
305+
register: number_of_passed_tests
306+
changed_when: false
307+
when: openstack_test_use_ote | bool
308+
309+
- name: Get the number of failed openstack tests (legacy log)
96310
ansible.builtin.shell: >
97311
set -o pipefail &&
98-
grep -e "^failed:" {{ openstack_test_results_dir }}/{{ openstack_test_name }}.log | wc -l
312+
grep -e "^failed:" {{ openstack_test_log_path }} | wc -l
99313
|| true
100314
register: number_of_failed_tests
101315
changed_when: false
316+
when: not (openstack_test_use_ote | bool)
102317

103-
- name: Get the number of passed openstack tests
318+
- name: Get the number of passed openstack tests (legacy log)
104319
ansible.builtin.shell: >
105320
set -o pipefail &&
106-
grep -e "^passed:" {{ openstack_test_results_dir }}/{{ openstack_test_name }}.log | wc -l
321+
grep -e "^passed:" {{ openstack_test_log_path }} | wc -l
107322
|| true
108323
register: number_of_passed_tests
109324
changed_when: false
325+
when: not (openstack_test_use_ote | bool)
110326

111327
# This fail task is added to detect failures in openstack-test execution
112328
- name: Fail the playbook in case there are no failed and passed tests

0 commit comments

Comments
 (0)