Repository navigation
Expand file tree
/
Copy pathcontainer_manager.py
More file actions
305 lines (251 loc) · 11.9 KB
/
Copy pathcontainer_manager.py
File metadata and controls
305 lines (251 loc) · 11.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
from collections import OrderedDict
from tabnanny import check
import docker
import datetime
import docker.types
import os
import random
from modules import splunk_container
import string
from modules import test_driver
import threading
import time
import timeit
from typing import Union
WEB_PORT_STRING = "8000/tcp"
MANAGEMENT_PORT_STRING = "8089/tcp"
class ContainerManager:
def __init__(
self,
test_list: list[str],
full_docker_hub_name: str,
container_name_template: str,
num_containers: int,
apps: OrderedDict,
branch:str,
commit_hash:str,
summarization_reproduce_failure_config:dict,
files_to_copy_to_container: OrderedDict = OrderedDict(),
web_port_start: int = 8000,
management_port_start: int = 8089,
mounts: list[dict[str, str]] = [],
show_container_password:bool=True,
container_password: Union[str, None] = None,
splunkbase_username: Union[str, None] = None,
splunkbase_password: Union[str, None] = None,
reuse_image:bool = True,
interactive_failure:bool=False,
interactive:bool=False
):
#Used to determine whether or not we should wait for container threads to finish when summarizing
self.all_tests_completed = False
self.synchronization_object = test_driver.TestDriver(
test_list, num_containers, summarization_reproduce_failure_config)
self.mounts = self.create_mounts(mounts)
self.apps = apps
if container_password is None:
self.container_password = self.get_random_password()
else:
self.container_password = container_password
print("\n\n***********************")
print("Log into your [%d] Splunk Container(s) after they boot at http://127.0.0.1:[%d-%d]"%(num_containers, web_port_start, web_port_start + num_containers - 1))
print("\tSplunk App Username: [%s]"%("admin"))
print("\tSplunk App Password: ", end='')
if show_container_password:
print("[%s]"%(self.container_password))
else:
print(" --show_splunk_app_password set to False - password not printed")
print("***********************\n\n")
self.containers = self.create_containers(
full_docker_hub_name,
container_name_template,
num_containers,
web_port_start,
management_port_start,
splunkbase_username,
splunkbase_password,
files_to_copy_to_container,
reuse_image,
interactive_failure,
interactive
)
self.summary_thread = threading.Thread(target=self.queue_status_thread,args=())
#Construct the baseline from the splunk version and the apps to be installed
self.baseline = OrderedDict()
#Get a datetime and add it as the first entry in the baseline
self.start_time = datetime.datetime.now()
self.baseline['SPLUNK_VERSION'] = full_docker_hub_name
self.baseline["branch"] = branch
self.baseline["commit_hash"] = commit_hash
#Added here first to preserve ordering for OrderedDict
self.baseline['TEST_START_TIME'] = "TO BE UPDATED"
self.baseline['TEST_FINISH_TIME'] = "TO BE UPDATED"
self.baseline['TEST_DURATION'] = "TO BE UPDATED"
for key in self.apps:
self.baseline[key] = self.apps[key]
def run_test(self)->bool:
self.run_status_thread()
self.run_containers()
self.summary_thread.join()
for container in self.containers:
if self.all_tests_completed == True:
container.thread.join()
elif self.all_tests_completed == False:
#For some reason, we stopped early. So don't wait on the child threads to finish. Don't join,
#these threads may be stuck in their setup loops. Continue on.
pass
print(container.get_container_summary())
print("All containers completed testing!")
stop_time = datetime.datetime.now()
x = stop_time - self.start_time
self.baseline['TEST_START_TIME'] = str(self.start_time)
self.baseline['TEST_FINISH_TIME'] = str(stop_time)
duration = stop_time - self.start_time
self.baseline['TEST_DURATION'] = str(duration - datetime.timedelta(microseconds=duration.microseconds))
return self.synchronization_object.finish(self.baseline)
def run_containers(self) -> None:
for container_number, container in enumerate(self.containers):
#give a little time between container startup if there is more than one container.
#Never wait on the first container. This gets us to testing as fast as possible
#for the most common case (one container) and gives us some extra time and
#reduces load when we are launching more than one container
if (container_number != 0):
time.sleep(10)
container.thread.start()
def run_status_thread(self) -> None:
self.summary_thread.start()
def create_containers(
self,
full_docker_hub_name: str,
container_name_template: str,
num_containers: int,
web_port_start: int,
management_port_start: int,
splunkbase_username: Union[str, None] = None,
splunkbase_password: Union[str, None] = None,
files_to_copy_to_container: OrderedDict = OrderedDict(),
reuse_image:bool = True,
interactive_failure:bool = False,
interactive:bool = False
) -> list[splunk_container.SplunkContainer]:
#First make sure that the image exists and has been downloaded.
#Note that this is intentionally not part of the time to start
#since it can take a long time on a slow connection!
self.setup_image(reuse_image, full_docker_hub_name)
new_containers = []
for index in range(num_containers):
container_name = container_name_template % index
web_port_tuple = (WEB_PORT_STRING, web_port_start + index)
management_port_tuple = (
MANAGEMENT_PORT_STRING,
management_port_start + index,
)
new_containers.append(
splunk_container.SplunkContainer(
self.synchronization_object,
full_docker_hub_name,
container_name,
self.apps,
web_port_tuple,
management_port_tuple,
self.container_password,
files_to_copy_to_container,
self.mounts,
splunkbase_username,
splunkbase_password,
interactive_failure=interactive_failure,
interactive=interactive
)
)
return new_containers
def create_mounts(
self, mounts: list[dict[str, str]]
) -> list[docker.types.Mount]:
new_mounts = []
for mount in mounts:
new_mounts.append(self.create_mount(mount))
return new_mounts
def create_mount(self, mount: dict[str, str]) -> docker.types.Mount:
return docker.types.Mount(
source=os.path.abspath(mount["local_path"]),
target=mount["container_path"],
type=mount["type"],
read_only=mount["read_only"],
)
# taken from attack_range
def get_random_password(
self, password_min_length: int = 16, password_max_length: int = 26
) -> str:
random_source = string.ascii_letters + string.digits
password = random.choice(string.ascii_lowercase)
password += random.choice(string.ascii_uppercase)
password += random.choice(string.digits)
for i in range(random.randrange(password_min_length, password_max_length)):
password += random.choice(random_source)
password_list = list(password)
random.SystemRandom().shuffle(password_list)
password = "".join(password_list)
return password
def queue_status_thread(self, status_interval:int=60, num_steps:int=10)->None:
while True:
#This for loop lets us run the summarize print less often, but check for failure more often
for chunk in range(0, status_interval, int(status_interval/num_steps)):
if self.synchronization_object.checkContainerFailure():
print("One of the containers has shut down prematurely or the test was halted. Ensuring all containers are stopped.")
for container in self.containers:
container.stopContainer()
print("All containers stopped")
self.all_tests_completed = False
return None
time.sleep(status_interval/num_steps)
at_least_one_container_has_started_running_tests = False
for container in self.containers:
if container.test_start_time != -1:
at_least_one_container_has_started_running_tests = True
break
if self.synchronization_object.summarize(testing_currently_active = at_least_one_container_has_started_running_tests) == False:
#There are no more tests to run, so we can return from this thread
self.all_tests_completed = True
return None
def setup_image(self, reuse_images: bool, container_name: str) -> None:
client = docker.client.from_env()
if not reuse_images:
#Check to see if the image exists. If it does, then remove it. If it does not, then do nothing
docker_image = None
try:
docker_image = client.images.get(container_name)
except Exception as e:
#We don't need to do anything, the image did not exist on our system
#print("Image named [%s] did not exist, so we don't need to try and remove it."%(container_name))
pass
if docker_image != None:
#We found the image. Let's try to delete it
print("Found docker image named [%s] and you have requested that we forcefully remove it"%(container_name))
try:
client.images.remove(image=container_name, force=True, noprune=False)
print("Docker image named [%s] forcefully removed"%(container_name))
except Exception as e:
print("Error forcefully removing [%s]"%(container_name))
raise(e)
#See if the image exists. If it doesn't, then pull it from Docker Hub
try:
docker_image = client.images.get(container_name)
print("Docker image [%s] found, no need to download it."%(container_name))
except Exception as e:
#Image did not exist on the system
docker_image = None
if docker_image is None:
#We did not find the image, so pull it
try:
print("Downloading image [%s]. Please note "
"that this could take a long time depending on your "
"connection. It's around 2GB."%(container_name))
pull_start_time = timeit.default_timer()
client.images.pull(container_name,platform="linux/amd64")
pull_finish_time = timeit.default_timer()
print("Successfully pulled the docker image [%s] in %ss"%
(container_name,
datetime.timedelta(seconds=pull_finish_time - pull_start_time, microseconds=0) ))
except Exception as e:
print("There was an error trying to pull the image [%s]: [%s]"%(container_name,str(e)))
raise(e)