stoobly-agent 1.0.9__py3-none-any.whl → 1.0.11__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 +76 -5
- stoobly_agent/app/cli/scaffold_cli.py +6 -4
- {stoobly_agent-1.0.9.dist-info → stoobly_agent-1.0.11.dist-info}/METADATA +1 -1
- {stoobly_agent-1.0.9.dist-info → stoobly_agent-1.0.11.dist-info}/RECORD +20 -19
- {stoobly_agent-1.0.9.dist-info → stoobly_agent-1.0.11.dist-info}/LICENSE +0 -0
- {stoobly_agent-1.0.9.dist-info → stoobly_agent-1.0.11.dist-info}/WHEEL +0 -0
- {stoobly_agent-1.0.9.dist-info → stoobly_agent-1.0.11.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.11'
|
@@ -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,9 +10,12 @@ 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
|
|
@@ -62,6 +68,20 @@ class WorkflowRunCommand(WorkflowCommand):
|
|
62
68
|
def extra_compose_path(self):
|
63
69
|
return self.__extra_compose_path
|
64
70
|
|
71
|
+
@property
|
72
|
+
def nameservers(self):
|
73
|
+
path = self.nameservers_path
|
74
|
+
if not os.path.exists(path):
|
75
|
+
return []
|
76
|
+
|
77
|
+
with open(path, 'r') as fp:
|
78
|
+
nameservers = fp.read()
|
79
|
+
return nameservers.split("\n")
|
80
|
+
|
81
|
+
@property
|
82
|
+
def nameservers_path(self):
|
83
|
+
return os.path.join(self.app.data_dir.tmp_dir_path, NAMESERVERS_FILE)
|
84
|
+
|
65
85
|
@property
|
66
86
|
def network(self):
|
67
87
|
return self.__network
|
@@ -110,6 +130,7 @@ class WorkflowRunCommand(WorkflowCommand):
|
|
110
130
|
command.append('-d')
|
111
131
|
|
112
132
|
command.append('--build')
|
133
|
+
command.append('--pull always')
|
113
134
|
|
114
135
|
self.write_env()
|
115
136
|
|
@@ -136,6 +157,27 @@ class WorkflowRunCommand(WorkflowCommand):
|
|
136
157
|
|
137
158
|
return ' '.join(command)
|
138
159
|
|
160
|
+
def write_nameservers(self):
|
161
|
+
# If hostname is set then the service is external and we will need to configure the container's DNS.
|
162
|
+
# If we don't configure the container's DNS, then Docker's embedded DNS will potentially
|
163
|
+
# use configuration from the host's /etc/hosts file. The user may have configured their
|
164
|
+
# /etc/hosts file to resolve requests to localhost
|
165
|
+
#
|
166
|
+
# See:
|
167
|
+
# https://forums.docker.com/t/docker-127-0-0-11-resolver-should-use-host-etc-hosts-file/55157
|
168
|
+
# https://docs.docker.com/network/#dns-services
|
169
|
+
#
|
170
|
+
# TODO: ideally we want to know if the service is built locally, if so, then no need to set DNS
|
171
|
+
# since Docker's embedded DNS will resolve to it
|
172
|
+
dns_resolver = dns.resolver.Resolver()
|
173
|
+
|
174
|
+
with open(self.nameservers_path, 'w') as fp:
|
175
|
+
nameservers = self.__find_nameservers(dns_resolver)
|
176
|
+
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]?)$')
|
177
|
+
nameservers = [ip for ip in nameservers if ipv4_pattern.match(ip)]
|
178
|
+
if nameservers:
|
179
|
+
fp.write("\n".join(nameservers))
|
180
|
+
|
139
181
|
def write_env(self):
|
140
182
|
_config = {}
|
141
183
|
_config[CA_CERTS_DIR_ENV] = self.ca_certs_dir_path
|
@@ -148,7 +190,36 @@ class WorkflowRunCommand(WorkflowCommand):
|
|
148
190
|
if self.network:
|
149
191
|
_config[APP_NETWORK_ENV] = self.network
|
150
192
|
|
151
|
-
|
152
|
-
|
153
|
-
|
193
|
+
nameservers = self.nameservers
|
194
|
+
if nameservers:
|
195
|
+
_config[SERVICE_DNS_ENV] = nameservers[0]
|
154
196
|
|
197
|
+
env_vars = self.config(_config)
|
198
|
+
WorkflowEnv(self.workflow_path).write(env_vars)
|
199
|
+
return env_vars
|
200
|
+
|
201
|
+
def __find_nameservers(self, dns_resolver: dns.resolver.Resolver):
|
202
|
+
nameservers = dns_resolver.nameservers
|
203
|
+
|
204
|
+
# If systemd-resolved is not used
|
205
|
+
if nameservers != ['127.0.0.53']:
|
206
|
+
return nameservers
|
207
|
+
|
208
|
+
# Run the `resolvectl status` command and capture its output
|
209
|
+
result = subprocess.run(['resolvectl', 'status'], stdout=subprocess.PIPE, stderr=subprocess.PIPE, text=True)
|
210
|
+
|
211
|
+
# Check if the command ran successfully
|
212
|
+
if result.returncode != 0:
|
213
|
+
return []
|
214
|
+
|
215
|
+
# Extract the DNS servers using a regular expression
|
216
|
+
#dns_servers = re.findall(r'DNS Servers: ([\d.]+(?:, [\d.]+)*)', result.stdout)
|
217
|
+
pattern = re.compile('DNS Servers:(.*?)DNS Domain', re.DOTALL)
|
218
|
+
match = re.findall(pattern, result.stdout)
|
219
|
+
|
220
|
+
if not match:
|
221
|
+
return []
|
222
|
+
|
223
|
+
# Split the DNS servers string into a list
|
224
|
+
dns_servers = match[0].strip().split("\n")
|
225
|
+
return list(map(lambda dns_server: dns_server.strip(), dns_servers))
|
@@ -367,8 +367,12 @@ def run(**kwargs):
|
|
367
367
|
|
368
368
|
# Before services can be started, their network needs to be created
|
369
369
|
if len(commands) > 0:
|
370
|
-
|
370
|
+
command = commands[0]
|
371
|
+
create_network_command = command.create_network()
|
372
|
+
|
371
373
|
if not kwargs['dry_run']:
|
374
|
+
command.write_nameservers()
|
375
|
+
|
372
376
|
exec_stream(create_network_command)
|
373
377
|
else:
|
374
378
|
print(create_network_command)
|
@@ -418,7 +422,6 @@ def validate(**kwargs):
|
|
418
422
|
print(f"\nFatal Scaffold Validation Exception: {sve}", file=sys.stderr)
|
419
423
|
sys.exit(1)
|
420
424
|
|
421
|
-
|
422
425
|
scaffold.add_command(app)
|
423
426
|
scaffold.add_command(service)
|
424
427
|
scaffold.add_command(workflow)
|
@@ -473,5 +476,4 @@ def __workflow_build(app, **kwargs):
|
|
473
476
|
headless=kwargs['headless'],
|
474
477
|
template=kwargs['template'],
|
475
478
|
workflow_decorators=workflow_decorators
|
476
|
-
)
|
477
|
-
|
479
|
+
)
|
@@ -1,4 +1,4 @@
|
|
1
|
-
stoobly_agent/__init__.py,sha256=
|
1
|
+
stoobly_agent/__init__.py,sha256=4ndlBfDCZW8uz1Z5ZYihu_3hi9biEC-Z6NJUmmeJqvI,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=CTa-JcaTtIwXvGXeVmvgAYEXnvAh9NnbWK_iXtRSnOU,7107
|
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=3bMMw5M2uTk0Z3KUB3VGyS9JiS3TCaEpbDSeL6RHUZI,18568
|
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.11.dist-info/LICENSE,sha256=8QKGyy45eN76Zk52h8gu1DKX2B_gbWgZ3nzDLofEbaE,548
|
725
|
+
stoobly_agent-1.0.11.dist-info/METADATA,sha256=h-NGNvATfZVk3ILpiL8t3svqHQYCHtkR6RF7U8VCcrw,3426
|
726
|
+
stoobly_agent-1.0.11.dist-info/WHEEL,sha256=IYZQI976HJqqOpQU6PHkJ8fb3tMNBFjg-Cn-pwAbaFM,88
|
727
|
+
stoobly_agent-1.0.11.dist-info/entry_points.txt,sha256=aq5wix5oC8MDQtmyPGU0xaFrsjJg7WH28NmXh2sc3Z8,56
|
728
|
+
stoobly_agent-1.0.11.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|