stoobly-agent 1.0.10__py3-none-any.whl → 1.0.12__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.py +5 -0
- stoobly_agent/app/cli/scaffold/app_create_command.py +1 -1
- stoobly_agent/app/cli/scaffold/constants.py +2 -0
- stoobly_agent/app/cli/scaffold/docker/workflow/builder.py +2 -1
- stoobly_agent/app/cli/scaffold/docker/workflow/reverse_proxy_decorator.py +4 -2
- stoobly_agent/app/cli/scaffold/service_config.py +1 -63
- stoobly_agent/app/cli/scaffold/templates/app/.Makefile +27 -15
- stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/exec/bin/.run +2 -0
- stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/exec/bin/.stop +2 -0
- stoobly_agent/app/cli/scaffold/validate_command.py +2 -1
- stoobly_agent/app/cli/scaffold/workflow_command.py +5 -1
- stoobly_agent/app/cli/scaffold/workflow_env.py +20 -0
- stoobly_agent/app/cli/scaffold/workflow_run_command.py +79 -5
- stoobly_agent/app/cli/scaffold_cli.py +8 -5
- {stoobly_agent-1.0.10.dist-info → stoobly_agent-1.0.12.dist-info}/METADATA +1 -1
- {stoobly_agent-1.0.10.dist-info → stoobly_agent-1.0.12.dist-info}/RECORD +20 -19
- {stoobly_agent-1.0.10.dist-info → stoobly_agent-1.0.12.dist-info}/LICENSE +0 -0
- {stoobly_agent-1.0.10.dist-info → stoobly_agent-1.0.12.dist-info}/WHEEL +0 -0
- {stoobly_agent-1.0.10.dist-info → stoobly_agent-1.0.12.dist-info}/entry_points.txt +0 -0
stoobly_agent/__init__.py
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
COMMAND = 'stoobly-agent'
|
2
|
-
VERSION = '1.0.
|
2
|
+
VERSION = '1.0.12'
|
@@ -13,6 +13,7 @@ class App():
|
|
13
13
|
self.__ca_certs_dir_path = kwargs.get('ca_certs_dir_path') or data_dir.mitmproxy_conf_dir_path
|
14
14
|
self.__certs_dir_path = data_dir.certs_dir_path
|
15
15
|
self.__context_dir_path = data_dir.context_dir_path
|
16
|
+
self.__data_dir = data_dir
|
16
17
|
self.__dir_path = path
|
17
18
|
self.__name = os.path.basename(self.__dir_path)
|
18
19
|
self.__network = os.path.basename(self.__dir_path)
|
@@ -46,6 +47,10 @@ class App():
|
|
46
47
|
self.__validate_path(v)
|
47
48
|
self.__context_dir_path = v
|
48
49
|
|
50
|
+
@property
|
51
|
+
def data_dir(self):
|
52
|
+
return self.__data_dir
|
53
|
+
|
49
54
|
@property
|
50
55
|
def data_dir_path(self):
|
51
56
|
return os.path.join(self.context_dir_path, DATA_DIR_NAME)
|
@@ -19,6 +19,6 @@ class AppCreateCommand(AppCommand):
|
|
19
19
|
self.app.copy_folders_and_hidden_files(self.app_templates_root_dir, dest)
|
20
20
|
|
21
21
|
with open(os.path.join(dest, '.gitignore'), 'w') as fp:
|
22
|
-
fp.write("\n".join(['**/.env'
|
22
|
+
fp.write("\n".join(['**/.env']))
|
23
23
|
|
24
24
|
self.app_config.write()
|
@@ -4,6 +4,7 @@ from stoobly_agent.config.data_dir import DATA_DIR_NAME
|
|
4
4
|
|
5
5
|
|
6
6
|
APP_NETWORK_ENV = 'APP_NETWORK'
|
7
|
+
BIN_FOLDER_NAME = 'bin'
|
7
8
|
CA_CERTS_DIR_ENV = 'CA_CERTS_DIR'
|
8
9
|
CERTS_DIR_ENV = 'CERTS_DIR'
|
9
10
|
COMPOSE_TEMPLATE = '.docker-compose.{workflow}.yml'
|
@@ -12,6 +13,7 @@ CONTEXT_DIR_ENV = 'CONTEXT_DIR'
|
|
12
13
|
DOCKER_NAMESPACE = 'docker'
|
13
14
|
ENV_FILE = '.env'
|
14
15
|
FIXTURES_FOLDER_NAME = 'fixtures'
|
16
|
+
NAMESERVERS_FILE = '.nameservers'
|
15
17
|
SERVICE_DETACHED = '${SERVICE_DETACHED}'
|
16
18
|
SERVICE_DETACHED_ENV = 'SERVICE_DETACHED'
|
17
19
|
SERVICE_DOCKER_COMPOSE_PATH = '${SERVICE_DOCKER_COMPOSE_PATH}'
|
@@ -7,9 +7,10 @@ from ...constants import (
|
|
7
7
|
COMPOSE_TEMPLATE, SERVICE_HOSTNAME, SERVICE_HOSTNAME_ENV, SERVICE_NAME_ENV, SERVICE_PORT, SERVICE_PORT_ENV, SERVICE_SCHEME,
|
8
8
|
SERVICE_SCHEME_ENV, STOOBLY_HOME_DIR, WORKFLOW_NAME_ENV
|
9
9
|
)
|
10
|
+
from ...templates.constants import SERVICE_HOSTNAME_BUILD_ARG
|
11
|
+
from ...workflow_env import WorkflowEnv
|
10
12
|
from ..builder import Builder
|
11
13
|
from ..service.builder import ServiceBuilder
|
12
|
-
from ...templates.constants import SERVICE_HOSTNAME_BUILD_ARG
|
13
14
|
|
14
15
|
class WorkflowBuilder(Builder):
|
15
16
|
|
@@ -2,7 +2,9 @@ import pdb
|
|
2
2
|
|
3
3
|
from urllib.parse import urlparse
|
4
4
|
|
5
|
-
from
|
5
|
+
from stoobly_agent.config.data_dir import DataDir
|
6
|
+
|
7
|
+
from ...constants import SERVICE_DNS, SERVICE_DNS_ENV, SERVICE_HOSTNAME, SERVICE_PORT
|
6
8
|
from .builder import WorkflowBuilder
|
7
9
|
|
8
10
|
class ReverseProxyDecorator():
|
@@ -57,7 +59,7 @@ class ReverseProxyDecorator():
|
|
57
59
|
# If we are reverse proxying to potentially an external host,
|
58
60
|
# Docker's embedded DNS will use the host's /etc/host file as part of the resolution process
|
59
61
|
# This can lead the hostname to resolve to localhost instead of the service's actual IP address
|
60
|
-
if
|
62
|
+
if not config.detached:
|
61
63
|
service['dns'] = SERVICE_DNS
|
62
64
|
|
63
65
|
services[proxy_name] = service
|
@@ -1,16 +1,11 @@
|
|
1
1
|
# Wraps the .config.yml file in the service folder
|
2
|
-
|
3
|
-
import dns.resolver
|
4
2
|
import pdb
|
5
|
-
import subprocess
|
6
|
-
import re
|
7
3
|
|
8
4
|
from .config import Config
|
9
5
|
from .constants import (
|
10
6
|
SERVICE_DETACHED_ENV,
|
11
7
|
SERVICE_DOCKER_COMPOSE_PATH_ENV,
|
12
8
|
SERVICE_HOSTNAME_ENV,
|
13
|
-
SERVICE_DNS_ENV,
|
14
9
|
SERVICE_PRIORITY_ENV,
|
15
10
|
SERVICE_PORT_ENV,
|
16
11
|
SERVICE_PROXY_MODE_ENV,
|
@@ -25,7 +20,6 @@ class ServiceConfig(Config):
|
|
25
20
|
self.__detached = None
|
26
21
|
self.__docker_compose_path = None
|
27
22
|
self.__hostname = None
|
28
|
-
self.__dns = None
|
29
23
|
self.__port = None
|
30
24
|
self.__priority = None
|
31
25
|
self.__proxy_mode = None
|
@@ -75,28 +69,6 @@ class ServiceConfig(Config):
|
|
75
69
|
def hostname(self, v):
|
76
70
|
self.__hostname = v
|
77
71
|
|
78
|
-
@property
|
79
|
-
def dns(self):
|
80
|
-
# If hostname is set then the service is external and we will need to configure the container's DNS.
|
81
|
-
# If we don't configure the container's DNS, then Docker's embedded DNS will potentially
|
82
|
-
# use configuration from the host's /etc/hosts file. The user may have configured their
|
83
|
-
# /etc/hosts file to resolve requests to localhost
|
84
|
-
#
|
85
|
-
# See:
|
86
|
-
# https://forums.docker.com/t/docker-127-0-0-11-resolver-should-use-host-etc-hosts-file/55157
|
87
|
-
# https://docs.docker.com/network/#dns-services
|
88
|
-
#
|
89
|
-
# TODO: ideally we want to know if the service is built locally, if so, then no need to set DNS
|
90
|
-
# since Docker's embedded DNS will resolve to it
|
91
|
-
if self.hostname and not self.__dns:
|
92
|
-
nameservers = self.__find_dns()
|
93
|
-
self.__dns = nameservers[0] if nameservers else None
|
94
|
-
return self.__dns
|
95
|
-
|
96
|
-
@dns.setter
|
97
|
-
def dns(self, v):
|
98
|
-
self.__dns = v
|
99
|
-
|
100
72
|
@property
|
101
73
|
def port(self):
|
102
74
|
if not self.__port:
|
@@ -147,9 +119,6 @@ class ServiceConfig(Config):
|
|
147
119
|
def load(self, config = None):
|
148
120
|
config = config or self.read()
|
149
121
|
|
150
|
-
# Do not load dns from config, have it dynamically determined
|
151
|
-
#self.dns = config.get(SERVICE_DNS_ENV)
|
152
|
-
|
153
122
|
self.detached = config.get(SERVICE_DETACHED_ENV)
|
154
123
|
self.docker_compose_path = config.get(SERVICE_DOCKER_COMPOSE_PATH_ENV)
|
155
124
|
self.hostname = config.get(SERVICE_HOSTNAME_ENV)
|
@@ -167,9 +136,6 @@ class ServiceConfig(Config):
|
|
167
136
|
if self.hostname:
|
168
137
|
config[SERVICE_HOSTNAME_ENV] = self.hostname
|
169
138
|
|
170
|
-
if self.dns:
|
171
|
-
config[SERVICE_DNS_ENV] = self.dns
|
172
|
-
|
173
139
|
if self.port:
|
174
140
|
config[SERVICE_PORT_ENV] = self.port
|
175
141
|
|
@@ -183,32 +149,4 @@ class ServiceConfig(Config):
|
|
183
149
|
|
184
150
|
config[SERVICE_PROXY_MODE_ENV] = self.proxy_mode
|
185
151
|
|
186
|
-
super().write(config)
|
187
|
-
|
188
|
-
def __find_dns(self):
|
189
|
-
dns_resolver = dns.resolver.Resolver()
|
190
|
-
nameservers = dns_resolver.nameservers
|
191
|
-
|
192
|
-
# If systemd-resolved is not used
|
193
|
-
if nameservers != ['127.0.0.53']:
|
194
|
-
return nameservers
|
195
|
-
|
196
|
-
# Run the `resolvectl status` command and capture its output
|
197
|
-
result = subprocess.run(['resolvectl', 'status'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
198
|
-
|
199
|
-
# Check if the command ran successfully
|
200
|
-
if result.returncode != 0:
|
201
|
-
return []
|
202
|
-
|
203
|
-
# Extract the DNS servers using a regular expression
|
204
|
-
#dns_servers = re.findall(r'DNS Servers: ([\d.]+(?:, [\d.]+)*)', result.stdout)
|
205
|
-
pattern = re.compile('DNS Servers:(.*?)DNS Domain', re.DOTALL)
|
206
|
-
match = re.findall(pattern, result.stdout)
|
207
|
-
|
208
|
-
if not match:
|
209
|
-
return []
|
210
|
-
|
211
|
-
# Split the DNS servers string into a list
|
212
|
-
dns_servers = match[0].strip().split("\n")
|
213
|
-
return list(map(lambda dns_server: dns_server.strip(), dns_servers))
|
214
|
-
|
152
|
+
super().write(config)
|
@@ -21,6 +21,7 @@ workflow_run_options=$${STOOBLY_WORKFLOW_RUN_OPTIONS:+$$STOOBLY_WORKFLOW_RUN_OPT
|
|
21
21
|
|
22
22
|
app_data_dir=$(app_dir)/.stoobly
|
23
23
|
data_dir=$(context_dir)/.stoobly
|
24
|
+
app_tmp_dir=$(app_data_dir)/tmp
|
24
25
|
|
25
26
|
# Commands
|
26
27
|
docker_compose_command=docker compose
|
@@ -41,71 +42,82 @@ workflow_run_env=export APP_DIR="$(app_dir)" && export CERTS_DIR="$(certs_dir)"
|
|
41
42
|
workflow_run=$(workflow_run_env) && $(source_env) && bash "$(workflow_run_script)"
|
42
43
|
|
43
44
|
certs:
|
44
|
-
export EXEC_COMMAND=bin/.mkcert && \
|
45
|
+
@export EXEC_COMMAND=bin/.mkcert && \
|
45
46
|
$(stoobly_exec)
|
47
|
+
nameservers:
|
48
|
+
@if [ -f /etc/resolv.conf ]; then \
|
49
|
+
nameserver=$$(grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' /etc/resolv.conf | tr -d '\n') && \
|
50
|
+
if [ "$$nameserver" = "127.0.0.53" ]; then \
|
51
|
+
echo "Nameserver is 127.0.0.53. Checking resolvectl status..."; \
|
52
|
+
nameserver=$$(resolvectl status | sed -n '/DNS Servers:/s/.*DNS Servers:\s*\([^ ]*\).*/\1/p' | head -n 1); \
|
53
|
+
fi; \
|
54
|
+
echo "$$nameserver" > $(app_tmp_dir)/.nameservers; \
|
55
|
+
else \
|
56
|
+
echo "/etc/resolv.conf not found."; \
|
57
|
+
fi
|
46
58
|
intercept/disable:
|
47
|
-
export EXEC_COMMAND=bin/.disable && \
|
59
|
+
@export EXEC_COMMAND=bin/.disable && \
|
48
60
|
$(stoobly_exec)
|
49
61
|
intercept/enable:
|
50
|
-
export EXEC_COMMAND=bin/.enable && \
|
62
|
+
@export EXEC_COMMAND=bin/.enable && \
|
51
63
|
export EXEC_ARGS=$(scenario_key) && \
|
52
64
|
$(stoobly_exec)
|
53
|
-
mock:
|
54
|
-
export EXEC_COMMAND=bin/.run && \
|
65
|
+
mock: nameservers
|
66
|
+
@export EXEC_COMMAND=bin/.run && \
|
55
67
|
export EXEC_OPTIONS="$(workflow_run_options)$(options)" && \
|
56
68
|
export EXEC_ARGS="mock" && \
|
57
69
|
$(stoobly_exec_run) && \
|
58
70
|
$(workflow_run)
|
59
71
|
mock/stop:
|
60
|
-
export EXEC_COMMAND=bin/.stop && \
|
72
|
+
@export EXEC_COMMAND=bin/.stop && \
|
61
73
|
export EXEC_OPTIONS="$(options)" && \
|
62
74
|
export EXEC_ARGS="mock" && \
|
63
75
|
$(stoobly_exec_run) && \
|
64
76
|
$(workflow_run)
|
65
|
-
record:
|
66
|
-
export EXEC_COMMAND=bin/.run && \
|
77
|
+
record: nameservers
|
78
|
+
@export EXEC_COMMAND=bin/.run && \
|
67
79
|
export EXEC_OPTIONS="$(workflow_run_options)$(options)" && \
|
68
80
|
export EXEC_ARGS="record" && \
|
69
81
|
$(stoobly_exec_run) && \
|
70
82
|
$(workflow_run)
|
71
83
|
record/stop:
|
72
|
-
export EXEC_COMMAND=bin/.stop && \
|
84
|
+
@export EXEC_COMMAND=bin/.stop && \
|
73
85
|
export EXEC_OPTIONS="$(options)" && \
|
74
86
|
export EXEC_ARGS="record" && \
|
75
87
|
$(stoobly_exec_run) && \
|
76
88
|
$(workflow_run)
|
77
89
|
scenario/create:
|
78
90
|
# Create a scenario
|
79
|
-
export EXEC_COMMAND=bin/.create && \
|
91
|
+
@export EXEC_COMMAND=bin/.create && \
|
80
92
|
export EXEC_OPTIONS="$(options)" && \
|
81
93
|
export EXEC_ARGS="$(name)" && \
|
82
94
|
$(stoobly_exec)
|
83
95
|
scenario/delete:
|
84
96
|
# Delete a scenario
|
85
|
-
export EXEC_COMMAND=bin/.delete && \
|
97
|
+
@export EXEC_COMMAND=bin/.delete && \
|
86
98
|
export EXEC_OPTIONS="$(options)" && \
|
87
99
|
export EXEC_ARGS="$(key)" && \
|
88
100
|
$(stoobly_exec)
|
89
101
|
scenario/reset:
|
90
102
|
# Resets a scenario to its last snapshot
|
91
|
-
export EXEC_COMMAND=bin/.reset && \:
|
103
|
+
@export EXEC_COMMAND=bin/.reset && \:
|
92
104
|
export EXEC_OPTIONS="$(options)" && \
|
93
105
|
export EXEC_ARGS="$(key)" && \
|
94
106
|
$(stoobly_exec)
|
95
107
|
scenario/snapshot:
|
96
108
|
# Create committable files for a scenario
|
97
|
-
export EXEC_COMMAND=bin/.snapshot && \
|
109
|
+
@export EXEC_COMMAND=bin/.snapshot && \
|
98
110
|
export EXEC_OPTIONS="$(options)" && \
|
99
111
|
export EXEC_ARGS="$(key)" && \
|
100
112
|
$(stoobly_exec)
|
101
113
|
test:
|
102
|
-
export EXEC_COMMAND=bin/.run && \
|
114
|
+
@export EXEC_COMMAND=bin/.run && \
|
103
115
|
export EXEC_OPTIONS="$(workflow_run_options)$(options)" && \
|
104
116
|
export EXEC_ARGS="test" && \
|
105
117
|
$(stoobly_exec_run) && \
|
106
118
|
$(workflow_run)
|
107
119
|
test/stop:
|
108
|
-
export EXEC_COMMAND=bin/.stop && \
|
120
|
+
@export EXEC_COMMAND=bin/.stop && \
|
109
121
|
export EXEC_OPTIONS="$(options)" && \
|
110
122
|
export EXEC_ARGS="test" && \
|
111
123
|
$(stoobly_exec_run) && \
|
@@ -1,4 +1,5 @@
|
|
1
1
|
import pdb
|
2
|
+
import re
|
2
3
|
from time import sleep
|
3
4
|
|
4
5
|
import docker
|
@@ -31,7 +32,7 @@ class ValidateCommand():
|
|
31
32
|
|
32
33
|
init_container = self.__get_container(init_container_name)
|
33
34
|
logs = init_container.logs()
|
34
|
-
if logs:
|
35
|
+
if logs and re.search('error', str(logs), re.IGNORECASE):
|
35
36
|
raise ScaffoldValidateException(f"Error logs potentially detected in: {init_container_name}")
|
36
37
|
if init_container.status != 'exited' or init_container.attrs['State']['ExitCode'] != 0:
|
37
38
|
raise ScaffoldValidateException(f"init container has not exited like expected: {init_container_name}")
|
@@ -6,7 +6,7 @@ from stoobly_agent.lib.logger import Logger
|
|
6
6
|
|
7
7
|
from .app import App
|
8
8
|
from .config import Config
|
9
|
-
from .constants import COMPOSE_TEMPLATE, CONFIG_FILE, ENV_FILE, FIXTURES_FOLDER_NAME
|
9
|
+
from .constants import BIN_FOLDER_NAME, COMPOSE_TEMPLATE, CONFIG_FILE, ENV_FILE, FIXTURES_FOLDER_NAME
|
10
10
|
from .docker.constants import DOCKER_COMPOSE_CUSTOM
|
11
11
|
from .service_command import ServiceCommand
|
12
12
|
|
@@ -19,6 +19,10 @@ class WorkflowCommand(ServiceCommand):
|
|
19
19
|
|
20
20
|
self.__workflow_name = kwargs['workflow_name']
|
21
21
|
|
22
|
+
@property
|
23
|
+
def bin_dir_path(self):
|
24
|
+
return os.path.join(self.workflow_path, BIN_FOLDER_NAME)
|
25
|
+
|
22
26
|
@property
|
23
27
|
def compose_path(self):
|
24
28
|
return os.path.join(
|
@@ -0,0 +1,20 @@
|
|
1
|
+
from .config import Config
|
2
|
+
from .constants import ENV_FILE
|
3
|
+
from .env import Env
|
4
|
+
|
5
|
+
class WorkflowEnv(Config):
|
6
|
+
|
7
|
+
def __init__(self, dir: str):
|
8
|
+
super().__init__(dir, ENV_FILE)
|
9
|
+
|
10
|
+
self.__env_vars = {}
|
11
|
+
|
12
|
+
def get(self, key):
|
13
|
+
return self.__env_vars.get(key)
|
14
|
+
|
15
|
+
def load(self):
|
16
|
+
self.__env_vars = self.read()
|
17
|
+
|
18
|
+
def write(self, env_vars: dict):
|
19
|
+
Env(self.path).write(env_vars)
|
20
|
+
self.__env_vars = env_vars
|
@@ -1,5 +1,8 @@
|
|
1
|
+
import dns.resolver
|
1
2
|
import os
|
2
3
|
import pdb
|
4
|
+
import subprocess
|
5
|
+
import re
|
3
6
|
|
4
7
|
from typing import TypedDict
|
5
8
|
|
@@ -7,14 +10,18 @@ from stoobly_agent.config.data_dir import DataDir
|
|
7
10
|
from stoobly_agent.lib.logger import Logger
|
8
11
|
|
9
12
|
from .app import App
|
10
|
-
from .constants import
|
11
|
-
|
13
|
+
from .constants import (
|
14
|
+
APP_NETWORK_ENV, CA_CERTS_DIR_ENV, CERTS_DIR_ENV, CONTEXT_DIR_ENV, NAMESERVERS_FILE,
|
15
|
+
SERVICE_DNS_ENV, SERVICE_NAME_ENV, USER_ID_ENV, WORKFLOW_NAME_ENV
|
16
|
+
)
|
12
17
|
from .workflow_command import WorkflowCommand
|
18
|
+
from .workflow_env import WorkflowEnv
|
13
19
|
|
14
20
|
LOG_ID = 'WorkflowRunCommand'
|
15
21
|
|
16
22
|
class UpOptions(TypedDict):
|
17
23
|
attached: bool
|
24
|
+
exit_code_from: str
|
18
25
|
namespace: str
|
19
26
|
|
20
27
|
class WorkflowRunCommand(WorkflowCommand):
|
@@ -62,6 +69,20 @@ class WorkflowRunCommand(WorkflowCommand):
|
|
62
69
|
def extra_compose_path(self):
|
63
70
|
return self.__extra_compose_path
|
64
71
|
|
72
|
+
@property
|
73
|
+
def nameservers(self):
|
74
|
+
path = self.nameservers_path
|
75
|
+
if not os.path.exists(path):
|
76
|
+
return []
|
77
|
+
|
78
|
+
with open(path, 'r') as fp:
|
79
|
+
nameservers = fp.read()
|
80
|
+
return nameservers.split("\n")
|
81
|
+
|
82
|
+
@property
|
83
|
+
def nameservers_path(self):
|
84
|
+
return os.path.join(self.app.data_dir.tmp_dir_path, NAMESERVERS_FILE)
|
85
|
+
|
65
86
|
@property
|
66
87
|
def network(self):
|
67
88
|
return self.__network
|
@@ -108,6 +129,9 @@ class WorkflowRunCommand(WorkflowCommand):
|
|
108
129
|
|
109
130
|
if not options.get('attached'):
|
110
131
|
command.append('-d')
|
132
|
+
else:
|
133
|
+
if options.get('exit_code_from'):
|
134
|
+
command.append(f"--exit-code-from {options['exit_code_from']}")
|
111
135
|
|
112
136
|
command.append('--build')
|
113
137
|
command.append('--pull always')
|
@@ -137,6 +161,27 @@ class WorkflowRunCommand(WorkflowCommand):
|
|
137
161
|
|
138
162
|
return ' '.join(command)
|
139
163
|
|
164
|
+
def write_nameservers(self):
|
165
|
+
# If hostname is set then the service is external and we will need to configure the container's DNS.
|
166
|
+
# If we don't configure the container's DNS, then Docker's embedded DNS will potentially
|
167
|
+
# use configuration from the host's /etc/hosts file. The user may have configured their
|
168
|
+
# /etc/hosts file to resolve requests to localhost
|
169
|
+
#
|
170
|
+
# See:
|
171
|
+
# https://forums.docker.com/t/docker-127-0-0-11-resolver-should-use-host-etc-hosts-file/55157
|
172
|
+
# https://docs.docker.com/network/#dns-services
|
173
|
+
#
|
174
|
+
# TODO: ideally we want to know if the service is built locally, if so, then no need to set DNS
|
175
|
+
# since Docker's embedded DNS will resolve to it
|
176
|
+
dns_resolver = dns.resolver.Resolver()
|
177
|
+
|
178
|
+
with open(self.nameservers_path, 'w') as fp:
|
179
|
+
nameservers = self.__find_nameservers(dns_resolver)
|
180
|
+
ipv4_pattern = re.compile(r'^((25[0-5]|2[0-4][0-9]|1?[0-9][0-9]?)\.){3}(25[0-5]|2[0-4][0-9]|1?[0-9][0-9]?)$')
|
181
|
+
nameservers = [ip for ip in nameservers if ipv4_pattern.match(ip)]
|
182
|
+
if nameservers:
|
183
|
+
fp.write("\n".join(nameservers))
|
184
|
+
|
140
185
|
def write_env(self):
|
141
186
|
_config = {}
|
142
187
|
_config[CA_CERTS_DIR_ENV] = self.ca_certs_dir_path
|
@@ -149,7 +194,36 @@ class WorkflowRunCommand(WorkflowCommand):
|
|
149
194
|
if self.network:
|
150
195
|
_config[APP_NETWORK_ENV] = self.network
|
151
196
|
|
152
|
-
|
153
|
-
|
154
|
-
|
197
|
+
nameservers = self.nameservers
|
198
|
+
if nameservers:
|
199
|
+
_config[SERVICE_DNS_ENV] = nameservers[0]
|
155
200
|
|
201
|
+
env_vars = self.config(_config)
|
202
|
+
WorkflowEnv(self.workflow_path).write(env_vars)
|
203
|
+
return env_vars
|
204
|
+
|
205
|
+
def __find_nameservers(self, dns_resolver: dns.resolver.Resolver):
|
206
|
+
nameservers = dns_resolver.nameservers
|
207
|
+
|
208
|
+
# If systemd-resolved is not used
|
209
|
+
if nameservers != ['127.0.0.53']:
|
210
|
+
return nameservers
|
211
|
+
|
212
|
+
# Run the `resolvectl status` command and capture its output
|
213
|
+
result = subprocess.run(['resolvectl', 'status'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
214
|
+
|
215
|
+
# Check if the command ran successfully
|
216
|
+
if result.returncode != 0:
|
217
|
+
return []
|
218
|
+
|
219
|
+
# Extract the DNS servers using a regular expression
|
220
|
+
#dns_servers = re.findall(r'DNS Servers: ([\d.]+(?:, [\d.]+)*)', result.stdout)
|
221
|
+
pattern = re.compile('DNS Servers:(.*?)DNS Domain', re.DOTALL)
|
222
|
+
match = re.findall(pattern, result.stdout)
|
223
|
+
|
224
|
+
if not match:
|
225
|
+
return []
|
226
|
+
|
227
|
+
# Split the DNS servers string into a list
|
228
|
+
dns_servers = match[0].strip().split("\n")
|
229
|
+
return list(map(lambda dns_server: dns_server.strip(), dns_servers))
|
@@ -319,6 +319,7 @@ def logs(**kwargs):
|
|
319
319
|
@click.option('--detached', is_flag=True, help='If set, will not run the highest priority service in the foreground.')
|
320
320
|
@click.option('--filter', multiple=True, type=click.Choice([WORKFLOW_CUSTOM_FILTER]), help='Select which service groups to run. Defaults to all.')
|
321
321
|
@click.option('--dry-run', default=False, is_flag=True, help='If set, prints commands.')
|
322
|
+
@click.option('--exit-code-from', help='Name of container service to obtain exit code from.')
|
322
323
|
@click.option('--extra-compose-path', help='Path to extra compose configuration files.')
|
323
324
|
@click.option('--log-level', default=INFO, type=click.Choice([DEBUG, INFO, WARNING, ERROR]), help='''
|
324
325
|
Log levels can be "debug", "info", "warning", or "error"
|
@@ -367,8 +368,12 @@ def run(**kwargs):
|
|
367
368
|
|
368
369
|
# Before services can be started, their network needs to be created
|
369
370
|
if len(commands) > 0:
|
370
|
-
|
371
|
+
command = commands[0]
|
372
|
+
create_network_command = command.create_network()
|
373
|
+
|
371
374
|
if not kwargs['dry_run']:
|
375
|
+
command.write_nameservers()
|
376
|
+
|
372
377
|
exec_stream(create_network_command)
|
373
378
|
else:
|
374
379
|
print(create_network_command)
|
@@ -380,7 +385,7 @@ def run(**kwargs):
|
|
380
385
|
# By default, the entrypoint service should be last
|
381
386
|
# However, this can change if the user has configured a service's priority to be higher
|
382
387
|
attached = not kwargs['detached'] and index == len(commands) - 1
|
383
|
-
exec_command = command.up(attached=attached, namespace=kwargs['namespace'])
|
388
|
+
exec_command = command.up(attached=attached, exit_code_from=kwargs['exit_code_from'], namespace=kwargs['namespace'])
|
384
389
|
if not exec_command:
|
385
390
|
continue
|
386
391
|
|
@@ -418,7 +423,6 @@ def validate(**kwargs):
|
|
418
423
|
print(f"\nFatal Scaffold Validation Exception: {sve}", file=sys.stderr)
|
419
424
|
sys.exit(1)
|
420
425
|
|
421
|
-
|
422
426
|
scaffold.add_command(app)
|
423
427
|
scaffold.add_command(service)
|
424
428
|
scaffold.add_command(workflow)
|
@@ -473,5 +477,4 @@ def __workflow_build(app, **kwargs):
|
|
473
477
|
headless=kwargs['headless'],
|
474
478
|
template=kwargs['template'],
|
475
479
|
workflow_decorators=workflow_decorators
|
476
|
-
)
|
477
|
-
|
480
|
+
)
|
@@ -1,4 +1,4 @@
|
|
1
|
-
stoobly_agent/__init__.py,sha256=
|
1
|
+
stoobly_agent/__init__.py,sha256=fpNnGTz0zD56l0rzU3F6VxKtLU_SjNTBMAXJgelQlwA,45
|
2
2
|
stoobly_agent/app/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
3
3
|
stoobly_agent/app/api/__init__.py,sha256=ctkB8KR-eXO0SFhj602huHiyvQ3PslFWd8fkcufgrAI,1000
|
4
4
|
stoobly_agent/app/api/application_http_request_handler.py,sha256=jf4fkqjOiCeI2IM5Ro7ie0v_C6y0-7-5TIE_IKMPOfg,5513
|
@@ -67,13 +67,13 @@ stoobly_agent/app/cli/project_cli.py,sha256=EXjeLjbnq9PhfCjvyfZ0UnJ2tejeCS0SIAo3
|
|
67
67
|
stoobly_agent/app/cli/report_cli.py,sha256=ZxJw0Xkx7KFZJn9e45BSKRKon8AD0Msrwy1fbPfbv0c,2543
|
68
68
|
stoobly_agent/app/cli/request_cli.py,sha256=THNloW111l9MLE0oPg4y7hVXL1U7OXoz760g0A1CtJ0,7747
|
69
69
|
stoobly_agent/app/cli/scaffold/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
70
|
-
stoobly_agent/app/cli/scaffold/app.py,sha256=
|
70
|
+
stoobly_agent/app/cli/scaffold/app.py,sha256=jmOn0moveA4pgCgI6i_RzztaNh-GK17FaznU9B9tmsc,3648
|
71
71
|
stoobly_agent/app/cli/scaffold/app_command.py,sha256=o6WV6tcMGBWJsPFdV2DD-m1Dq0cy3Q3SESokaU5hgp4,2061
|
72
72
|
stoobly_agent/app/cli/scaffold/app_config.py,sha256=ZAHGovwxFy3zICfusj9_gUC_FBTIoB7KbPbQaGK8x1c,573
|
73
|
-
stoobly_agent/app/cli/scaffold/app_create_command.py,sha256=
|
73
|
+
stoobly_agent/app/cli/scaffold/app_create_command.py,sha256=1qdKvRvJDmDmYERvbhaUapsglOOWPEHpCj0o7U7bQfo,550
|
74
74
|
stoobly_agent/app/cli/scaffold/command.py,sha256=klpsgL6V7b2DRHpN33pl-QlmntDkvDO_dsF5JziYnL8,245
|
75
75
|
stoobly_agent/app/cli/scaffold/config.py,sha256=HZU5tkvr3dkPr4JMXZtrJlu2wxxO-134Em6jReFFcq0,688
|
76
|
-
stoobly_agent/app/cli/scaffold/constants.py,sha256=
|
76
|
+
stoobly_agent/app/cli/scaffold/constants.py,sha256=B9nUW7FIoq2PUSSkQue0MSG6U7WNsnIBGzvFzoA8PXI,1565
|
77
77
|
stoobly_agent/app/cli/scaffold/docker/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
78
78
|
stoobly_agent/app/cli/scaffold/docker/app_builder.py,sha256=IsXZFUumWEBA8NYDFdAohuKmxocoqHqlVFg0-E92QLI,621
|
79
79
|
stoobly_agent/app/cli/scaffold/docker/builder.py,sha256=lNOvDxoLB1L8KSDrvoG4CUuWLAM2egLxwOWeV9Tf8H8,2832
|
@@ -85,15 +85,15 @@ stoobly_agent/app/cli/scaffold/docker/service/set_gateway_ports.py,sha256=jvlO5x
|
|
85
85
|
stoobly_agent/app/cli/scaffold/docker/service/types.py,sha256=qB-yYHlu-PZDc0HYgTUvE5bWNpHxaSThC3JUG8okR1k,88
|
86
86
|
stoobly_agent/app/cli/scaffold/docker/workflow/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
87
87
|
stoobly_agent/app/cli/scaffold/docker/workflow/build_decorator.py,sha256=vbmME0cbN2EnNRlzQ2umj7Y3L7aZT-EHqEpkBFMfe8U,758
|
88
|
-
stoobly_agent/app/cli/scaffold/docker/workflow/builder.py,sha256=
|
88
|
+
stoobly_agent/app/cli/scaffold/docker/workflow/builder.py,sha256=z1yJ-fEb7trBR-4ADK1tZq2PbTt-iqsnutzvRnTrtf4,6904
|
89
89
|
stoobly_agent/app/cli/scaffold/docker/workflow/decorators_factory.py,sha256=Mi6SEFpG2MG9pymPLbPTWBMfosi1ThRAs5DXfjBz4Iw,643
|
90
90
|
stoobly_agent/app/cli/scaffold/docker/workflow/mock_decorator.py,sha256=D8PgYULERvWLaW1Vn3qspRTuNBoicuP2M1EZnQyvD_M,1080
|
91
|
-
stoobly_agent/app/cli/scaffold/docker/workflow/reverse_proxy_decorator.py,sha256
|
91
|
+
stoobly_agent/app/cli/scaffold/docker/workflow/reverse_proxy_decorator.py,sha256=d2E0ba0ga2IjIE3v1VnLmZLgKiyBB15BerCtL0FyndI,2095
|
92
92
|
stoobly_agent/app/cli/scaffold/env.py,sha256=e-Ve4p3RUgzFx22B3SIYttvJ_yLuDtA27oDACZ8n-6E,1140
|
93
93
|
stoobly_agent/app/cli/scaffold/managed_services_docker_compose.py,sha256=-wLBXUi7DCWsfm5KzZzd_kdJKOTl1NT924XR7dyjbSY,574
|
94
94
|
stoobly_agent/app/cli/scaffold/service.py,sha256=HzYTiQGfReYXEY0Iv4YjGpu5fjCicV61Wst4khmmURs,504
|
95
95
|
stoobly_agent/app/cli/scaffold/service_command.py,sha256=xqifA0PM9xpXK3TiilplmIrJNR7VG7BO0HrLh74yyZw,1058
|
96
|
-
stoobly_agent/app/cli/scaffold/service_config.py,sha256=
|
96
|
+
stoobly_agent/app/cli/scaffold/service_config.py,sha256=XFwTgf1JTfVVWmVyGTIadtfxSoPaoUV-70l3lAtSCzU,3485
|
97
97
|
stoobly_agent/app/cli/scaffold/service_create_command.py,sha256=bmLGgx9qnh-X_i2_XfdWSQIer0gGkaQx6lXZSIzy-LI,2793
|
98
98
|
stoobly_agent/app/cli/scaffold/service_docker_compose.py,sha256=OMUN1-ujQYIZXxDvS4XBf5C9wGalQULkwOiBBQPZbHY,820
|
99
99
|
stoobly_agent/app/cli/scaffold/service_workflow.py,sha256=sQ_Edy_wGHKMXpD0DmhnOWkGEKz7gSgEGNI8f7aXOdg,444
|
@@ -101,7 +101,7 @@ stoobly_agent/app/cli/scaffold/service_workflow_validate_command.py,sha256=zvqKm
|
|
101
101
|
stoobly_agent/app/cli/scaffold/templates/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
102
102
|
stoobly_agent/app/cli/scaffold/templates/app/.Dockerfile.context,sha256=1Zm5_ROKZzFMAkMt8dluEJASI2CBi9d9If_Gch9FMNM,193
|
103
103
|
stoobly_agent/app/cli/scaffold/templates/app/.Dockerfile.proxy,sha256=JfZOVALEmRE_hVEMIHTmem9Ln32qnfKOAUO1c5Z9fqw,1027
|
104
|
-
stoobly_agent/app/cli/scaffold/templates/app/.Makefile,sha256=
|
104
|
+
stoobly_agent/app/cli/scaffold/templates/app/.Makefile,sha256=S2kEyDPSOeTYpyLEjPqGZa-r3nf3vhe23WEpuiidtqM,4717
|
105
105
|
stoobly_agent/app/cli/scaffold/templates/app/.docker-compose.base.yml,sha256=LNNuygX6hMtmElxaO6jTIYq3psYmxNv7xax7ra6JzO4,407
|
106
106
|
stoobly_agent/app/cli/scaffold/templates/app/Makefile,sha256=t7xRaCc9q3k7W34S0h9KX02GljSVwGpPnFmiLUvWxsw,80
|
107
107
|
stoobly_agent/app/cli/scaffold/templates/app/build/.config.yml,sha256=8Wt8ZZ5irvBYYS44xGrR_EWlZDuXH9kyWmquzsh7s8g,19
|
@@ -154,9 +154,9 @@ stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/exec/bin/.disable,sha256
|
|
154
154
|
stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/exec/bin/.enable,sha256=sfUSPG4uHdXX95BLgivXQYLbsLBP2DjJIiSTXRtvXuY,188
|
155
155
|
stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/exec/bin/.mkcert,sha256=vyHaXmvy-7oL2RD8rIxwT-fdJS5kXmB0yHK5fRMo_cM,46
|
156
156
|
stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/exec/bin/.reset,sha256=_pvbLk-ektsCHEh_egDtd6c0sVqCPunwUWggarfoli0,238
|
157
|
-
stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/exec/bin/.run,sha256=
|
157
|
+
stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/exec/bin/.run,sha256=Jkx7q0MO8pYCSnlOEAEkx2Odgb8xbUeylv_RLHDD8NI,268
|
158
158
|
stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/exec/bin/.snapshot,sha256=Kav1QNhKG7f0DBzc4-9dmxZMmmVTxmPqakT_W4kVYKk,241
|
159
|
-
stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/exec/bin/.stop,sha256=
|
159
|
+
stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/exec/bin/.stop,sha256=qj5fRkBubAGGAKiFrjHPMVULDiRLMsIkEF5Al3uhQlo,232
|
160
160
|
stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/mock/.docker-compose.mock.yml,sha256=Ba-jNMZUcq62kK_hLpaN7dkEwz3_ZS91aJOOJ8U3Ark,257
|
161
161
|
stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/record/.docker-compose.record.yml,sha256=cbK8vJUnk8llJ6pIE65AxJxD6SSal2ugm5sKjtz3uRY,260
|
162
162
|
stoobly_agent/app/cli/scaffold/templates/constants.py,sha256=TjuWAZuAbWxS1hZR4jViF1LTBzT8iKLIJE65AvWRKNI,1460
|
@@ -180,16 +180,17 @@ stoobly_agent/app/cli/scaffold/templates/workflow/test/bin/init,sha256=YxWVVQMEd
|
|
180
180
|
stoobly_agent/app/cli/scaffold/templates/workflow/test/fixtures/.keep,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
181
181
|
stoobly_agent/app/cli/scaffold/templates/workflow/test/fixtures.yml,sha256=8DW2LN5WYg1hxmZkqr8o5dxLgDxHtBARFSUiNjz009Y,226
|
182
182
|
stoobly_agent/app/cli/scaffold/templates/workflow/test/lifecycle_hooks.py,sha256=U7mlzT_wBR3uhHSG6CAyt5tBUNAvdIrCw33gdB-F294,467
|
183
|
-
stoobly_agent/app/cli/scaffold/validate_command.py,sha256=
|
183
|
+
stoobly_agent/app/cli/scaffold/validate_command.py,sha256=Rc_z632OSZLPyA4FuhNGz6oGxKKNxaQbE4CRkPcJYdo,2362
|
184
184
|
stoobly_agent/app/cli/scaffold/validate_exceptions.py,sha256=Jtjl4OkbbSRWm0hy7Kf_50zcFh2J324HhNcnwJKH_Oc,54
|
185
185
|
stoobly_agent/app/cli/scaffold/workflow.py,sha256=_uur4wdlxZh-piZralqPNoggIkVJ9AICiJVOz4KqKQQ,1421
|
186
|
-
stoobly_agent/app/cli/scaffold/workflow_command.py,sha256=
|
186
|
+
stoobly_agent/app/cli/scaffold/workflow_command.py,sha256=5fq0t8utfxUQGwJt8dqssBH7AKhsyNVA8PJhmztz0_4,3425
|
187
187
|
stoobly_agent/app/cli/scaffold/workflow_copy_command.py,sha256=R9hh5dWVz7vGld7pENAA_a9gW56EkgG2K35nBQYXwyI,1462
|
188
188
|
stoobly_agent/app/cli/scaffold/workflow_create_command.py,sha256=5xnRYrVb2KlSDARZFjLGGWeURXRu63OHoRLEhO-EAvw,3401
|
189
|
+
stoobly_agent/app/cli/scaffold/workflow_env.py,sha256=x8V5pJmIiklD3f2q2-qq-CORf4YaXYq_r2JpR2MmSwk,416
|
189
190
|
stoobly_agent/app/cli/scaffold/workflow_log_command.py,sha256=FN-XyMVnulbassBuqeB21J_PQnOsr_LNs0Dg2tcDkjg,435
|
190
|
-
stoobly_agent/app/cli/scaffold/workflow_run_command.py,sha256=
|
191
|
+
stoobly_agent/app/cli/scaffold/workflow_run_command.py,sha256=08E7fFnyTn6N9lgQVxzopWkYl7t_LOs9cGIoWh2GvKg,7251
|
191
192
|
stoobly_agent/app/cli/scaffold/workflow_validate_command.py,sha256=fhHciJXg_u32Wmh8us8LhgQj8D1SxkBADtuBBF4K0FM,4377
|
192
|
-
stoobly_agent/app/cli/scaffold_cli.py,sha256=
|
193
|
+
stoobly_agent/app/cli/scaffold_cli.py,sha256=3cKiFyDNs__Uz0pIeLFdAgMtH9lpHtNpNaQBb55Z-3Q,18703
|
193
194
|
stoobly_agent/app/cli/scenario_cli.py,sha256=3J1EiJOvunkfWrEkOsanw-XrKkOk78ij_GjBlE9p7CE,8229
|
194
195
|
stoobly_agent/app/cli/snapshot_cli.py,sha256=XNxpTm5z3bnBGNGI3_XZIif0ypN62WqYfDmShL5fXg4,9965
|
195
196
|
stoobly_agent/app/cli/trace_cli.py,sha256=K7E-vx3JUcqEDSWOdIOi_AieKNQz7dBfmRrVvKDkzFI,4605
|
@@ -720,8 +721,8 @@ stoobly_agent/test/mock_data/scaffold/docker-compose-local-service.yml,sha256=x7
|
|
720
721
|
stoobly_agent/test/mock_data/scaffold/index.html,sha256=qJwuYajKZ4ihWZrJQ3BNObV5kf1VGnnm_vqlPJzdqLE,258
|
721
722
|
stoobly_agent/test/mock_data/uspto.yaml,sha256=6U5se7C3o-86J4m9xpOk9Npias399f5CbfWzR87WKwE,7835
|
722
723
|
stoobly_agent/test/test_helper.py,sha256=m_oAI7tmRYCNZdKfNqISWhMv3e44tjeYViQ3nTUfnos,1007
|
723
|
-
stoobly_agent-1.0.
|
724
|
-
stoobly_agent-1.0.
|
725
|
-
stoobly_agent-1.0.
|
726
|
-
stoobly_agent-1.0.
|
727
|
-
stoobly_agent-1.0.
|
724
|
+
stoobly_agent-1.0.12.dist-info/LICENSE,sha256=8QKGyy45eN76Zk52h8gu1DKX2B_gbWgZ3nzDLofEbaE,548
|
725
|
+
stoobly_agent-1.0.12.dist-info/METADATA,sha256=6sEDVBgyenOEFxaPIwnLLqJpmoKELTXHsjgJ3QdcpqA,3426
|
726
|
+
stoobly_agent-1.0.12.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
|
727
|
+
stoobly_agent-1.0.12.dist-info/entry_points.txt,sha256=aq5wix5oC8MDQtmyPGU0xaFrsjJg7WH28NmXh2sc3Z8,56
|
728
|
+
stoobly_agent-1.0.12.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|