stoobly-agent 1.10.0__py3-none-any.whl → 1.10.2__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/__main__.py +10 -0
- stoobly_agent/app/api/application_http_request_handler.py +5 -2
- stoobly_agent/app/cli/ca_cert_cli.py +9 -5
- stoobly_agent/app/cli/helpers/replay_facade.py +2 -2
- stoobly_agent/app/cli/intercept_cli.py +5 -5
- stoobly_agent/app/cli/request_cli.py +2 -2
- stoobly_agent/app/cli/scaffold/app.py +14 -5
- stoobly_agent/app/cli/scaffold/app_command.py +0 -4
- stoobly_agent/app/cli/scaffold/app_config.py +49 -2
- stoobly_agent/app/cli/scaffold/app_create_command.py +145 -76
- stoobly_agent/app/cli/scaffold/constants.py +9 -4
- stoobly_agent/app/cli/scaffold/docker/constants.py +3 -1
- stoobly_agent/app/cli/scaffold/docker/service/build_decorator.py +4 -4
- stoobly_agent/app/cli/scaffold/docker/service/builder.py +31 -54
- stoobly_agent/app/cli/scaffold/docker/service/configure_gateway.py +3 -0
- stoobly_agent/app/cli/scaffold/docker/template_files.py +112 -0
- stoobly_agent/app/cli/scaffold/docker/workflow/build_decorator.py +1 -1
- stoobly_agent/app/cli/scaffold/docker/workflow/builder.py +30 -47
- stoobly_agent/app/cli/scaffold/docker/workflow/command_decorator.py +3 -2
- stoobly_agent/app/cli/scaffold/docker/workflow/detached_decorator.py +1 -1
- stoobly_agent/app/cli/scaffold/docker/workflow/dns_decorator.py +2 -3
- stoobly_agent/app/cli/scaffold/docker/workflow/local_decorator.py +1 -1
- stoobly_agent/app/cli/scaffold/docker/workflow/mock_decorator.py +1 -1
- stoobly_agent/app/cli/scaffold/docker/workflow/reverse_proxy_decorator.py +1 -1
- stoobly_agent/app/cli/scaffold/docker/workflow/run_command.py +423 -0
- stoobly_agent/app/cli/scaffold/local/__init__.py +0 -0
- stoobly_agent/app/cli/scaffold/local/service/__init__.py +0 -0
- stoobly_agent/app/cli/scaffold/local/service/builder.py +72 -0
- stoobly_agent/app/cli/scaffold/local/workflow/__init__.py +0 -0
- stoobly_agent/app/cli/scaffold/local/workflow/builder.py +35 -0
- stoobly_agent/app/cli/scaffold/local/workflow/run_command.py +339 -0
- stoobly_agent/app/cli/scaffold/service_command.py +9 -1
- stoobly_agent/app/cli/scaffold/service_config.py +9 -25
- stoobly_agent/app/cli/scaffold/service_create_command.py +18 -6
- stoobly_agent/app/cli/scaffold/service_docker_compose.py +3 -3
- stoobly_agent/app/cli/scaffold/service_workflow_validate_command.py +10 -7
- stoobly_agent/app/cli/scaffold/templates/app/.Makefile +2 -2
- stoobly_agent/app/cli/scaffold/templates/app/build/.docker-compose.base.yml +4 -4
- stoobly_agent/app/cli/scaffold/templates/app/build/mock/configure +3 -0
- stoobly_agent/app/cli/scaffold/templates/app/build/record/configure +28 -0
- stoobly_agent/app/cli/scaffold/templates/app/build/test/configure +3 -0
- stoobly_agent/app/cli/scaffold/templates/app/entrypoint/.docker-compose.base.yml +4 -4
- stoobly_agent/app/cli/scaffold/templates/app/entrypoint/mock/configure +3 -0
- stoobly_agent/app/cli/scaffold/templates/app/entrypoint/mock/run +3 -0
- stoobly_agent/app/cli/scaffold/templates/app/entrypoint/record/configure +3 -0
- stoobly_agent/app/cli/scaffold/templates/app/entrypoint/record/run +3 -0
- stoobly_agent/app/cli/scaffold/templates/app/entrypoint/test/configure +3 -0
- stoobly_agent/app/cli/scaffold/templates/app/entrypoint/test/run +3 -0
- stoobly_agent/app/cli/scaffold/templates/build/services/build/mock/.configure +5 -1
- stoobly_agent/app/cli/scaffold/templates/build/services/build/mock/.init +5 -1
- stoobly_agent/app/cli/scaffold/templates/build/services/build/mock/.run +14 -0
- stoobly_agent/app/cli/scaffold/templates/build/services/build/record/.configure +5 -1
- stoobly_agent/app/cli/scaffold/templates/build/services/build/record/.init +5 -1
- stoobly_agent/app/cli/scaffold/templates/build/services/build/record/.run +14 -0
- stoobly_agent/app/cli/scaffold/templates/build/services/build/test/.configure +5 -1
- stoobly_agent/app/cli/scaffold/templates/build/services/build/test/.init +5 -1
- stoobly_agent/app/cli/scaffold/templates/build/services/build/test/.run +14 -0
- stoobly_agent/app/cli/scaffold/templates/build/services/entrypoint/mock/.configure +5 -1
- stoobly_agent/app/cli/scaffold/templates/build/services/entrypoint/mock/.init +5 -1
- stoobly_agent/app/cli/scaffold/templates/build/services/entrypoint/mock/.run +19 -0
- stoobly_agent/app/cli/scaffold/templates/build/services/entrypoint/record/.configure +5 -1
- stoobly_agent/app/cli/scaffold/templates/build/services/entrypoint/record/.init +5 -1
- stoobly_agent/app/cli/scaffold/templates/build/services/entrypoint/record/.run +19 -0
- stoobly_agent/app/cli/scaffold/templates/build/services/entrypoint/test/.configure +5 -1
- stoobly_agent/app/cli/scaffold/templates/build/services/entrypoint/test/.init +5 -1
- stoobly_agent/app/cli/scaffold/templates/build/services/entrypoint/test/.run +19 -0
- stoobly_agent/app/cli/scaffold/templates/build/workflows/exec/scaffold/.up +0 -1
- stoobly_agent/app/cli/scaffold/templates/build/workflows/mock/.configure +5 -1
- stoobly_agent/app/cli/scaffold/templates/build/workflows/mock/.init +5 -1
- stoobly_agent/app/cli/scaffold/templates/build/workflows/mock/.run +14 -0
- stoobly_agent/app/cli/scaffold/templates/build/workflows/record/.configure +25 -1
- stoobly_agent/app/cli/scaffold/templates/build/workflows/record/.init +5 -1
- stoobly_agent/app/cli/scaffold/templates/build/workflows/record/.run +14 -0
- stoobly_agent/app/cli/scaffold/templates/build/workflows/test/.configure +5 -1
- stoobly_agent/app/cli/scaffold/templates/build/workflows/test/.init +5 -1
- stoobly_agent/app/cli/scaffold/templates/build/workflows/test/.run +14 -0
- stoobly_agent/app/cli/scaffold/templates/constants.py +35 -19
- stoobly_agent/app/cli/scaffold/templates/factory.py +34 -18
- stoobly_agent/app/cli/scaffold/templates/plugins/cypress/test/.run +21 -0
- stoobly_agent/app/cli/scaffold/templates/plugins/playwright/test/.run +21 -0
- stoobly_agent/app/cli/scaffold/templates/workflow/mock/configure +5 -0
- stoobly_agent/app/cli/scaffold/templates/workflow/mock/run +3 -0
- stoobly_agent/app/cli/scaffold/templates/workflow/record/configure +21 -0
- stoobly_agent/app/cli/scaffold/templates/workflow/record/run +3 -0
- stoobly_agent/app/cli/scaffold/templates/workflow/test/configure +5 -0
- stoobly_agent/app/cli/scaffold/templates/workflow/test/run +3 -0
- stoobly_agent/app/cli/scaffold/workflow_command.py +18 -4
- stoobly_agent/app/cli/scaffold/workflow_copy_command.py +5 -4
- stoobly_agent/app/cli/scaffold/workflow_create_command.py +31 -29
- stoobly_agent/app/cli/scaffold/workflow_run_command.py +18 -151
- stoobly_agent/app/cli/scaffold_cli.py +134 -182
- stoobly_agent/app/cli/scenario_cli.py +2 -2
- stoobly_agent/app/cli/types/test.py +2 -2
- stoobly_agent/app/cli/types/workflow_run_command.py +52 -3
- stoobly_agent/app/proxy/handle_mock_service.py +1 -1
- stoobly_agent/app/proxy/intercept_settings.py +6 -26
- stoobly_agent/app/proxy/mock/eval_fixtures_service.py +177 -27
- stoobly_agent/app/proxy/mock/types/__init__.py +22 -1
- stoobly_agent/app/proxy/record/upload_request_service.py +3 -6
- stoobly_agent/app/proxy/replay/body_parser_service.py +8 -5
- stoobly_agent/app/proxy/replay/multipart.py +15 -13
- stoobly_agent/app/proxy/replay/replay_request_service.py +2 -2
- stoobly_agent/app/proxy/run.py +3 -0
- stoobly_agent/app/proxy/test/context.py +0 -4
- stoobly_agent/app/proxy/test/context_abc.py +0 -5
- stoobly_agent/app/proxy/utils/publish_change_service.py +20 -23
- stoobly_agent/app/settings/__init__.py +10 -7
- stoobly_agent/cli.py +61 -16
- stoobly_agent/config/data_dir.py +1 -8
- stoobly_agent/public/12-es2015.618ecfd5f735b801b50f.js +1 -0
- stoobly_agent/public/12-es5.618ecfd5f735b801b50f.js +1 -0
- stoobly_agent/public/index.html +1 -1
- stoobly_agent/public/main-es2015.5a9aa16433404c3f423a.js +1 -0
- stoobly_agent/public/main-es5.5a9aa16433404c3f423a.js +1 -0
- stoobly_agent/public/runtime-es2015.77bcd31efed9e5d5d431.js +1 -0
- stoobly_agent/public/runtime-es5.77bcd31efed9e5d5d431.js +1 -0
- stoobly_agent/test/app/cli/intercept/intercept_configure_test.py +17 -6
- stoobly_agent/test/app/cli/scaffold/docker/cli_invoker.py +177 -0
- stoobly_agent/test/app/cli/scaffold/{cli_test.py → docker/cli_test.py} +4 -11
- stoobly_agent/test/app/cli/scaffold/{e2e_test.py → docker/e2e_test.py} +42 -27
- stoobly_agent/test/app/cli/scaffold/local/__init__.py +0 -0
- stoobly_agent/test/app/cli/scaffold/{cli_invoker.py → local/cli_invoker.py} +38 -32
- stoobly_agent/test/app/cli/scaffold/local/e2e_test.py +342 -0
- stoobly_agent/test/app/models/schemas/.stoobly/db/VERSION +1 -1
- stoobly_agent/test/app/proxy/mock/eval_fixtures_service_test.py +903 -2
- stoobly_agent/test/app/proxy/replay/body_parser_service_test.py +95 -3
- stoobly_agent/test/config/data_dir_test.py +2 -7
- stoobly_agent/test/test_helper.py +16 -5
- {stoobly_agent-1.10.0.dist-info → stoobly_agent-1.10.2.dist-info}/METADATA +4 -2
- {stoobly_agent-1.10.0.dist-info → stoobly_agent-1.10.2.dist-info}/RECORD +157 -129
- {stoobly_agent-1.10.0.dist-info → stoobly_agent-1.10.2.dist-info}/WHEEL +1 -1
- stoobly_agent/app/cli/helpers/shell.py +0 -26
- stoobly_agent/app/cli/scaffold/templates/app/build/mock/bin/configure +0 -3
- stoobly_agent/app/cli/scaffold/templates/app/build/record/bin/configure +0 -3
- stoobly_agent/app/cli/scaffold/templates/app/build/test/bin/configure +0 -3
- stoobly_agent/app/cli/scaffold/templates/app/entrypoint/mock/bin/configure +0 -3
- stoobly_agent/app/cli/scaffold/templates/app/entrypoint/record/bin/configure +0 -3
- stoobly_agent/app/cli/scaffold/templates/app/entrypoint/test/bin/configure +0 -3
- stoobly_agent/app/cli/scaffold/templates/workflow/mock/bin/configure +0 -13
- stoobly_agent/app/cli/scaffold/templates/workflow/record/bin/configure +0 -47
- stoobly_agent/app/cli/scaffold/templates/workflow/test/bin/configure +0 -13
- stoobly_agent/public/12-es2015.be58ed0ef449008b932e.js +0 -1
- stoobly_agent/public/12-es5.be58ed0ef449008b932e.js +0 -1
- stoobly_agent/public/main-es2015.089b46f303768fbe864f.js +0 -1
- stoobly_agent/public/main-es5.089b46f303768fbe864f.js +0 -1
- stoobly_agent/public/runtime-es2015.f8c814b38b27708e91c1.js +0 -1
- stoobly_agent/public/runtime-es5.f8c814b38b27708e91c1.js +0 -1
- /stoobly_agent/app/cli/scaffold/templates/app/build/mock/{.docker-compose.mock.yml → .docker-compose.yml} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/app/build/mock/{bin/init → init} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/app/build/record/{.docker-compose.record.yml → .docker-compose.yml} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/app/build/record/{bin/init → init} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/app/build/test/{.docker-compose.test.yml → .docker-compose.yml} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/app/build/test/{bin/init → init} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/app/entrypoint/mock/{.docker-compose.mock.yml → .docker-compose.yml} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/app/entrypoint/mock/{bin/init → init} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/app/entrypoint/record/{.docker-compose.record.yml → .docker-compose.yml} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/app/entrypoint/record/{bin/init → init} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/app/entrypoint/test/{.docker-compose.test.yml → .docker-compose.yml} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/app/entrypoint/test/{bin/init → init} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/app/gateway/mock/{.docker-compose.mock.yml → .docker-compose.yml} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/app/gateway/record/{.docker-compose.record.yml → .docker-compose.yml} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/app/gateway/test/{.docker-compose.test.yml → .docker-compose.yml} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/exec/{.docker-compose.exec.yml → .docker-compose.yml} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/mock/{.docker-compose.mock.yml → .docker-compose.yml} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/record/{.docker-compose.record.yml → .docker-compose.yml} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/plugins/cypress/test/{.docker-compose.test.yml → .docker-compose.yml} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/plugins/playwright/test/{.docker-compose.test.yml → .docker-compose.yml} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/workflow/mock/{bin/init → init} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/workflow/record/{bin/init → init} +0 -0
- /stoobly_agent/app/cli/scaffold/templates/workflow/test/{bin/init → init} +0 -0
- {stoobly_agent-1.10.0.dist-info → stoobly_agent-1.10.2.dist-info}/entry_points.txt +0 -0
- {stoobly_agent-1.10.0.dist-info → stoobly_agent-1.10.2.dist-info/licenses}/LICENSE +0 -0
@@ -0,0 +1,339 @@
|
|
1
|
+
import os
|
2
|
+
import pdb
|
3
|
+
import signal
|
4
|
+
import subprocess
|
5
|
+
import sys
|
6
|
+
|
7
|
+
from typing import Optional, List
|
8
|
+
|
9
|
+
from stoobly_agent.app.cli.scaffold.constants import PLUGIN_CYPRESS, PLUGIN_PLAYWRIGHT
|
10
|
+
from stoobly_agent.app.cli.scaffold.docker.constants import PLUGIN_CONTAINER_SERVICE_TEMPLATE
|
11
|
+
from stoobly_agent.app.cli.scaffold.templates.constants import CORE_BUILD_SERVICE_NAME, CORE_ENTRYPOINT_SERVICE_NAME, CUSTOM_CONFIGURE, CUSTOM_INIT, CUSTOM_RUN, MAINTAINED_CONFIGURE, MAINTAINED_INIT, MAINTAINED_RUN
|
12
|
+
from stoobly_agent.app.cli.scaffold.workflow_run_command import WorkflowRunCommand
|
13
|
+
from stoobly_agent.app.cli.types.workflow_run_command import WorkflowUpOptions, WorkflowDownOptions, WorkflowLogsOptions
|
14
|
+
from stoobly_agent.lib.logger import Logger
|
15
|
+
|
16
|
+
LOG_ID = 'LocalWorkflowRunCommand'
|
17
|
+
|
18
|
+
class LocalWorkflowRunCommand(WorkflowRunCommand):
|
19
|
+
"""Local workflow run command that executes stoobly-agent run directly."""
|
20
|
+
|
21
|
+
def __init__(self, app, services=None, script=None, **kwargs):
|
22
|
+
super().__init__(app, **kwargs)
|
23
|
+
|
24
|
+
self.services = services or []
|
25
|
+
self.script = script
|
26
|
+
|
27
|
+
self._log_file_path = None
|
28
|
+
self._pid_file_path = None
|
29
|
+
|
30
|
+
@property
|
31
|
+
def log_file_path(self):
|
32
|
+
"""Get the path to the PID file for this workflow."""
|
33
|
+
if not self._log_file_path:
|
34
|
+
self._log_file_path = os.path.join(self.workflow_namespace.path, f"{self.workflow_name}.log")
|
35
|
+
return self._log_file_path
|
36
|
+
|
37
|
+
@property
|
38
|
+
def pid_file_path(self):
|
39
|
+
"""Get the path to the PID file for this workflow."""
|
40
|
+
if not self._pid_file_path:
|
41
|
+
self._pid_file_path = os.path.join(self.workflow_namespace.path, f"{self.workflow_name}.pid")
|
42
|
+
return self._pid_file_path
|
43
|
+
|
44
|
+
def _write_pid(self, pid: int):
|
45
|
+
"""Write the process PID to the PID file."""
|
46
|
+
os.makedirs(os.path.dirname(self.pid_file_path), exist_ok=True)
|
47
|
+
with open(self.pid_file_path, 'w') as f:
|
48
|
+
f.write(str(pid))
|
49
|
+
|
50
|
+
def _read_pid(self) -> Optional[int]:
|
51
|
+
"""Read the process PID from the PID file."""
|
52
|
+
if not os.path.exists(self.pid_file_path):
|
53
|
+
return None
|
54
|
+
|
55
|
+
try:
|
56
|
+
with open(self.pid_file_path, 'r') as f:
|
57
|
+
return int(f.read().strip())
|
58
|
+
except (ValueError, IOError):
|
59
|
+
return None
|
60
|
+
|
61
|
+
def _kill_process(self, pid: int, signal_type=signal.SIGTERM):
|
62
|
+
"""Kill a process by PID."""
|
63
|
+
try:
|
64
|
+
os.kill(pid, signal_type)
|
65
|
+
return True
|
66
|
+
except (OSError, ProcessLookupError):
|
67
|
+
return False
|
68
|
+
|
69
|
+
def _is_process_running(self, pid: int) -> bool:
|
70
|
+
"""Check if a process is still running."""
|
71
|
+
try:
|
72
|
+
os.kill(pid, 0) # Signal 0 doesn't kill the process, just checks if it exists
|
73
|
+
return True
|
74
|
+
except (OSError, ProcessLookupError):
|
75
|
+
return False
|
76
|
+
|
77
|
+
def exec_service_script(self, service_name: str, step_script_path: str, args: List[str], cwd = None):
|
78
|
+
workflow_path = cwd or os.path.join(self.app.scaffold_namespace_path, service_name, self.workflow_name)
|
79
|
+
|
80
|
+
# Change directory to workflow path
|
81
|
+
command = [step_script_path] + args
|
82
|
+
if self.script:
|
83
|
+
command = [f"cd \"{workflow_path}\";"] + command
|
84
|
+
|
85
|
+
# Write the command to self.script_path
|
86
|
+
if self.script:
|
87
|
+
print(' '.join(command), file=self.script)
|
88
|
+
|
89
|
+
if self.dry_run:
|
90
|
+
print(' '.join(command))
|
91
|
+
else:
|
92
|
+
result = subprocess.run(['sh', '-c', ' '.join(command)], cwd=workflow_path)
|
93
|
+
if result.returncode != 0:
|
94
|
+
sys.exit(1)
|
95
|
+
|
96
|
+
def service_up(self, **options: WorkflowUpOptions):
|
97
|
+
print_service_header = options.get('print_service_header')
|
98
|
+
service_name = self.service_name
|
99
|
+
workflow_template = self.workflow_template
|
100
|
+
|
101
|
+
if print_service_header:
|
102
|
+
print_service_header(service_name)
|
103
|
+
|
104
|
+
self.write_env(**options)
|
105
|
+
|
106
|
+
# If service is build or entrypoint, use path in templates/build/services/SERVICE_NAME/.init
|
107
|
+
if service_name in [CORE_BUILD_SERVICE_NAME, CORE_ENTRYPOINT_SERVICE_NAME]:
|
108
|
+
init_script_path = os.path.join(self.service_templates_root_dir, service_name, workflow_template, MAINTAINED_INIT)
|
109
|
+
configure_script_path = os.path.join(self.service_templates_root_dir, service_name, workflow_template, MAINTAINED_CONFIGURE)
|
110
|
+
run_script_path = os.path.join(self.workflow_path, MAINTAINED_RUN)
|
111
|
+
|
112
|
+
if not os.path.exists(run_script_path):
|
113
|
+
run_script_path = os.path.join(self.service_templates_root_dir, service_name, workflow_template, MAINTAINED_RUN)
|
114
|
+
else:
|
115
|
+
# Absolute path to workflow .init script
|
116
|
+
# e.g. stoobly_agent/app/cli/scaffold/templates/build/workflows/record/.init
|
117
|
+
init_script_path = os.path.join(self.workflow_templates_build_dir, MAINTAINED_INIT)
|
118
|
+
|
119
|
+
# Absolute path to workflow .configure script
|
120
|
+
# e.g. stoobly_agent/app/cli/scaffold/templates/build/workflows/record/.configure
|
121
|
+
configure_script_path = os.path.join(self.workflow_templates_build_dir, MAINTAINED_CONFIGURE)
|
122
|
+
|
123
|
+
run_script_path = os.path.join(self.workflow_templates_build_dir, MAINTAINED_RUN)
|
124
|
+
|
125
|
+
self.exec_service_script(service_name, init_script_path, [CUSTOM_INIT])
|
126
|
+
self.exec_service_script(service_name, configure_script_path, [CUSTOM_CONFIGURE])
|
127
|
+
self.exec_service_script(service_name, run_script_path, [CUSTOM_RUN], cwd=os.getcwd())
|
128
|
+
|
129
|
+
def up(self, **options: WorkflowUpOptions):
|
130
|
+
"""Start the workflow using local stoobly-agent run."""
|
131
|
+
detached = options.get('detached', False)
|
132
|
+
|
133
|
+
commands = self.workflow_service_commands(**options)
|
134
|
+
|
135
|
+
# iterate through each service in the workflow
|
136
|
+
public_directory_paths = []
|
137
|
+
response_fixtures_paths = []
|
138
|
+
for command in commands:
|
139
|
+
url = command.service_config.url
|
140
|
+
if url:
|
141
|
+
if os.path.exists(command.public_dir_path):
|
142
|
+
public_directory_paths.append('--public-directory-path')
|
143
|
+
public_directory_paths.append(f"{command.public_dir_path}:{url}")
|
144
|
+
|
145
|
+
if os.path.exists(command.response_fixtures_path):
|
146
|
+
response_fixtures_paths.append('--response-fixtures-path')
|
147
|
+
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
|
+
|
160
|
+
for command in commands:
|
161
|
+
command.service_up(**options)
|
162
|
+
|
163
|
+
# If second from last command, run up_command i.e. right before entrypoint
|
164
|
+
if command == commands[-2]:
|
165
|
+
self.__up_command(public_directory_paths, response_fixtures_paths, **options)
|
166
|
+
|
167
|
+
def down(self, **options: WorkflowDownOptions):
|
168
|
+
"""Stop the workflow by killing the local process."""
|
169
|
+
|
170
|
+
pid = self._read_pid()
|
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
|
+
return
|
181
|
+
|
182
|
+
# Kill the process
|
183
|
+
if self.script:
|
184
|
+
self.__dry_run_down(pid, self.script)
|
185
|
+
|
186
|
+
if self.dry_run:
|
187
|
+
self.__dry_run_down(pid, sys.stdout)
|
188
|
+
else:
|
189
|
+
try:
|
190
|
+
# Try graceful shutdown first with SIGTERM
|
191
|
+
Logger.instance(LOG_ID).info(f"Sending SIGTERM to process {pid} for {self.workflow_name}")
|
192
|
+
self._kill_process(pid, signal.SIGTERM)
|
193
|
+
|
194
|
+
# Wait a bit for graceful shutdown
|
195
|
+
import time
|
196
|
+
time.sleep(2)
|
197
|
+
|
198
|
+
# Check if process still exists
|
199
|
+
if self._is_process_running(pid):
|
200
|
+
Logger.instance(LOG_ID).info(f"Process {pid} still running, sending SIGKILL")
|
201
|
+
self._kill_process(pid, signal.SIGKILL)
|
202
|
+
|
203
|
+
# Wait a bit more for SIGKILL to take effect
|
204
|
+
time.sleep(1)
|
205
|
+
|
206
|
+
# Final check
|
207
|
+
if self._is_process_running(pid):
|
208
|
+
Logger.instance(LOG_ID).warning(f"Process {pid} may still be running after SIGKILL")
|
209
|
+
else:
|
210
|
+
Logger.instance(LOG_ID).info(f"Successfully stopped process {pid} for {self.workflow_name}")
|
211
|
+
else:
|
212
|
+
Logger.instance(LOG_ID).info(f"Successfully stopped process {pid} for {self.workflow_name}")
|
213
|
+
|
214
|
+
# Clean up PID file
|
215
|
+
if os.path.exists(self.pid_file_path):
|
216
|
+
os.remove(self.pid_file_path)
|
217
|
+
|
218
|
+
except Exception as e:
|
219
|
+
Logger.instance(LOG_ID).error(f"Failed to stop {self.workflow_name}: {e}")
|
220
|
+
|
221
|
+
def logs(self, **options: WorkflowLogsOptions):
|
222
|
+
"""Show logs for the local workflow process."""
|
223
|
+
follow = options.get('follow', False)
|
224
|
+
|
225
|
+
pid = self._read_pid()
|
226
|
+
if not pid:
|
227
|
+
Logger.instance(LOG_ID).warning(f"No PID file found for {self.workflow_name}")
|
228
|
+
return
|
229
|
+
|
230
|
+
# Build log command
|
231
|
+
log_file = f"{self.log_file_path}"
|
232
|
+
if self.script:
|
233
|
+
self.__dry_run_logs(log_file, self.script, follow)
|
234
|
+
|
235
|
+
if self.dry_run:
|
236
|
+
self.__dry_run_logs(log_file, sys.stdout, follow)
|
237
|
+
else:
|
238
|
+
try:
|
239
|
+
if follow:
|
240
|
+
subprocess.run(['tail', '-f', log_file])
|
241
|
+
else:
|
242
|
+
subprocess.run(['cat', log_file])
|
243
|
+
except subprocess.CalledProcessError as e:
|
244
|
+
Logger.instance(LOG_ID).error(f"Failed to show logs for {self.workflow_name}: {e}")
|
245
|
+
|
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
|
+
def workflow_service_commands(self, **options: WorkflowUpOptions):
|
258
|
+
commands = list(map(lambda service_name: LocalWorkflowRunCommand(self.app, service_name=service_name, **options), self.services))
|
259
|
+
commands.sort(key=lambda command: command.service_config.priority)
|
260
|
+
return commands
|
261
|
+
|
262
|
+
def __dry_run_down(self, pid: int, output_file: str):
|
263
|
+
print(f"# Stop {self.workflow_name} (PID: {pid})", file=output_file)
|
264
|
+
print(f"kill {pid} || true", file=output_file)
|
265
|
+
print("sleep 1", file=output_file)
|
266
|
+
print(f"kill -0 {pid} 2>/dev/null && kill {pid} || true", file=output_file)
|
267
|
+
print(f"rm -f {self.pid_file_path}", file=output_file)
|
268
|
+
|
269
|
+
def __dry_run_logs(self, log_file: str, output_file: str, follow: bool):
|
270
|
+
print(f"# Show logs for {self.workflow_name}", file=output_file)
|
271
|
+
if follow:
|
272
|
+
print(f"tail -f {log_file}", file=output_file)
|
273
|
+
else:
|
274
|
+
print(f"cat {log_file}", file=output_file)
|
275
|
+
|
276
|
+
def __up_command(self, public_directory_paths: List[str], response_fixtures_paths: List[str], **options: WorkflowUpOptions):
|
277
|
+
# Build the stoobly-agent run command
|
278
|
+
command = ['stoobly-agent', 'run']
|
279
|
+
|
280
|
+
# Add log level if provided
|
281
|
+
if options.get('log_level'):
|
282
|
+
command.extend(['--log-level', options['log_level']])
|
283
|
+
|
284
|
+
# Use the PID file path as the detached output file
|
285
|
+
command.extend(['--detached', self.log_file_path])
|
286
|
+
|
287
|
+
command.extend(['--proxy-port', f"{self.app_config.proxy_port}"])
|
288
|
+
command.extend(['--ui-port', f"{self.app_config.ui_port}"])
|
289
|
+
|
290
|
+
if public_directory_paths:
|
291
|
+
command.extend(public_directory_paths)
|
292
|
+
|
293
|
+
if response_fixtures_paths:
|
294
|
+
command.extend(response_fixtures_paths)
|
295
|
+
|
296
|
+
# Convert command to string
|
297
|
+
command_str = ' '.join(command)
|
298
|
+
|
299
|
+
# Run in background using the main run command's --detached option
|
300
|
+
if self.script:
|
301
|
+
# Write to script for detached execution
|
302
|
+
script_lines = [
|
303
|
+
f"# Start {self.workflow_name} in background using --detached",
|
304
|
+
f"{command_str} > {self.pid_file_path}",
|
305
|
+
f"echo \"Started {self.workflow_name} with PID: $(cat {self.pid_file_path})\""
|
306
|
+
]
|
307
|
+
for line in script_lines:
|
308
|
+
print(line, file=self.script)
|
309
|
+
|
310
|
+
if self.dry_run:
|
311
|
+
print(command_str)
|
312
|
+
else:
|
313
|
+
# Execute directly
|
314
|
+
try:
|
315
|
+
# Run the command with --detached option
|
316
|
+
result = subprocess.run(
|
317
|
+
command,
|
318
|
+
capture_output=True,
|
319
|
+
text=True,
|
320
|
+
check=True
|
321
|
+
)
|
322
|
+
|
323
|
+
if result.returncode != 0:
|
324
|
+
Logger.instance(LOG_ID).error(f"Failed to start agent, run `stoobly-agent workflow logs {self.workflow_name}` to see the error")
|
325
|
+
sys.exit(1)
|
326
|
+
|
327
|
+
# The --detached option prints the PID to stdout
|
328
|
+
pid = int(result.stdout.strip())
|
329
|
+
|
330
|
+
# Write PID to file
|
331
|
+
self._write_pid(pid)
|
332
|
+
|
333
|
+
Logger.instance(LOG_ID).info(f"Started {self.workflow_name} with PID: {pid}")
|
334
|
+
except subprocess.CalledProcessError as e:
|
335
|
+
Logger.instance(LOG_ID).error(f"Failed to start {self.workflow_name}: {e}")
|
336
|
+
return None
|
337
|
+
except ValueError as e:
|
338
|
+
Logger.instance(LOG_ID).error(f"Failed to parse PID from output: {e}")
|
339
|
+
return None
|
@@ -12,10 +12,14 @@ class ServiceCommand(AppCommand):
|
|
12
12
|
super().__init__(app)
|
13
13
|
self.__service_name = kwargs.get('service_name')
|
14
14
|
|
15
|
-
|
15
|
+
if kwargs.get('service_name'):
|
16
|
+
self.__config = ServiceConfig(self.service_path, **kwargs)
|
16
17
|
|
17
18
|
@property
|
18
19
|
def service_config(self):
|
20
|
+
if not self.service_name:
|
21
|
+
raise Exception("Service name is required")
|
22
|
+
|
19
23
|
return self.__config
|
20
24
|
|
21
25
|
@service_config.setter
|
@@ -52,6 +56,10 @@ class ServiceCommand(AppCommand):
|
|
52
56
|
self.service_name,
|
53
57
|
)
|
54
58
|
|
59
|
+
@property
|
60
|
+
def service_templates_root_dir(self):
|
61
|
+
return os.path.join(self.templates_root_dir, 'build', 'services')
|
62
|
+
|
55
63
|
def config(self, _c: dict):
|
56
64
|
_config = self.app_config.read()
|
57
65
|
_config.update(self.service_config.read())
|
@@ -1,5 +1,6 @@
|
|
1
1
|
# Wraps the .config.yml file in the service folder
|
2
2
|
import hashlib
|
3
|
+
import os
|
3
4
|
import pdb
|
4
5
|
import re
|
5
6
|
|
@@ -12,7 +13,6 @@ from .constants import (
|
|
12
13
|
SERVICE_NAME_ENV,
|
13
14
|
SERVICE_PRIORITY_ENV,
|
14
15
|
SERVICE_PORT_ENV,
|
15
|
-
SERVICE_PROXY_MODE_ENV,
|
16
16
|
SERVICE_SCHEME_ENV,
|
17
17
|
SERVICE_UPSTREAM_HOSTNAME_ENV,
|
18
18
|
SERVICE_UPSTREAM_PORT_ENV,
|
@@ -30,7 +30,6 @@ class ServiceConfig(Config):
|
|
30
30
|
self.__name = None
|
31
31
|
self.__port = None
|
32
32
|
self.__priority = None
|
33
|
-
self.__proxy_mode = None
|
34
33
|
self.__scheme = None
|
35
34
|
self.__upstream_hostname = None
|
36
35
|
self.__upstream_port = None
|
@@ -58,9 +57,6 @@ class ServiceConfig(Config):
|
|
58
57
|
if 'priority' in kwargs:
|
59
58
|
self.__priority = kwargs.get('priority')
|
60
59
|
|
61
|
-
if 'proxy_mode' in kwargs:
|
62
|
-
self.__proxy_mode = kwargs.get('proxy_mode')
|
63
|
-
|
64
60
|
if 'scheme' in kwargs:
|
65
61
|
self.__scheme = kwargs.get('scheme')
|
66
62
|
|
@@ -73,6 +69,10 @@ class ServiceConfig(Config):
|
|
73
69
|
if 'upstream_scheme' in kwargs:
|
74
70
|
self.__upstream_scheme = kwargs.get('upstream_scheme')
|
75
71
|
|
72
|
+
@property
|
73
|
+
def app_config_dir_path(self):
|
74
|
+
return os.path.dirname(self.dir)
|
75
|
+
|
76
76
|
@property
|
77
77
|
def detached(self) -> bool:
|
78
78
|
return not not self.__detached
|
@@ -152,22 +152,6 @@ class ServiceConfig(Config):
|
|
152
152
|
def priority(self, v):
|
153
153
|
self.__priority = v
|
154
154
|
|
155
|
-
@property
|
156
|
-
def proxy_mode(self) -> str:
|
157
|
-
if self.__proxy_mode:
|
158
|
-
return (self.__proxy_mode or '').strip()
|
159
|
-
|
160
|
-
if not self.hostname:
|
161
|
-
return ''
|
162
|
-
|
163
|
-
return 'regular'
|
164
|
-
|
165
|
-
@proxy_mode.setter
|
166
|
-
def proxy_mode(self, v: str):
|
167
|
-
if v and v not in ['regular', 'reverse']:
|
168
|
-
v = 'reverse'
|
169
|
-
self.__proxy_mode = v
|
170
|
-
|
171
155
|
@property
|
172
156
|
def scheme(self):
|
173
157
|
if not self.__scheme and self.__port:
|
@@ -221,10 +205,13 @@ class ServiceConfig(Config):
|
|
221
205
|
|
222
206
|
@upstream_scheme.setter
|
223
207
|
def upstream_scheme(self, v):
|
224
|
-
self.
|
208
|
+
self.__upstream_scheme = v
|
225
209
|
|
226
210
|
@property
|
227
211
|
def url(self):
|
212
|
+
if not self.hostname:
|
213
|
+
return ''
|
214
|
+
|
228
215
|
_url = f"{self.scheme}://{self.hostname}"
|
229
216
|
|
230
217
|
if not self.port:
|
@@ -247,7 +234,6 @@ class ServiceConfig(Config):
|
|
247
234
|
self.name = config.get(SERVICE_NAME_ENV)
|
248
235
|
self.port = config.get(SERVICE_PORT_ENV)
|
249
236
|
self.priority = config.get(SERVICE_PRIORITY_ENV)
|
250
|
-
self.proxy_mode = config.get(SERVICE_PROXY_MODE_ENV)
|
251
237
|
self.scheme = config.get(SERVICE_SCHEME_ENV)
|
252
238
|
self.upstream_hostname = config.get(SERVICE_UPSTREAM_HOSTNAME_ENV)
|
253
239
|
self.upstream_port = config.get(SERVICE_UPSTREAM_PORT_ENV)
|
@@ -261,7 +247,6 @@ class ServiceConfig(Config):
|
|
261
247
|
'name': self.name,
|
262
248
|
'port': self.port,
|
263
249
|
'priority': self.priority,
|
264
|
-
'proxy_mode': self.proxy_mode,
|
265
250
|
'scheme': self.scheme if self.hostname else '',
|
266
251
|
'upstream_hostname': self.upstream_hostname,
|
267
252
|
'upstream_port': self.upstream_port,
|
@@ -300,6 +285,5 @@ class ServiceConfig(Config):
|
|
300
285
|
|
301
286
|
config[SERVICE_DETACHED_ENV] = bool(self.detached)
|
302
287
|
config[SERVICE_ID_ENV] = self.id
|
303
|
-
config[SERVICE_PROXY_MODE_ENV] = self.proxy_mode
|
304
288
|
|
305
289
|
super().write(config)
|
@@ -3,10 +3,12 @@ import pdb
|
|
3
3
|
import shutil
|
4
4
|
|
5
5
|
from copy import deepcopy
|
6
|
+
from typing import Union
|
6
7
|
|
7
8
|
from .app import App
|
8
|
-
from .constants import WORKFLOW_MOCK_TYPE, WORKFLOW_RECORD_TYPE, WORKFLOW_TEST_TYPE
|
9
|
-
from .
|
9
|
+
from .constants import RUN_ON_DOCKER, WORKFLOW_MOCK_TYPE, WORKFLOW_RECORD_TYPE, WORKFLOW_TEST_TYPE
|
10
|
+
from .local.service.builder import ServiceBuilder
|
11
|
+
from .docker.service.builder import DockerServiceBuilder
|
10
12
|
from .docker.workflow.decorators_factory import get_workflow_decorators
|
11
13
|
from .service_command import ServiceCommand
|
12
14
|
from .workflow_create_command import WorkflowCreateCommand
|
@@ -32,8 +34,18 @@ class ServiceCreateCommand(ServiceCommand):
|
|
32
34
|
def workflows(self):
|
33
35
|
return self.__workflows
|
34
36
|
|
37
|
+
@property
|
38
|
+
def create_docker_files(self):
|
39
|
+
"""Determine if Docker files should be created based on app config run-on setting."""
|
40
|
+
return RUN_ON_DOCKER in self.app_config.run_on
|
41
|
+
|
35
42
|
def build(self):
|
36
|
-
|
43
|
+
# Choose builder based on app run_on configuration
|
44
|
+
if RUN_ON_DOCKER in self.app_config.run_on:
|
45
|
+
service_builder = DockerServiceBuilder(self.service_config)
|
46
|
+
else:
|
47
|
+
service_builder = ServiceBuilder(self.service_config)
|
48
|
+
|
37
49
|
service_builder.with_env(list(self.env_vars))
|
38
50
|
service_decorators = []
|
39
51
|
|
@@ -67,19 +79,19 @@ class ServiceCreateCommand(ServiceCommand):
|
|
67
79
|
if os.path.exists(dest):
|
68
80
|
shutil.rmtree(dest)
|
69
81
|
|
70
|
-
def __build_with_mock_workflow(self, service_builder: ServiceBuilder, **kwargs):
|
82
|
+
def __build_with_mock_workflow(self, service_builder: Union[ServiceBuilder, DockerServiceBuilder], **kwargs):
|
71
83
|
mock_workflow = WorkflowCreateCommand(self.app, **{ **kwargs, **{ 'workflow_name': WORKFLOW_MOCK_TYPE }})
|
72
84
|
|
73
85
|
workflow_decorators = get_workflow_decorators(WORKFLOW_MOCK_TYPE, self.service_config)
|
74
86
|
mock_workflow.build(service_builder=service_builder, workflow_decorators=workflow_decorators)
|
75
87
|
|
76
|
-
def __build_with_record_workflow(self, service_builder: ServiceBuilder, **kwargs):
|
88
|
+
def __build_with_record_workflow(self, service_builder: Union[ServiceBuilder, DockerServiceBuilder], **kwargs):
|
77
89
|
record_workflow = WorkflowCreateCommand(self.app, **{ **kwargs, **{ 'workflow_name': WORKFLOW_RECORD_TYPE }})
|
78
90
|
|
79
91
|
workflow_decorators = get_workflow_decorators(WORKFLOW_RECORD_TYPE, self.service_config)
|
80
92
|
record_workflow.build(service_builder=service_builder, workflow_decorators=workflow_decorators)
|
81
93
|
|
82
|
-
def __build_with_test_workflow(self, service_builder: ServiceBuilder, **kwargs):
|
94
|
+
def __build_with_test_workflow(self, service_builder: Union[ServiceBuilder, DockerServiceBuilder], **kwargs):
|
83
95
|
mock_workflow = WorkflowCreateCommand(self.app, **{ **kwargs, **{ 'workflow_name': WORKFLOW_TEST_TYPE }})
|
84
96
|
|
85
97
|
workflow_decorators = get_workflow_decorators(WORKFLOW_TEST_TYPE, self.service_config)
|
@@ -1,6 +1,6 @@
|
|
1
|
+
from stoobly_agent.app.cli.scaffold.constants import SERVICES_NAMESPACE
|
1
2
|
from stoobly_agent.config.data_dir import DataDir
|
2
3
|
|
3
|
-
|
4
4
|
class ServiceDockerCompose():
|
5
5
|
def __init__(self, app_dir_path, target_workflow_name, service_name, hostname):
|
6
6
|
self.service_name = service_name
|
@@ -11,5 +11,5 @@ class ServiceDockerCompose():
|
|
11
11
|
self.configure_container_name = f"{target_workflow_name}-{service_name}.configure-1"
|
12
12
|
|
13
13
|
data_dir_path = DataDir.instance(app_dir_path).path
|
14
|
-
self.docker_compose_path = f"{data_dir_path}/
|
15
|
-
self.init_script_path = f"{data_dir_path}/
|
14
|
+
self.docker_compose_path = f"{data_dir_path}/{SERVICES_NAMESPACE}/{service_name}/{target_workflow_name}/docker-compose.yml"
|
15
|
+
self.init_script_path = f"{data_dir_path}/{SERVICES_NAMESPACE}/{service_name}/{target_workflow_name}/init"
|
@@ -12,11 +12,12 @@ from docker.models.containers import Container
|
|
12
12
|
|
13
13
|
from stoobly_agent.app.cli.scaffold.constants import (
|
14
14
|
PUBLIC_FOLDER_NAME,
|
15
|
+
SERVICES_NAMESPACE,
|
15
16
|
STOOBLY_DATA_DIR,
|
16
17
|
WORKFLOW_RECORD_TYPE,
|
17
18
|
WORKFLOW_TEST_TYPE,
|
18
19
|
)
|
19
|
-
from stoobly_agent.app.cli.scaffold.docker.constants import
|
20
|
+
from stoobly_agent.app.cli.scaffold.docker.constants import APP_INGRESS_NETWORK_TEMPLATE
|
20
21
|
from stoobly_agent.app.cli.scaffold.hosts_file_manager import HostsFileManager
|
21
22
|
from stoobly_agent.app.cli.scaffold.service_command import ServiceCommand
|
22
23
|
from stoobly_agent.app.cli.scaffold.service_docker_compose import ServiceDockerCompose
|
@@ -118,12 +119,14 @@ class ServiceWorkflowValidateCommand(ServiceCommand, ValidateCommand):
|
|
118
119
|
print(f"Validating hostname inside Docker network, url: {url}")
|
119
120
|
|
120
121
|
# See WorkflowRunCommand for how 'network' is generated
|
122
|
+
# Debug command=f"curl -k --max-time {timeout_seconds} {url} --verbose",
|
121
123
|
network = f"{self.workflow_name}.{self.app.network}"
|
122
124
|
timeout_seconds = 1
|
125
|
+
http_code_format = '"%{http_code}"'
|
123
126
|
output = self.docker_client.containers.run(
|
124
127
|
image='curlimages/curl:8.11.0',
|
125
|
-
command=f"curl --max-time {timeout_seconds} {url}
|
126
|
-
network=
|
128
|
+
command=f"curl -k -s -o /dev/null -w {http_code_format} --max-time {timeout_seconds} {url}",
|
129
|
+
network=APP_INGRESS_NETWORK_TEMPLATE.format(network=network),
|
127
130
|
stderr=True,
|
128
131
|
remove=True,
|
129
132
|
)
|
@@ -131,7 +134,7 @@ class ServiceWorkflowValidateCommand(ServiceCommand, ValidateCommand):
|
|
131
134
|
# Note: 499 error could also mean success because it shows the proxy
|
132
135
|
# connection is working, but we haven't recorded anything yet
|
133
136
|
logs = output.decode('ascii')
|
134
|
-
if
|
137
|
+
if logs != '200' and logs != '499':
|
135
138
|
raise ScaffoldValidateException(f"Error reaching {url} from inside Docker network")
|
136
139
|
|
137
140
|
# Check public folder exists in container
|
@@ -190,14 +193,14 @@ class ServiceWorkflowValidateCommand(ServiceCommand, ValidateCommand):
|
|
190
193
|
def validate(self) -> bool:
|
191
194
|
print(f"Validating service: {self.service_name}")
|
192
195
|
|
193
|
-
url =
|
196
|
+
url = self.service_config.url
|
194
197
|
|
195
198
|
if self.service_config.hostname and self.workflow_name not in [WORKFLOW_TEST_TYPE]:
|
196
199
|
self.validate_hostname(self.hostname, self.service_config.port)
|
197
200
|
|
198
201
|
# Test workflow won't expose services that are detached and have a hostname to the host such as assets.
|
199
202
|
# Need to test connection from inside the Docker network
|
200
|
-
if self.service_config.hostname and self.workflow_name == WORKFLOW_TEST_TYPE
|
203
|
+
if self.service_config.hostname and self.workflow_name == WORKFLOW_TEST_TYPE:
|
201
204
|
self.validate_internal_hostname(url)
|
202
205
|
|
203
206
|
self.validate_init_containers(self.service_docker_compose.init_container_name, self.service_docker_compose.configure_container_name)
|
@@ -231,7 +234,7 @@ class ServiceWorkflowValidateCommand(ServiceCommand, ValidateCommand):
|
|
231
234
|
if self.is_local():
|
232
235
|
print(f"Validating local user defined service: {self.service_name}")
|
233
236
|
# Validate docker-compose path exists
|
234
|
-
docker_compose_path = f"{self.app_dir_path}/{DATA_DIR_NAME}/
|
237
|
+
docker_compose_path = f"{self.app_dir_path}/{DATA_DIR_NAME}/{SERVICES_NAMESPACE}/{self.service_docker_compose.service_name}/{self.workflow_name}/docker-compose.yml"
|
235
238
|
destination_path = Path(docker_compose_path)
|
236
239
|
|
237
240
|
if not destination_path.exists():
|
@@ -33,10 +33,10 @@ workflow=record
|
|
33
33
|
workflow_service_options=$(shell echo $$STOOBLY_WORKFLOW_SERVICE_OPTIONS)
|
34
34
|
|
35
35
|
app_data_dir=$(app_dir)/.stoobly
|
36
|
-
app_namespace_dir=$(app_data_dir)/
|
36
|
+
app_namespace_dir=$(app_data_dir)/services
|
37
37
|
app_tmp_dir=$(app_data_dir)/tmp
|
38
38
|
dockerfile_path=$(app_namespace_dir)/.Dockerfile.context
|
39
|
-
exec_docker_compose_file_path=$(app_namespace_dir)/stoobly-ui/exec/.docker-compose.
|
39
|
+
exec_docker_compose_file_path=$(app_namespace_dir)/stoobly-ui/exec/.docker-compose.yml
|
40
40
|
workflow_namespace=$(if $(namespace),$(namespace),$(workflow))
|
41
41
|
workflow_namespace_dir=$(app_tmp_dir)/$(workflow_namespace)
|
42
42
|
workflow_script=.stoobly/tmp/$(workflow_namespace)/run.sh
|
@@ -2,18 +2,18 @@ services:
|
|
2
2
|
build.configure_base:
|
3
3
|
command:
|
4
4
|
- ${SERVICE_SCRIPTS}/${SERVICE_NAME}/${WORKFLOW_TEMPLATE}/.configure
|
5
|
-
-
|
5
|
+
- configure
|
6
6
|
extends:
|
7
7
|
file: ../.docker-compose.base.yml
|
8
8
|
service: context_base
|
9
|
-
working_dir: /home/stoobly/.stoobly/
|
9
|
+
working_dir: /home/stoobly/.stoobly/services/${SERVICE_NAME}/${WORKFLOW_NAME}
|
10
10
|
build.init_base:
|
11
11
|
command:
|
12
12
|
- ${SERVICE_SCRIPTS}/${SERVICE_NAME}/${WORKFLOW_TEMPLATE}/.init
|
13
|
-
-
|
13
|
+
- init
|
14
14
|
extends:
|
15
15
|
file: ../.docker-compose.base.yml
|
16
16
|
service: context_base
|
17
17
|
volumes:
|
18
18
|
- ${APP_DIR}:/app
|
19
|
-
working_dir: /home/stoobly/.stoobly/
|
19
|
+
working_dir: /home/stoobly/.stoobly/services/${SERVICE_NAME}/${WORKFLOW_NAME}
|
@@ -0,0 +1,28 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
|
3
|
+
# Add custom Stoobly configuration here, to learn more: https://docs.stoobly.com/core-concepts/agent/proxy-settings
|
4
|
+
|
5
|
+
echo "Configuring rewrite rules..."
|
6
|
+
stoobly-agent config rewrite set \
|
7
|
+
--method GET --method POST --method OPTIONS --method PUT --method DELETE \
|
8
|
+
--mode record \
|
9
|
+
--name authorization \
|
10
|
+
--pattern ".*" \
|
11
|
+
--type Header \
|
12
|
+
--value '' \
|
13
|
+
|
14
|
+
stoobly-agent config rewrite set \
|
15
|
+
--method GET --method POST --method OPTIONS --method PUT --method DELETE \
|
16
|
+
--mode record \
|
17
|
+
--name cookie \
|
18
|
+
--pattern ".*" \
|
19
|
+
--type Header \
|
20
|
+
--value '' \
|
21
|
+
|
22
|
+
stoobly-agent config rewrite set \
|
23
|
+
--method GET --method POST --method OPTIONS --method PUT --method DELETE \
|
24
|
+
--mode record \
|
25
|
+
--name set-cookie \
|
26
|
+
--pattern ".*" \
|
27
|
+
--type 'Response Header' \
|
28
|
+
--value '' \
|