stoobly-agent 1.10.3__py3-none-any.whl → 1.10.4__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- stoobly_agent/__init__.py +1 -1
- stoobly_agent/app/cli/scaffold/app_create_command.py +2 -1
- stoobly_agent/app/cli/scaffold/docker/workflow/run_command.py +89 -26
- stoobly_agent/app/cli/scaffold/local/workflow/run_command.py +94 -42
- stoobly_agent/app/cli/scaffold/service_workflow_validate_command.py +6 -1
- {stoobly_agent-1.10.3.dist-info → stoobly_agent-1.10.4.dist-info}/METADATA +1 -1
- {stoobly_agent-1.10.3.dist-info → stoobly_agent-1.10.4.dist-info}/RECORD +10 -10
- {stoobly_agent-1.10.3.dist-info → stoobly_agent-1.10.4.dist-info}/WHEEL +0 -0
- {stoobly_agent-1.10.3.dist-info → stoobly_agent-1.10.4.dist-info}/entry_points.txt +0 -0
- {stoobly_agent-1.10.3.dist-info → stoobly_agent-1.10.4.dist-info}/licenses/LICENSE +0 -0
stoobly_agent/__init__.py
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
COMMAND = 'stoobly-agent'
|
2
|
-
VERSION = '1.10.
|
2
|
+
VERSION = '1.10.4'
|
@@ -12,7 +12,7 @@ from .app import App
|
|
12
12
|
from .app_command import AppCommand
|
13
13
|
from .constants import PLUGIN_CYPRESS, PLUGIN_PLAYWRIGHT, RUN_ON_DOCKER, RUN_ON_LOCAL
|
14
14
|
from .docker.template_files import plugin_docker_cypress, plugin_docker_playwright, plugin_local_cypress, plugin_local_playwright, remove_app_docker_files, remove_service_docker_files
|
15
|
-
from .templates.constants import CORE_GATEWAY_SERVICE_NAME, CORE_MOCK_UI_SERVICE_NAME, MAINTAINED_RUN
|
15
|
+
from .templates.constants import CORE_GATEWAY_SERVICE_NAME, CORE_MOCK_UI_SERVICE_NAME, CUSTOM_RUN, MAINTAINED_RUN
|
16
16
|
|
17
17
|
class AppCreateOptions(TypedDict):
|
18
18
|
docker_socket_path: str
|
@@ -85,6 +85,7 @@ class AppCreateCommand(AppCommand):
|
|
85
85
|
ignore.append(f"{CORE_MOCK_UI_SERVICE_NAME}/.*")
|
86
86
|
|
87
87
|
if RUN_ON_DOCKER in self.app_run_on:
|
88
|
+
ignore.append(f".*/{CUSTOM_RUN}")
|
88
89
|
ignore.append(f".*/{MAINTAINED_RUN}")
|
89
90
|
|
90
91
|
# Copy all app templates
|
@@ -2,12 +2,14 @@ import os
|
|
2
2
|
import pdb
|
3
3
|
import subprocess
|
4
4
|
import sys
|
5
|
+
import time
|
5
6
|
|
6
7
|
from typing import List
|
8
|
+
from types import FunctionType
|
7
9
|
|
8
10
|
from stoobly_agent.app.cli.scaffold.docker.constants import APP_EGRESS_NETWORK_TEMPLATE, APP_INGRESS_NETWORK_TEMPLATE, DOCKERFILE_CONTEXT
|
9
11
|
from stoobly_agent.app.cli.scaffold.docker.service.configure_gateway import configure_gateway
|
10
|
-
from stoobly_agent.app.cli.scaffold.templates.constants import CORE_ENTRYPOINT_SERVICE_NAME
|
12
|
+
from stoobly_agent.app.cli.scaffold.templates.constants import CORE_ENTRYPOINT_SERVICE_NAME, CORE_SERVICES
|
11
13
|
from stoobly_agent.app.cli.scaffold.workflow import Workflow
|
12
14
|
from stoobly_agent.app.cli.scaffold.workflow_run_command import WorkflowRunCommand
|
13
15
|
from stoobly_agent.app.cli.types.workflow_run_command import BuildOptions, DownOptions, UpOptions, WorkflowDownOptions, WorkflowUpOptions, WorkflowLogsOptions
|
@@ -27,10 +29,14 @@ class DockerWorkflowRunCommand(WorkflowRunCommand):
|
|
27
29
|
self.services = services or []
|
28
30
|
self.script = script
|
29
31
|
|
32
|
+
@property
|
33
|
+
def timestamp_file_extension(self):
|
34
|
+
return '.timestamp'
|
35
|
+
|
30
36
|
@property
|
31
37
|
def timestamp_file_path(self):
|
32
38
|
"""Get the path to the timestamp file for this workflow."""
|
33
|
-
return os.path.join(self.workflow_namespace.path,
|
39
|
+
return os.path.join(self.workflow_namespace.path, self.timestamp_file_name(self.workflow_name))
|
34
40
|
|
35
41
|
def exec_setup(self, containerized=False, user_id=None, verbose=False):
|
36
42
|
"""Setup Docker environment including gateway, images, and networks."""
|
@@ -47,25 +53,34 @@ class DockerWorkflowRunCommand(WorkflowRunCommand):
|
|
47
53
|
|
48
54
|
for command in init_commands:
|
49
55
|
self.exec(command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
50
|
-
|
51
|
-
def
|
52
|
-
"
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
+
|
57
|
+
def timestamp_file_name(self, workflow_name: str):
|
58
|
+
return f"{workflow_name}{self.timestamp_file_extension}"
|
59
|
+
|
60
|
+
def __create_timestamp_file(self):
|
56
61
|
# Create timestamp file to indicate workflow is starting
|
62
|
+
timestamp_file = self.timestamp_file_path
|
63
|
+
|
57
64
|
try:
|
58
65
|
with open(timestamp_file, 'w') as f:
|
59
|
-
import time
|
60
66
|
f.write(str(time.time()))
|
61
|
-
Logger.instance(LOG_ID).
|
67
|
+
Logger.instance(LOG_ID).debug(f"Created timestamp file: {timestamp_file}")
|
62
68
|
except Exception as e:
|
63
69
|
Logger.instance(LOG_ID).error(f"Failed to create timestamp file: {e}")
|
64
70
|
sys.exit(1)
|
65
71
|
|
72
|
+
return timestamp_file
|
73
|
+
|
74
|
+
def up(self, **options: WorkflowUpOptions):
|
75
|
+
"""Execute the complete Docker workflow up process."""
|
76
|
+
|
66
77
|
no_publish = options.get('no_publish', False)
|
67
78
|
print_service_header = options.get('print_service_header')
|
68
|
-
|
79
|
+
|
80
|
+
self.__iterate_active_workflows(handle_active=self.__handle_up_active)
|
81
|
+
|
82
|
+
timestamp_file = self.__create_timestamp_file()
|
83
|
+
|
69
84
|
try:
|
70
85
|
# Create individual service commands
|
71
86
|
commands: List[DockerWorkflowRunCommand] = []
|
@@ -128,11 +143,7 @@ class DockerWorkflowRunCommand(WorkflowRunCommand):
|
|
128
143
|
|
129
144
|
def down(self, **options: WorkflowDownOptions):
|
130
145
|
"""Execute the complete Docker workflow down process."""
|
131
|
-
|
132
|
-
timestamp_file = self.timestamp_file_path
|
133
|
-
if not os.path.exists(timestamp_file):
|
134
|
-
Logger.instance(LOG_ID).info(f"Workflow '{self.workflow_name}' is not running. No timestamp file found: {timestamp_file}")
|
135
|
-
return
|
146
|
+
timestamp_file = self.__find_and_verify_timestamp_file()
|
136
147
|
|
137
148
|
print_service_header = options.get('print_service_header')
|
138
149
|
|
@@ -188,7 +199,6 @@ class DockerWorkflowRunCommand(WorkflowRunCommand):
|
|
188
199
|
self.exec(remove_ingress_network_command, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
189
200
|
|
190
201
|
# Clean up timestamp file
|
191
|
-
timestamp_file = os.path.join(self.workflow_namespace.path, f"{self.workflow_name}.timestamp")
|
192
202
|
if os.path.exists(timestamp_file):
|
193
203
|
try:
|
194
204
|
os.remove(timestamp_file)
|
@@ -197,14 +207,8 @@ class DockerWorkflowRunCommand(WorkflowRunCommand):
|
|
197
207
|
|
198
208
|
def logs(self, **options: WorkflowLogsOptions):
|
199
209
|
"""Execute the complete Docker workflow logs process."""
|
200
|
-
|
201
|
-
|
202
|
-
if not os.path.exists(timestamp_file):
|
203
|
-
Logger.instance(LOG_ID).info(f"Workflow '{self.workflow_name}' is not running. No timestamp file found: {timestamp_file}")
|
204
|
-
return
|
205
|
-
|
206
|
-
from ...templates.constants import CORE_SERVICES
|
207
|
-
|
210
|
+
timestamp_file = self.__find_and_verify_timestamp_file()
|
211
|
+
|
208
212
|
print_service_header = options.get('print_service_header')
|
209
213
|
|
210
214
|
# Filter services based on options
|
@@ -420,4 +424,63 @@ class DockerWorkflowRunCommand(WorkflowRunCommand):
|
|
420
424
|
result = subprocess.run(command, shell=True, **options)
|
421
425
|
if result.returncode != 0:
|
422
426
|
Logger.instance(LOG_ID).error(command)
|
423
|
-
sys.exit(1)
|
427
|
+
sys.exit(1)
|
428
|
+
|
429
|
+
def __find_and_verify_timestamp_file(self):
|
430
|
+
# Check if workflow is running (timestamp file exists)
|
431
|
+
|
432
|
+
timestamp_file = self.timestamp_file_path
|
433
|
+
if not os.path.exists(timestamp_file):
|
434
|
+
Logger.instance(LOG_ID).error(f"Workflow '{self.workflow_name}' is not running.")
|
435
|
+
|
436
|
+
if self.workflow_name != self.workflow_namespace.namespace:
|
437
|
+
Logger.instance(LOG_ID).error(f"Run `stoobly-agent scaffold workflow up {self.workflow_name} --namespace {self.workflow_namespace.namespace}` to start it first.")
|
438
|
+
else:
|
439
|
+
Logger.instance(LOG_ID).error(f"Run `stoobly-agent scaffold workflow up {self.workflow_name}` to start it first.")
|
440
|
+
sys.exit(2)
|
441
|
+
|
442
|
+
return timestamp_file
|
443
|
+
|
444
|
+
def __handle_up_active(self, folder: str, timestamp_file_path: str):
|
445
|
+
file_name = os.path.basename(timestamp_file_path)
|
446
|
+
|
447
|
+
# In the case of a namespace, the workflow name is the name of the file without the timestamp extension
|
448
|
+
workflow_name = self.workflow_name
|
449
|
+
if folder != self.workflow_name:
|
450
|
+
workflow_name = file_name.split(self.timestamp_file_extension)[0]
|
451
|
+
|
452
|
+
Logger.instance(LOG_ID).error(f"Workflow '{workflow_name}' is running, please stop it first.")
|
453
|
+
|
454
|
+
if folder != workflow_name:
|
455
|
+
Logger.instance(LOG_ID).error(f"Run `stoobly-agent scaffold workflow down {workflow_name} --namespace {folder}` to stop it first.")
|
456
|
+
else:
|
457
|
+
Logger.instance(LOG_ID).error(f"Run `stoobly-agent scaffold workflow down {workflow_name}` to stop it first.")
|
458
|
+
|
459
|
+
sys.exit(1)
|
460
|
+
|
461
|
+
def __iterate_active_workflows(self, **kwargs):
|
462
|
+
handle_active: FunctionType = kwargs.get('handle_active')
|
463
|
+
tmp_dir_path = self.app.data_dir.tmp_dir_path
|
464
|
+
|
465
|
+
# For each folder in self.app.data_dir.tmp_dir_path
|
466
|
+
for folder in os.listdir(tmp_dir_path):
|
467
|
+
folder_path = os.path.join(tmp_dir_path, folder)
|
468
|
+
|
469
|
+
# If the folder is not a directory, skip
|
470
|
+
if not os.path.isdir(folder_path):
|
471
|
+
continue
|
472
|
+
|
473
|
+
# For each file in folder_path that ends with .timestamp
|
474
|
+
for file in os.listdir(folder_path):
|
475
|
+
if not file.endswith(self.timestamp_file_extension):
|
476
|
+
continue
|
477
|
+
|
478
|
+
# If the folder contains a .timestamp file, then another workflow is running
|
479
|
+
timestamp_file_path = os.path.join(folder_path, file)
|
480
|
+
|
481
|
+
# Allow re-running the same workflow
|
482
|
+
if timestamp_file_path == self.timestamp_file_path:
|
483
|
+
continue
|
484
|
+
|
485
|
+
if handle_active:
|
486
|
+
handle_active(folder, timestamp_file_path)
|
@@ -4,6 +4,7 @@ import signal
|
|
4
4
|
import subprocess
|
5
5
|
import sys
|
6
6
|
|
7
|
+
from types import FunctionType
|
7
8
|
from typing import Optional, List
|
8
9
|
|
9
10
|
from stoobly_agent.app.cli.scaffold.constants import PLUGIN_CYPRESS, PLUGIN_PLAYWRIGHT
|
@@ -34,26 +35,34 @@ class LocalWorkflowRunCommand(WorkflowRunCommand):
|
|
34
35
|
self._log_file_path = os.path.join(self.workflow_namespace.path, f"{self.workflow_name}.log")
|
35
36
|
return self._log_file_path
|
36
37
|
|
38
|
+
@property
|
39
|
+
def pid_file_extension(self):
|
40
|
+
return '.pid'
|
41
|
+
|
37
42
|
@property
|
38
43
|
def pid_file_path(self):
|
39
44
|
"""Get the path to the PID file for this workflow."""
|
40
45
|
if not self._pid_file_path:
|
41
|
-
self._pid_file_path = os.path.join(self.workflow_namespace.path,
|
46
|
+
self._pid_file_path = os.path.join(self.workflow_namespace.path, self.pid_file_name(self.workflow_name))
|
42
47
|
return self._pid_file_path
|
43
48
|
|
49
|
+
def pid_file_name(self, workflow_name: str):
|
50
|
+
return f"{workflow_name}{self.pid_file_extension}"
|
51
|
+
|
44
52
|
def _write_pid(self, pid: int):
|
45
53
|
"""Write the process PID to the PID file."""
|
46
54
|
os.makedirs(os.path.dirname(self.pid_file_path), exist_ok=True)
|
47
55
|
with open(self.pid_file_path, 'w') as f:
|
48
56
|
f.write(str(pid))
|
49
57
|
|
50
|
-
def _read_pid(self) -> Optional[int]:
|
58
|
+
def _read_pid(self, file_path = None) -> Optional[int]:
|
51
59
|
"""Read the process PID from the PID file."""
|
52
|
-
|
60
|
+
file_path = file_path or self.pid_file_path
|
61
|
+
if not os.path.exists(file_path):
|
53
62
|
return None
|
54
63
|
|
55
64
|
try:
|
56
|
-
with open(
|
65
|
+
with open(file_path, 'r') as f:
|
57
66
|
return int(f.read().strip())
|
58
67
|
except (ValueError, IOError):
|
59
68
|
return None
|
@@ -130,9 +139,11 @@ class LocalWorkflowRunCommand(WorkflowRunCommand):
|
|
130
139
|
"""Start the workflow using local stoobly-agent run."""
|
131
140
|
detached = options.get('detached', False)
|
132
141
|
|
133
|
-
|
142
|
+
self.__iterate_active_workflows(handle_active=self.__handle_up_active, handle_stale=self.__handle_up_stale)
|
134
143
|
|
135
144
|
# iterate through each service in the workflow
|
145
|
+
commands = self.workflow_service_commands(**options)
|
146
|
+
|
136
147
|
public_directory_paths = []
|
137
148
|
response_fixtures_paths = []
|
138
149
|
for command in commands:
|
@@ -145,18 +156,7 @@ class LocalWorkflowRunCommand(WorkflowRunCommand):
|
|
145
156
|
if os.path.exists(command.response_fixtures_path):
|
146
157
|
response_fixtures_paths.append('--response-fixtures-path')
|
147
158
|
response_fixtures_paths.append(f"{command.response_fixtures_path}:{url}")
|
148
|
-
|
149
|
-
# Check if PID file already exists
|
150
|
-
if os.path.exists(self.pid_file_path):
|
151
|
-
pid = self._read_pid()
|
152
|
-
if pid and self._is_process_running(pid):
|
153
|
-
Logger.instance(LOG_ID).error(f"Workflow {self.workflow_name} is already running with PID: {pid}")
|
154
|
-
Logger.instance(LOG_ID).error(f"Run `stoobly-agent scaffold workflow down {self.workflow_name}` to stop it first")
|
155
|
-
sys.exit(1)
|
156
|
-
else:
|
157
|
-
# PID file exists but process is not running, clean it up
|
158
|
-
os.remove(self.pid_file_path)
|
159
|
-
|
159
|
+
|
160
160
|
for command in commands:
|
161
161
|
command.service_up(**options)
|
162
162
|
|
@@ -166,17 +166,9 @@ class LocalWorkflowRunCommand(WorkflowRunCommand):
|
|
166
166
|
|
167
167
|
def down(self, **options: WorkflowDownOptions):
|
168
168
|
"""Stop the workflow by killing the local process."""
|
169
|
-
|
170
|
-
pid = self.
|
169
|
+
|
170
|
+
pid = self.__find_and_verify_workflow_pid()
|
171
171
|
if not pid:
|
172
|
-
Logger.instance(LOG_ID).warning(f"No PID file found for {self.workflow_name}")
|
173
|
-
return
|
174
|
-
|
175
|
-
if not self._is_process_running(pid):
|
176
|
-
Logger.instance(LOG_ID).info(f"Process {pid} for {self.workflow_name} is not running")
|
177
|
-
# Clean up PID file
|
178
|
-
if os.path.exists(self.pid_file_path):
|
179
|
-
os.remove(self.pid_file_path)
|
180
172
|
return
|
181
173
|
|
182
174
|
# Kill the process
|
@@ -222,10 +214,8 @@ class LocalWorkflowRunCommand(WorkflowRunCommand):
|
|
222
214
|
"""Show logs for the local workflow process."""
|
223
215
|
follow = options.get('follow', False)
|
224
216
|
|
225
|
-
|
226
|
-
|
227
|
-
Logger.instance(LOG_ID).warning(f"No PID file found for {self.workflow_name}")
|
228
|
-
return
|
217
|
+
# Find and verify the workflow PID
|
218
|
+
self.__find_and_verify_workflow_pid()
|
229
219
|
|
230
220
|
# Build log command
|
231
221
|
log_file = f"{self.log_file_path}"
|
@@ -243,17 +233,6 @@ class LocalWorkflowRunCommand(WorkflowRunCommand):
|
|
243
233
|
except subprocess.CalledProcessError as e:
|
244
234
|
Logger.instance(LOG_ID).error(f"Failed to show logs for {self.workflow_name}: {e}")
|
245
235
|
|
246
|
-
def status(self):
|
247
|
-
"""Check the status of the local workflow process."""
|
248
|
-
pid = self._read_pid()
|
249
|
-
if not pid:
|
250
|
-
return "not running"
|
251
|
-
|
252
|
-
if self._is_process_running(pid):
|
253
|
-
return f"running (PID: {pid})"
|
254
|
-
else:
|
255
|
-
return "not running (stale PID file)"
|
256
|
-
|
257
236
|
def workflow_service_commands(self, **options: WorkflowUpOptions):
|
258
237
|
commands = list(map(lambda service_name: LocalWorkflowRunCommand(self.app, service_name=service_name, **options), self.services))
|
259
238
|
commands.sort(key=lambda command: command.service_config.priority)
|
@@ -273,6 +252,79 @@ class LocalWorkflowRunCommand(WorkflowRunCommand):
|
|
273
252
|
else:
|
274
253
|
print(f"cat {log_file}", file=output_file)
|
275
254
|
|
255
|
+
def __find_and_verify_workflow_pid(self):
|
256
|
+
pid = self._read_pid()
|
257
|
+
if not pid:
|
258
|
+
Logger.instance(LOG_ID).error(f"Workflow {self.workflow_name} is not running.")
|
259
|
+
|
260
|
+
# If the workflow name does not match the workflow namespace, then recommend with --namespace option
|
261
|
+
if self.workflow_name != self.workflow_namespace.namespace:
|
262
|
+
Logger.instance(LOG_ID).error(f"Run `stoobly-agent scaffold workflow up {self.workflow_name} --namespace {self.workflow_namespace.namespace}` to start it first.")
|
263
|
+
else:
|
264
|
+
Logger.instance(LOG_ID).error(f"Run `stoobly-agent scaffold workflow up {self.workflow_name}` to start it first.")
|
265
|
+
|
266
|
+
sys.exit(1)
|
267
|
+
|
268
|
+
if not self._is_process_running(pid):
|
269
|
+
Logger.instance(LOG_ID).info(f"Process {pid} for {self.workflow_name} is not running")
|
270
|
+
# Clean up PID file
|
271
|
+
if os.path.exists(self.pid_file_path):
|
272
|
+
os.remove(self.pid_file_path)
|
273
|
+
return
|
274
|
+
|
275
|
+
return pid
|
276
|
+
|
277
|
+
def __handle_up_active(self, folder: str, pid: str, pid_file_path: str):
|
278
|
+
# Allow re-running the same workflow, bring workflow down first
|
279
|
+
if pid_file_path == self.pid_file_path and os.path.exists(pid_file_path):
|
280
|
+
self.down()
|
281
|
+
else:
|
282
|
+
file_name = os.path.basename(pid_file_path)
|
283
|
+
workflow_name = self.workflow_name
|
284
|
+
if folder != self.workflow_name:
|
285
|
+
workflow_name = file_name.split(self.pid_file_extension)[0]
|
286
|
+
|
287
|
+
Logger.instance(LOG_ID).error(f"Workflow {workflow_name} is already running with PID {pid}")
|
288
|
+
|
289
|
+
if folder != workflow_name:
|
290
|
+
Logger.instance(LOG_ID).error(f"Run `stoobly-agent scaffold workflow down {workflow_name} --namespace {folder}` to stop it first.")
|
291
|
+
else:
|
292
|
+
Logger.instance(LOG_ID).error(f"Run `stoobly-agent scaffold workflow down {workflow_name}` to stop it first.")
|
293
|
+
|
294
|
+
sys.exit(1)
|
295
|
+
|
296
|
+
def __handle_up_stale(self, folder: str, pid: str, pid_file_path: str):
|
297
|
+
# PID file exists but process is not running, clean it up
|
298
|
+
os.remove(pid_file_path)
|
299
|
+
|
300
|
+
def __iterate_active_workflows(self, **kwargs):
|
301
|
+
handle_active: FunctionType = kwargs.get('handle_active')
|
302
|
+
handle_stale: FunctionType = kwargs.get('handle_stale')
|
303
|
+
tmp_dir_path = self.app.data_dir.tmp_dir_path
|
304
|
+
|
305
|
+
# For each folder in self.app.data_dir.tmp_dir_path
|
306
|
+
for folder in os.listdir(tmp_dir_path):
|
307
|
+
folder_path = os.path.join(tmp_dir_path, folder)
|
308
|
+
|
309
|
+
# If the folder is not a directory, skip
|
310
|
+
if not os.path.isdir(folder_path):
|
311
|
+
continue
|
312
|
+
|
313
|
+
# For each file in folder_path that ends with .pid
|
314
|
+
for file in os.listdir(folder_path):
|
315
|
+
if not file.endswith(self.pid_file_extension):
|
316
|
+
continue
|
317
|
+
|
318
|
+
# If the folder contains a .pid file, then another workflow is running
|
319
|
+
pid_file_path = os.path.join(folder_path, file)
|
320
|
+
pid = self._read_pid(pid_file_path)
|
321
|
+
if pid and self._is_process_running(pid):
|
322
|
+
if handle_active:
|
323
|
+
handle_active(folder, pid, pid_file_path)
|
324
|
+
else:
|
325
|
+
if handle_stale:
|
326
|
+
handle_stale(folder, pid, pid_file_path)
|
327
|
+
|
276
328
|
def __up_command(self, public_directory_paths: List[str], response_fixtures_paths: List[str], **options: WorkflowUpOptions):
|
277
329
|
# Build the stoobly-agent run command
|
278
330
|
command = ['stoobly-agent', 'run']
|
@@ -201,7 +201,12 @@ class ServiceWorkflowValidateCommand(ServiceCommand, ValidateCommand):
|
|
201
201
|
# Test workflow won't expose services that are detached and have a hostname to the host such as assets.
|
202
202
|
# Need to test connection from inside the Docker network
|
203
203
|
if self.service_config.hostname and self.workflow_name == WORKFLOW_TEST_TYPE:
|
204
|
-
|
204
|
+
try:
|
205
|
+
self.validate_internal_hostname(url)
|
206
|
+
except ScaffoldValidateException:
|
207
|
+
time.sleep(1)
|
208
|
+
# Retry once
|
209
|
+
self.validate_internal_hostname(url)
|
205
210
|
|
206
211
|
self.validate_init_containers(self.service_docker_compose.init_container_name, self.service_docker_compose.configure_container_name)
|
207
212
|
|
@@ -1,4 +1,4 @@
|
|
1
|
-
stoobly_agent/__init__.py,sha256=
|
1
|
+
stoobly_agent/__init__.py,sha256=iF2Ku7Tb2xiieiPYtFeV3Jv11CvskFa6drNtPZgskys,45
|
2
2
|
stoobly_agent/__main__.py,sha256=tefOkFZeCFU4l3C-Y4R_lR9Yt-FETISiXGUnbh6Os54,146
|
3
3
|
stoobly_agent/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
4
4
|
stoobly_agent/app/api/__init__.py,sha256=NIgcbX7iiWrApsCITXlmhr4SYbWS0fwb01x-F3jTFdo,666
|
@@ -70,7 +70,7 @@ stoobly_agent/app/cli/scaffold/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5
|
|
70
70
|
stoobly_agent/app/cli/scaffold/app.py,sha256=7d5nHnQwyBxEwSC2Jrvbk2U3dj9OnhaHrE2zmC37oOs,3922
|
71
71
|
stoobly_agent/app/cli/scaffold/app_command.py,sha256=x--ejtVSBL0Jz8OiakBtxfI2IZFAWJWCwuSJo7TEU9Y,2386
|
72
72
|
stoobly_agent/app/cli/scaffold/app_config.py,sha256=oG06y9yuAo05ROpOhDC_LFPJ0KKNrhfvYLOgHx5nE4Y,2788
|
73
|
-
stoobly_agent/app/cli/scaffold/app_create_command.py,sha256=
|
73
|
+
stoobly_agent/app/cli/scaffold/app_create_command.py,sha256=6wGEuBkIkQkpyFv41C8CVkT2y1RCzD5OCEI9fZXku0s,8311
|
74
74
|
stoobly_agent/app/cli/scaffold/command.py,sha256=G4Zp647cuviaEXUdcl7Rbx_qQAr0Z_DS7-Y3MWDC1Qc,281
|
75
75
|
stoobly_agent/app/cli/scaffold/config.py,sha256=HZU5tkvr3dkPr4JMXZtrJlu2wxxO-134Em6jReFFcq0,688
|
76
76
|
stoobly_agent/app/cli/scaffold/constants.py,sha256=aI--kf5t9D10iBZKujMXVB5IjYML1mX8bhchT9d560k,3324
|
@@ -95,7 +95,7 @@ stoobly_agent/app/cli/scaffold/docker/workflow/dns_decorator.py,sha256=DGaSlbOvA
|
|
95
95
|
stoobly_agent/app/cli/scaffold/docker/workflow/local_decorator.py,sha256=xp1TmP8drOyl9Zhm5B1ci6NqPqRFDr2yxipmvSljgiE,717
|
96
96
|
stoobly_agent/app/cli/scaffold/docker/workflow/mock_decorator.py,sha256=QO1TbAj6QthIyHvy7itV_d9NteNcjClYaan1GX-0kLc,1201
|
97
97
|
stoobly_agent/app/cli/scaffold/docker/workflow/reverse_proxy_decorator.py,sha256=tSPnscsBZusBaSt_NlB4exrZ2MnWMRAUJgw_NaEdHiw,1199
|
98
|
-
stoobly_agent/app/cli/scaffold/docker/workflow/run_command.py,sha256=
|
98
|
+
stoobly_agent/app/cli/scaffold/docker/workflow/run_command.py,sha256=bwfywbENjxe9iHafQ4pHKFePLfJEq4XxKq6UIJ0O63M,17993
|
99
99
|
stoobly_agent/app/cli/scaffold/env.py,sha256=dT33tHoQaUxfsFCYm8kfaAv-qPVrUPmNFQmLnFQhZeQ,1107
|
100
100
|
stoobly_agent/app/cli/scaffold/hosts_file_manager.py,sha256=zNX5wh6zXQ4J2BA0YYdD7_CPqDz02b_ghXsY3oTjjB4,4999
|
101
101
|
stoobly_agent/app/cli/scaffold/local/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -103,7 +103,7 @@ stoobly_agent/app/cli/scaffold/local/service/__init__.py,sha256=47DEQpj8HBSa-_TI
|
|
103
103
|
stoobly_agent/app/cli/scaffold/local/service/builder.py,sha256=uZNPIQWo4UcLy3bcE6Wvntle6ONPpWjS5oAq3g0Punk,1852
|
104
104
|
stoobly_agent/app/cli/scaffold/local/workflow/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
105
105
|
stoobly_agent/app/cli/scaffold/local/workflow/builder.py,sha256=O8Iwyd231-ciQWkiGB5HAiFJXMDUtXIoyl0S_Jrj3lQ,806
|
106
|
-
stoobly_agent/app/cli/scaffold/local/workflow/run_command.py,sha256=
|
106
|
+
stoobly_agent/app/cli/scaffold/local/workflow/run_command.py,sha256=OxSBHM49cLK3V58OgSeeIcYPnl4lPPgGWuu64JEIvMs,15265
|
107
107
|
stoobly_agent/app/cli/scaffold/managed_services_docker_compose.py,sha256=-wLBXUi7DCWsfm5KzZzd_kdJKOTl1NT924XR7dyjbSY,574
|
108
108
|
stoobly_agent/app/cli/scaffold/service.py,sha256=74JwjTRRkk6lo-k9hre1iGztbKa9zDqjPVx3Qgpze-s,699
|
109
109
|
stoobly_agent/app/cli/scaffold/service_command.py,sha256=j-lkG5Zth_CBHa6Z9Kv3dJwxX9gylFBZMZbW691R8ZU,1480
|
@@ -114,7 +114,7 @@ stoobly_agent/app/cli/scaffold/service_dependency.py,sha256=olr_s_cfn51Pz5FlIihl
|
|
114
114
|
stoobly_agent/app/cli/scaffold/service_docker_compose.py,sha256=fVUZ-oo-bn5GVZp8JgGq7AkiQQ6-JkxwK_OMlinS9WM,915
|
115
115
|
stoobly_agent/app/cli/scaffold/service_update_command.py,sha256=oWusBKfvjt4RnK03_V3CJYWrfsCI4_LcR7W12eLXMR4,2579
|
116
116
|
stoobly_agent/app/cli/scaffold/service_workflow.py,sha256=sQ_Edy_wGHKMXpD0DmhnOWkGEKz7gSgEGNI8f7aXOdg,444
|
117
|
-
stoobly_agent/app/cli/scaffold/service_workflow_validate_command.py,sha256=
|
117
|
+
stoobly_agent/app/cli/scaffold/service_workflow_validate_command.py,sha256=DPYLBAwUsh4C8ho-4SwjBe32xuBSUpWlWgRF9JIbKRc,11768
|
118
118
|
stoobly_agent/app/cli/scaffold/templates/__init__.py,sha256=x8C_a0VoO_vUbosp4_6IC1U7Ge9NnUdVKDPpVMtMkeY,171
|
119
119
|
stoobly_agent/app/cli/scaffold/templates/app/.Dockerfile.context,sha256=9DQK-OXnRKjjKWsUSIRAio6dkR4eGxD1vizPT7Q5sp8,159
|
120
120
|
stoobly_agent/app/cli/scaffold/templates/app/.Makefile,sha256=OnY_3D9nxl3HxfUxvCgk7PY3XntEbgO9j1myx_ETK7w,9161
|
@@ -797,8 +797,8 @@ stoobly_agent/test/mock_data/scaffold/docker-compose-local-service.yml,sha256=1W
|
|
797
797
|
stoobly_agent/test/mock_data/scaffold/index.html,sha256=qJwuYajKZ4ihWZrJQ3BNObV5kf1VGnnm_vqlPJzdqLE,258
|
798
798
|
stoobly_agent/test/mock_data/uspto.yaml,sha256=6U5se7C3o-86J4m9xpOk9Npias399f5CbfWzR87WKwE,7835
|
799
799
|
stoobly_agent/test/test_helper.py,sha256=6v4AHeqYPw7vtRoxET_ubmRWPJoSmTR_DVHay3FxNbQ,1299
|
800
|
-
stoobly_agent-1.10.
|
801
|
-
stoobly_agent-1.10.
|
802
|
-
stoobly_agent-1.10.
|
803
|
-
stoobly_agent-1.10.
|
804
|
-
stoobly_agent-1.10.
|
800
|
+
stoobly_agent-1.10.4.dist-info/METADATA,sha256=lwL2ghoaa9GrpCvcjDcRzv5ADUgYBeTrqzyH965L7wI,3203
|
801
|
+
stoobly_agent-1.10.4.dist-info/WHEEL,sha256=zp0Cn7JsFoX2ATtOhtaFYIiE2rmFAD4OcMhtUki8W3U,88
|
802
|
+
stoobly_agent-1.10.4.dist-info/entry_points.txt,sha256=aq5wix5oC8MDQtmyPGU0xaFrsjJg7WH28NmXh2sc3Z8,56
|
803
|
+
stoobly_agent-1.10.4.dist-info/licenses/LICENSE,sha256=o93sj12cdoEOsTCjPaPFsw3Xq0SXs3pPcY-9reE2sEw,548
|
804
|
+
stoobly_agent-1.10.4.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|