Skip to content

Commit 5c0a493

Browse files
Merge branch 'main' into feature/risk-model-validation
2 parents aca2b3d + 8f322aa commit 5c0a493

File tree

11 files changed

+857
-146
lines changed

11 files changed

+857
-146
lines changed

.pre-commit-config.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ repos:
88
- id: detect-private-key
99
- id: forbid-submodules
1010
- repo: https://github.com/astral-sh/ruff-pre-commit
11-
rev: v0.9.10
11+
rev: v0.11.0
1212
hooks:
1313
- id: ruff
1414
args: [ --fix ]

contentctl/actions/detection_testing/DetectionTestingManager.py

Lines changed: 64 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,29 @@
1+
import concurrent.futures
2+
import datetime
3+
import signal
4+
import traceback
5+
from dataclasses import dataclass
16
from typing import List, Union
2-
from contentctl.objects.config import test, test_servers, Container, Infrastructure
7+
8+
import docker
9+
from pydantic import BaseModel
10+
311
from contentctl.actions.detection_testing.infrastructures.DetectionTestingInfrastructure import (
412
DetectionTestingInfrastructure,
13+
DetectionTestingManagerOutputDto,
514
)
615
from contentctl.actions.detection_testing.infrastructures.DetectionTestingInfrastructureContainer import (
716
DetectionTestingInfrastructureContainer,
817
)
918
from contentctl.actions.detection_testing.infrastructures.DetectionTestingInfrastructureServer import (
1019
DetectionTestingInfrastructureServer,
1120
)
12-
import signal
13-
import datetime
14-
15-
# from queue import Queue
16-
from dataclasses import dataclass
17-
18-
# import threading
19-
from contentctl.actions.detection_testing.infrastructures.DetectionTestingInfrastructure import (
20-
DetectionTestingManagerOutputDto,
21-
)
2221
from contentctl.actions.detection_testing.views.DetectionTestingView import (
2322
DetectionTestingView,
2423
)
25-
from contentctl.objects.enums import PostTestBehavior
26-
from pydantic import BaseModel
24+
from contentctl.objects.config import Container, Infrastructure, test, test_servers
2725
from contentctl.objects.detection import Detection
28-
import concurrent.futures
29-
import docker
26+
from contentctl.objects.enums import PostTestBehavior
3027

3128

3229
@dataclass(frozen=False)
@@ -63,12 +60,14 @@ def sigint_handler(signum, frame):
6360
# a newline '\r\n' which will cause that wait to stop
6461
print("*******************************")
6562
print(
66-
"If testing is paused and you are debugging a detection, you MUST hit CTRL-D at the prompt to complete shutdown."
63+
"If testing is paused and you are debugging a detection, you MUST hit CTRL-D "
64+
"at the prompt to complete shutdown."
6765
)
6866
print("*******************************")
6967

7068
signal.signal(signal.SIGINT, sigint_handler)
7169

