Skip to content

Commit 886acb8

Browse files
committed
test: preserve failed integration logs
Copy CCM log and config files before integration cleanup clears or removes cluster directories, so failed CI jobs can publish Scylla-side diagnostics.
1 parent 8f772c8 commit 886acb8

2 files changed

Lines changed: 62 additions & 1 deletion

File tree

tests/integration/__init__.py

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import time
3232
import traceback
3333
import platform
34+
from pathlib import Path
3435
from threading import Event
3536
from subprocess import call
3637
from itertools import groupby
@@ -72,10 +73,59 @@
7273
if not os.path.exists(path):
7374
os.mkdir(path)
7475

76+
_failed_reports = []
77+
_log_artifact_dir = os.environ.get('INTEGRATION_LOG_ARTIFACT_DIR', 'integration-test-logs')
78+
_preserved_file_extensions = ('.conf', '.log', '.yaml', '.yml')
79+
7580
cass_version = None
7681
cql_version = None
7782

7883

84+
def record_failed_test_report(report):
85+
if report.failed:
86+
_failed_reports.append(report)
87+
88+
89+
def preserve_ccm_logs_on_failure(cluster_name=None):
90+
if not _failed_reports:
91+
return
92+
93+
ccm_root = Path(path)
94+
if cluster_name:
95+
ccm_root = ccm_root / cluster_name
96+
if not ccm_root.is_dir():
97+
return
98+
99+
destination = Path(_log_artifact_dir).resolve()
100+
ccm_destination = destination / 'ccm'
101+
if cluster_name:
102+
ccm_destination = ccm_destination / cluster_name
103+
ccm_destination.mkdir(parents=True, exist_ok=True)
104+
105+
with (destination / 'failed-tests.txt').open('w') as failed_tests:
106+
for report in _failed_reports:
107+
failed_tests.write('%s %s\n' % (report.when, report.nodeid))
108+
109+
# Preserve CCM logs/configs before cleanup removes the cluster directories.
110+
for source_file in ccm_root.rglob('*'):
111+
if not source_file.is_file():
112+
continue
113+
114+
rel_file = source_file.relative_to(ccm_root)
115+
preserve_all = bool({'conf', 'logs'}.intersection(rel_file.parts))
116+
if not preserve_all and not source_file.name.endswith(_preserved_file_extensions):
117+
continue
118+
119+
destination_file = ccm_destination / rel_file
120+
try:
121+
destination_file.parent.mkdir(parents=True, exist_ok=True)
122+
shutil.copy2(source_file, destination_file)
123+
except OSError as exc:
124+
log.warning("Failed to preserve file %s: %s", source_file, exc)
125+
126+
log.info("Preserved ccm logs for failed tests under %s", destination)
127+
128+
79129
def get_server_versions():
80130
"""
81131
Probe system.local table to determine Cassandra and CQL version.
@@ -383,6 +433,7 @@ def remove_cluster():
383433
tries = 0
384434
while tries < 100:
385435
try:
436+
preserve_ccm_logs_on_failure(CCM_CLUSTER.name)
386437
CCM_CLUSTER.remove()
387438
CCM_CLUSTER = None
388439
return
@@ -455,6 +506,7 @@ def use_cluster(cluster_name, nodes, ipformat=None, start=True, workloads=None,
455506
try:
456507
CCM_CLUSTER = CCMClusterFactory.load(path, cluster_name)
457508
log.debug("Found existing CCM cluster, {0}; clearing.".format(cluster_name))
509+
preserve_ccm_logs_on_failure(cluster_name)
458510
CCM_CLUSTER.clear()
459511
CCM_CLUSTER.set_install_dir(**ccm_options)
460512
CCM_CLUSTER.set_configuration_options(configuration_options)
@@ -471,6 +523,7 @@ def use_cluster(cluster_name, nodes, ipformat=None, start=True, workloads=None,
471523
# Make sure we cleanup old cluster dir if it exists
472524
cluster_path = os.path.join(path, cluster_name)
473525
if os.path.exists(cluster_path):
526+
preserve_ccm_logs_on_failure(cluster_name)
474527
shutil.rmtree(cluster_path)
475528

476529
if SCYLLA_VERSION:
@@ -553,6 +606,7 @@ def teardown_package():
553606
return
554607
# when multiple modules are run explicitly, this runs between them
555608
# need to make sure CCM_CLUSTER is properly cleared for that case
609+
preserve_ccm_logs_on_failure()
556610
remove_cluster()
557611
for cluster_name in [CLUSTER_NAME, MULTIDC_CLUSTER_NAME]:
558612
try:

tests/integration/conftest.py

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,17 +4,23 @@
44
import pytest
55
from ccmlib.cluster_factory import ClusterFactory as CCMClusterFactory
66

7-
from tests.integration import teardown_package
7+
from tests.integration import record_failed_test_report, preserve_ccm_logs_on_failure, teardown_package
88

99
from . import CLUSTER_NAME, SINGLE_NODE_CLUSTER_NAME, MULTIDC_CLUSTER_NAME
1010
from . import path as ccm_path
1111

1212

13+
def pytest_runtest_logreport(report):
14+
record_failed_test_report(report)
15+
16+
1317
@pytest.fixture(scope="session", autouse=True)
1418
def cleanup_clusters():
1519

1620
yield
1721

22+
preserve_ccm_logs_on_failure()
23+
1824
if not os.environ.get('DISABLE_CLUSTER_CLEANUP'):
1925
for cluster_name in [CLUSTER_NAME, SINGLE_NODE_CLUSTER_NAME, MULTIDC_CLUSTER_NAME,
2026
'cluster_tests', 'shared_aware', 'sni_proxy', 'test_ip_change', 'test_client_routes_replacement']:
@@ -29,4 +35,5 @@ def cleanup_clusters():
2935
def setup_and_teardown_packages():
3036
print('setup')
3137
yield
38+
preserve_ccm_logs_on_failure()
3239
teardown_package()

0 commit comments

Comments
 (0)