70+
# TODO (#337): futures can be hard to maintain/debug; let's consider alternatives
7271
with (
7372
concurrent.futures.ThreadPoolExecutor(
7473
max_workers=len(self.input_dto.config.test_instances),
@@ -80,10 +79,19 @@ def sigint_handler(signum, frame):
8079
max_workers=len(self.input_dto.config.test_instances),
8180
) as view_shutdowner,
8281
):
82+
# Capture any errors for reporting at the end after all threads have been gathered
83+
errors: dict[str, list[Exception]] = {
84+
"INSTANCE SETUP ERRORS": [],
85+
"TESTING ERRORS": [],
86+
"ERRORS DURING VIEW SHUTDOWN": [],
87+
"ERRORS DURING VIEW EXECUTION": [],
88+
}
89+
8390
# Start all the views
8491
future_views = {
8592
view_runner.submit(view.setup): view for view in self.input_dto.views
8693
}
94+
8795
# Configure all the instances
8896
future_instances_setup = {
8997
instance_pool.submit(instance.setup): instance
@@ -96,7 +104,11 @@ def sigint_handler(signum, frame):
96104
future.result()
97105
except Exception as e:
98106
self.output_dto.terminate = True
99-
print(f"Error setting up container: {str(e)}")
107+
# Output the traceback if we encounter errors in verbose mode
108+
if self.input_dto.config.verbose:
109+
tb = traceback.format_exc()
110+
print(tb)
111+
errors["INSTANCE SETUP ERRORS"].append(e)
100112

101113
# Start and wait for all tests to run
102114
if not self.output_dto.terminate:
@@ -111,7 +123,11 @@ def sigint_handler(signum, frame):
111123
future.result()
112124
except Exception as e:
113125
self.output_dto.terminate = True
114-
print(f"Error running in container: {str(e)}")
126+
# Output the traceback if we encounter errors in verbose mode
127+
if self.input_dto.config.verbose:
128+
tb = traceback.format_exc()
129+
print(tb)
130+
errors["TESTING ERRORS"].append(e)
115131

116132
self.output_dto.terminate = True
117133

@@ -123,14 +139,34 @@ def sigint_handler(signum, frame):
123139
try:
124140
future.result()
125141
except Exception as e:
126-
print(f"Error stopping view: {str(e)}")
142+
# Output the traceback if we encounter errors in verbose mode
143+
if self.input_dto.config.verbose:
144+
tb = traceback.format_exc()
145+
print(tb)
146+
errors["ERRORS DURING VIEW SHUTDOWN"].append(e)
127147

128148
# Wait for original view-related threads to complete
129149
for future in concurrent.futures.as_completed(future_views):
130150
try:
131151
future.result()
132152
except Exception as e:
133-
print(f"Error running container: {str(e)}")
153+
# Output the traceback if we encounter errors in verbose mode
154+
if self.input_dto.config.verbose:
155+
tb = traceback.format_exc()
156+
print(tb)
157+
errors["ERRORS DURING VIEW EXECUTION"].append(e)
158+
159+
# Log any errors
160+
for error_type in errors:
161+
if len(errors[error_type]) > 0:
162+
print()
163+
print(f"[{error_type}]:")
164+
for error in errors[error_type]:
165+
print(f"\t{str(error)}")
166+
if isinstance(error, ExceptionGroup):
167+
for suberror in error.exceptions: # type: ignore
168+
print(f"\t\t{str(suberror)}") # type: ignore
169+
print()
134170

135171
return self.output_dto
136172

@@ -154,12 +190,15 @@ def create_DetectionTestingInfrastructureObjects(self):
154190
)
155191
if len(parts) != 2:
156192
raise Exception(
157-
f"Expected to find a name:tag in {self.input_dto.config.container_settings.full_image_path}, "
158-
f"but instead found {parts}. Note that this path MUST include the tag, which is separated by ':'"
193+
"Expected to find a name:tag in "
194+
f"{self.input_dto.config.container_settings.full_image_path}, "
195+
f"but instead found {parts}. Note that this path MUST include the "
196+
"tag, which is separated by ':'"
159197
)
160198

161199
print(
162-
f"Getting the latest version of the container image [{self.input_dto.config.container_settings.full_image_path}]...",
200+
"Getting the latest version of the container image "
201+
f"[{self.input_dto.config.container_settings.full_image_path}]...",
163202
end="",
164203
flush=True,
165204
)
@@ -168,7 +207,8 @@ def create_DetectionTestingInfrastructureObjects(self):
168207
break
169208
except Exception as e:
170209
raise Exception(
171-
f"Failed to pull docker container image [{self.input_dto.config.container_settings.full_image_path}]: {str(e)}"
210+
"Failed to pull docker container image "
211+
f"[{self.input_dto.config.container_settings.full_image_path}]: {str(e)}"
172212
)
173213

174214
already_staged_container_files = False

0 commit comments

Comments
 (0)