stoobly-agent 1.2.3__py3-none-any.whl → 1.4.0__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/api/application_http_request_handler.py +3 -3
- stoobly_agent/app/api/proxy_controller.py +8 -7
- stoobly_agent/app/cli/config_cli.py +1 -1
- stoobly_agent/app/cli/helpers/certificate_authority.py +7 -6
- stoobly_agent/app/cli/helpers/print_service.py +17 -0
- stoobly_agent/app/cli/scaffold/app.py +16 -34
- stoobly_agent/app/cli/scaffold/app_command.py +4 -7
- stoobly_agent/app/cli/scaffold/app_config.py +15 -2
- stoobly_agent/app/cli/scaffold/app_create_command.py +18 -2
- stoobly_agent/app/cli/scaffold/command.py +1 -1
- stoobly_agent/app/cli/scaffold/constants.py +9 -5
- stoobly_agent/app/cli/scaffold/docker/app_builder.py +3 -7
- stoobly_agent/app/cli/scaffold/docker/constants.py +0 -1
- stoobly_agent/app/cli/scaffold/docker/service/builder.py +12 -11
- stoobly_agent/app/cli/scaffold/docker/workflow/builder.py +14 -31
- stoobly_agent/app/cli/scaffold/docker/workflow/mock_decorator.py +6 -2
- stoobly_agent/app/cli/scaffold/docker/workflow/reverse_proxy_decorator.py +6 -2
- stoobly_agent/app/cli/scaffold/hosts_file_manager.py +112 -0
- stoobly_agent/app/cli/scaffold/service.py +1 -2
- stoobly_agent/app/cli/scaffold/service_command.py +1 -1
- stoobly_agent/app/cli/scaffold/service_config.py +10 -14
- stoobly_agent/app/cli/scaffold/service_workflow_validate_command.py +9 -11
- stoobly_agent/app/cli/scaffold/templates/app/.Dockerfile.context +2 -4
- stoobly_agent/app/cli/scaffold/templates/app/.Makefile +108 -68
- stoobly_agent/app/cli/scaffold/templates/app/.docker-compose.base.yml +8 -13
- stoobly_agent/app/cli/scaffold/templates/app/Makefile +1 -1
- stoobly_agent/app/cli/scaffold/templates/app/build/.docker-compose.base.yml +8 -4
- stoobly_agent/app/cli/scaffold/templates/app/build/mock/.docker-compose.mock.yml +2 -6
- stoobly_agent/app/cli/scaffold/templates/app/build/mock/bin/.configure +3 -0
- stoobly_agent/app/cli/scaffold/templates/app/build/mock/bin/.init +3 -0
- stoobly_agent/app/cli/scaffold/templates/app/build/record/.docker-compose.record.yml +2 -6
- stoobly_agent/app/cli/scaffold/templates/app/build/record/bin/.configure +3 -0
- stoobly_agent/app/cli/scaffold/templates/app/build/record/bin/.init +3 -0
- stoobly_agent/app/cli/scaffold/templates/app/build/test/.docker-compose.test.yml +2 -6
- stoobly_agent/app/cli/scaffold/templates/app/build/test/bin/.configure +3 -0
- stoobly_agent/app/cli/scaffold/templates/app/build/test/bin/.init +3 -0
- stoobly_agent/app/cli/scaffold/templates/app/entrypoint/.docker-compose.base.yml +2 -0
- stoobly_agent/app/cli/scaffold/templates/app/entrypoint/mock/.docker-compose.mock.yml +2 -8
- stoobly_agent/app/cli/scaffold/templates/app/entrypoint/record/.docker-compose.record.yml +2 -8
- stoobly_agent/app/cli/scaffold/templates/app/entrypoint/test/.docker-compose.test.yml +2 -8
- stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/exec/.docker-compose.exec.yml +2 -3
- stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/exec/bin/.logs +1 -0
- stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/exec/bin/.services +9 -0
- stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/mock/.docker-compose.mock.yml +1 -2
- stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/record/.docker-compose.record.yml +1 -2
- stoobly_agent/app/cli/scaffold/templates/workflow/mock/bin/.configure +3 -0
- stoobly_agent/app/cli/scaffold/templates/workflow/mock/bin/.init +7 -1
- stoobly_agent/app/cli/scaffold/templates/workflow/record/bin/.configure +3 -0
- stoobly_agent/app/cli/scaffold/templates/workflow/record/bin/.init +7 -1
- stoobly_agent/app/cli/scaffold/templates/workflow/test/bin/.configure +3 -0
- stoobly_agent/app/cli/scaffold/templates/workflow/test/bin/.init +7 -1
- stoobly_agent/app/cli/scaffold/validate_command.py +2 -2
- stoobly_agent/app/cli/scaffold/workflow.py +5 -4
- stoobly_agent/app/cli/scaffold/workflow_command.py +3 -3
- stoobly_agent/app/cli/scaffold/workflow_create_command.py +0 -1
- stoobly_agent/app/cli/scaffold/workflow_run_command.py +78 -45
- stoobly_agent/app/cli/scaffold_cli.py +246 -109
- stoobly_agent/app/cli/snapshot_cli.py +7 -3
- stoobly_agent/app/models/adapters/joined_request_adapter.py +6 -0
- stoobly_agent/app/models/factories/resource/local_db/helpers/scenario_snapshot.py +3 -1
- stoobly_agent/app/models/helpers/apply.py +34 -17
- stoobly_agent/app/models/helpers/create_request_params_service.py +4 -0
- stoobly_agent/app/proxy/handle_mock_service.py +2 -0
- stoobly_agent/app/proxy/handle_replay_service.py +2 -0
- stoobly_agent/app/proxy/mitmproxy/request_facade.py +1 -1
- stoobly_agent/app/proxy/mitmproxy/response_body_facade.py +19 -0
- stoobly_agent/app/proxy/mitmproxy/response_facade.py +90 -18
- stoobly_agent/app/proxy/record/join_request_service.py +1 -1
- stoobly_agent/app/proxy/replay/body_parser_service.py +11 -3
- stoobly_agent/app/settings/constants/request_component.py +2 -1
- stoobly_agent/config/constants/custom_headers.py +13 -13
- stoobly_agent/config/constants/headers.py +0 -2
- stoobly_agent/config/data_dir.py +2 -1
- stoobly_agent/config/schema.yml +2 -2
- stoobly_agent/public/18-es2015.583f191cc7ad512ee262.js +1 -0
- stoobly_agent/public/18-es5.583f191cc7ad512ee262.js +1 -0
- stoobly_agent/public/35-es2015.8f79ff8748d4ff06ab03.js +1 -0
- stoobly_agent/public/35-es5.8f79ff8748d4ff06ab03.js +1 -0
- stoobly_agent/public/index.html +1 -1
- stoobly_agent/public/main-es2015.2cc16523aa3fcaba51e5.js +1 -0
- stoobly_agent/public/main-es5.2cc16523aa3fcaba51e5.js +1 -0
- stoobly_agent/public/{runtime-es2015.9addf49b79aca951b7e2.js → runtime-es2015.b914470164e4d6e75d96.js} +1 -1
- stoobly_agent/public/{runtime-es5.9addf49b79aca951b7e2.js → runtime-es5.b914470164e4d6e75d96.js} +1 -1
- stoobly_agent/test/app/cli/scaffold/cli_invoker.py +1 -2
- stoobly_agent/test/app/cli/scaffold/{hosts_file_reader_test.py → hosts_file_manager_test.py} +20 -20
- stoobly_agent/test/app/cli/snapshot/snapshot_apply_test.py +162 -1
- stoobly_agent/test/app/models/schemas/.stoobly/db/VERSION +1 -1
- stoobly_agent/test/mock_data/scaffold/docker-compose-assets-service.yml +1 -3
- {stoobly_agent-1.2.3.dist-info → stoobly_agent-1.4.0.dist-info}/METADATA +1 -1
- {stoobly_agent-1.2.3.dist-info → stoobly_agent-1.4.0.dist-info}/RECORD +94 -93
- stoobly_agent/app/cli/scaffold/hosts_file_reader.py +0 -65
- stoobly_agent/app/cli/scaffold/templates/app/.Dockerfile.proxy +0 -34
- stoobly_agent/public/18-es2015.d3b430636a4d6f544d92.js +0 -1
- stoobly_agent/public/18-es5.d3b430636a4d6f544d92.js +0 -1
- stoobly_agent/public/35-es2015.f741ebce0bfc25f0ec99.js +0 -1
- stoobly_agent/public/35-es5.f741ebce0bfc25f0ec99.js +0 -1
- stoobly_agent/public/main-es2015.ccd46ac1b6638ddf2066.js +0 -1
- stoobly_agent/public/main-es5.ccd46ac1b6638ddf2066.js +0 -1
- {stoobly_agent-1.2.3.dist-info → stoobly_agent-1.4.0.dist-info}/LICENSE +0 -0
- {stoobly_agent-1.2.3.dist-info → stoobly_agent-1.4.0.dist-info}/WHEEL +0 -0
- {stoobly_agent-1.2.3.dist-info → stoobly_agent-1.4.0.dist-info}/entry_points.txt +0 -0
@@ -1,8 +1,9 @@
|
|
1
|
+
import os
|
1
2
|
import pdb
|
2
3
|
|
3
4
|
from urllib.parse import urlparse
|
4
5
|
|
5
|
-
from ...constants import SERVICE_HOSTNAME, SERVICE_PORT
|
6
|
+
from ...constants import SERVICE_HOSTNAME, SERVICE_PORT, STOOBLY_CERTS_DIR
|
6
7
|
from .builder import WorkflowBuilder
|
7
8
|
|
8
9
|
class ReverseProxyDecorator():
|
@@ -22,7 +23,6 @@ class ReverseProxyDecorator():
|
|
22
23
|
config = self.service_builder.config
|
23
24
|
|
24
25
|
command = [
|
25
|
-
'--certs', f"/etc/ssl/certs/{SERVICE_HOSTNAME}-joined.pem",
|
26
26
|
'--headless',
|
27
27
|
'--lifecycle-hooks-path', 'lifecycle_hooks.py',
|
28
28
|
'--proxy-mode', config.proxy_mode,
|
@@ -30,6 +30,10 @@ class ReverseProxyDecorator():
|
|
30
30
|
'--ssl-insecure'
|
31
31
|
]
|
32
32
|
|
33
|
+
if config.scheme == 'https':
|
34
|
+
command.append('--certs')
|
35
|
+
command.append(os.path.join(STOOBLY_CERTS_DIR, f"{SERVICE_HOSTNAME}-joined.pem"))
|
36
|
+
|
33
37
|
services = self.workflow_builder.services
|
34
38
|
proxy_name = self.workflow_builder.proxy
|
35
39
|
proxy_service = services.get(proxy_name) or {}
|
@@ -0,0 +1,112 @@
|
|
1
|
+
import os
|
2
|
+
import pdb
|
3
|
+
import sys
|
4
|
+
|
5
|
+
from dataclasses import dataclass
|
6
|
+
from typing import Union
|
7
|
+
|
8
|
+
SCAFFOLD_HOSTS_DELIMITTER_BEGIN = "##### STOOBLY SCAFFOLD HOSTS BEGIN #####\n"
|
9
|
+
SCAFFOLD_HOSTS_DELIMITTER_END = "##### STOOBLY SCAFFOLD HOSTS END #####\n"
|
10
|
+
|
11
|
+
class HostsFileManager():
|
12
|
+
|
13
|
+
@dataclass
|
14
|
+
class IpAddressToHostnames:
|
15
|
+
ip_address: str
|
16
|
+
hostnames: list[str]
|
17
|
+
|
18
|
+
def __get_hosts_file_path(self) -> str:
|
19
|
+
file_path = '/etc/hosts'
|
20
|
+
if not os.path.exists(file_path):
|
21
|
+
print(f"Error: File {file_path} not found.", file=sys.stderr)
|
22
|
+
sys.exit(1)
|
23
|
+
return file_path
|
24
|
+
|
25
|
+
# Split IP address and hostnames. Don't include inline comments
|
26
|
+
def __split_hosts_line(self, line: str) -> list[str]:
|
27
|
+
ip_addr_hosts_split = line.split('#')[0].split()
|
28
|
+
return ip_addr_hosts_split
|
29
|
+
|
30
|
+
# Parses hosts file and returns a mapping of IP address to hostnames in a list.
|
31
|
+
def get_hosts(self) -> list[IpAddressToHostnames]:
|
32
|
+
hosts_file_path = self.__get_hosts_file_path()
|
33
|
+
|
34
|
+
if not hosts_file_path:
|
35
|
+
return []
|
36
|
+
|
37
|
+
with open(hosts_file_path, 'r') as f:
|
38
|
+
hostlines = f.readlines()
|
39
|
+
|
40
|
+
# Skip comments and empty lines
|
41
|
+
hostlines = [line.strip() for line in hostlines
|
42
|
+
if not line.startswith('#') and line.strip() != '']
|
43
|
+
|
44
|
+
hosts = []
|
45
|
+
for line in hostlines:
|
46
|
+
ip_addr_hosts_split = self.__split_hosts_line(line)
|
47
|
+
ip_address = ip_addr_hosts_split[0]
|
48
|
+
hostnames = ip_addr_hosts_split[1:]
|
49
|
+
ipAddressToHostnames = self.IpAddressToHostnames(ip_address, hostnames)
|
50
|
+
|
51
|
+
hosts.append(ipAddressToHostnames)
|
52
|
+
|
53
|
+
return hosts
|
54
|
+
|
55
|
+
def find_host(self, hostname) -> Union[IpAddressToHostnames, None]:
|
56
|
+
hosts = self.get_hosts()
|
57
|
+
|
58
|
+
for mapping in hosts:
|
59
|
+
if ((mapping.ip_address == '0.0.0.0' or mapping.ip_address == '127.0.0.1') and
|
60
|
+
hostname in mapping.hostnames):
|
61
|
+
|
62
|
+
return mapping
|
63
|
+
|
64
|
+
return None
|
65
|
+
|
66
|
+
def install_hostnames(self, hostnames: list[str]) -> None:
|
67
|
+
hosts_file_path = self.__get_hosts_file_path()
|
68
|
+
|
69
|
+
self.remove_lines_between_markers(
|
70
|
+
hosts_file_path, SCAFFOLD_HOSTS_DELIMITTER_BEGIN, SCAFFOLD_HOSTS_DELIMITTER_END
|
71
|
+
)
|
72
|
+
|
73
|
+
with open(hosts_file_path, 'a+') as f:
|
74
|
+
if SCAFFOLD_HOSTS_DELIMITTER_BEGIN not in f.read():
|
75
|
+
f.write(SCAFFOLD_HOSTS_DELIMITTER_BEGIN)
|
76
|
+
|
77
|
+
for hostname in hostnames:
|
78
|
+
print(f"Installing hostname {hostname} to {hosts_file_path}")
|
79
|
+
f.write(f"127.0.0.1 {hostname}\n")
|
80
|
+
f.write(f"::1 {hostname}\n")
|
81
|
+
|
82
|
+
if SCAFFOLD_HOSTS_DELIMITTER_END not in f.read():
|
83
|
+
f.write(SCAFFOLD_HOSTS_DELIMITTER_END)
|
84
|
+
|
85
|
+
def uninstall_hostnames(self) -> None:
|
86
|
+
hosts_file_path = self.__get_hosts_file_path()
|
87
|
+
|
88
|
+
self.remove_lines_between_markers(
|
89
|
+
hosts_file_path, SCAFFOLD_HOSTS_DELIMITTER_BEGIN, SCAFFOLD_HOSTS_DELIMITTER_END
|
90
|
+
)
|
91
|
+
|
92
|
+
print(f"Uninstalled hostnames from {hosts_file_path}")
|
93
|
+
|
94
|
+
def remove_lines_between_markers(self, file_path, start_marker, end_marker):
|
95
|
+
with open(file_path, "r") as file:
|
96
|
+
lines = file.readlines()
|
97
|
+
|
98
|
+
inside_block = False
|
99
|
+
filtered_lines = []
|
100
|
+
|
101
|
+
for line in lines:
|
102
|
+
if start_marker in line:
|
103
|
+
inside_block = True
|
104
|
+
continue # Skip the start marker line
|
105
|
+
if end_marker in line:
|
106
|
+
inside_block = False
|
107
|
+
continue # Skip the end marker line
|
108
|
+
if not inside_block:
|
109
|
+
filtered_lines.append(line)
|
110
|
+
|
111
|
+
with open(file_path, "w") as file:
|
112
|
+
file.writelines(filtered_lines)
|
@@ -14,12 +14,11 @@ class Service():
|
|
14
14
|
|
15
15
|
@property
|
16
16
|
def dir_path(self):
|
17
|
-
return os.path.join(self.app.
|
17
|
+
return os.path.join(self.app.scaffold_namespace_path, self.service_name)
|
18
18
|
|
19
19
|
@property
|
20
20
|
def service_name(self):
|
21
21
|
return self.__service_name
|
22
22
|
|
23
|
-
@property
|
24
23
|
def workflow_dir_path(self, workflow_name: str):
|
25
24
|
return os.path.join(self.dir_path, workflow_name)
|
@@ -4,7 +4,6 @@ import pdb
|
|
4
4
|
from .config import Config
|
5
5
|
from .constants import (
|
6
6
|
SERVICE_DETACHED_ENV,
|
7
|
-
SERVICE_DOCKER_COMPOSE_PATH_ENV,
|
8
7
|
SERVICE_HOSTNAME_ENV,
|
9
8
|
SERVICE_PRIORITY_ENV,
|
10
9
|
SERVICE_PORT_ENV,
|
@@ -18,7 +17,6 @@ class ServiceConfig(Config):
|
|
18
17
|
super().__init__(dir)
|
19
18
|
|
20
19
|
self.__detached = None
|
21
|
-
self.__docker_compose_path = None
|
22
20
|
self.__hostname = None
|
23
21
|
self.__port = None
|
24
22
|
self.__priority = None
|
@@ -53,14 +51,6 @@ class ServiceConfig(Config):
|
|
53
51
|
def detached(self, v):
|
54
52
|
self.__detached = v
|
55
53
|
|
56
|
-
@property
|
57
|
-
def docker_compose_path(self):
|
58
|
-
return self.__docker_compose_path
|
59
|
-
|
60
|
-
@docker_compose_path.setter
|
61
|
-
def docker_compose_path(self, v):
|
62
|
-
self.__docker_compose_path = v
|
63
|
-
|
64
54
|
@property
|
65
55
|
def hostname(self):
|
66
56
|
return (self.__hostname or '').strip()
|
@@ -120,19 +110,25 @@ class ServiceConfig(Config):
|
|
120
110
|
config = config or self.read()
|
121
111
|
|
122
112
|
self.detached = config.get(SERVICE_DETACHED_ENV)
|
123
|
-
self.docker_compose_path = config.get(SERVICE_DOCKER_COMPOSE_PATH_ENV)
|
124
113
|
self.hostname = config.get(SERVICE_HOSTNAME_ENV)
|
125
114
|
self.port = config.get(SERVICE_PORT_ENV)
|
126
115
|
self.priority = config.get(SERVICE_PRIORITY_ENV)
|
127
116
|
self.proxy_mode = config.get(SERVICE_PROXY_MODE_ENV)
|
128
117
|
self.scheme = config.get(SERVICE_SCHEME_ENV)
|
129
118
|
|
119
|
+
def to_dict(self):
|
120
|
+
return {
|
121
|
+
'detached': self.detached,
|
122
|
+
'hostname': self.hostname,
|
123
|
+
'port': self.port,
|
124
|
+
'priority': self.priority,
|
125
|
+
'proxy_mode': self.proxy_mode,
|
126
|
+
'scheme': self.scheme,
|
127
|
+
}
|
128
|
+
|
130
129
|
def write(self):
|
131
130
|
config = {}
|
132
131
|
|
133
|
-
if self.docker_compose_path:
|
134
|
-
config[SERVICE_DOCKER_COMPOSE_PATH_ENV] = self.docker_compose_path
|
135
|
-
|
136
132
|
if self.hostname:
|
137
133
|
config[SERVICE_HOSTNAME_ENV] = self.hostname
|
138
134
|
|
@@ -1,28 +1,24 @@
|
|
1
1
|
import os
|
2
2
|
import pdb
|
3
3
|
import socket
|
4
|
-
import ssl
|
5
4
|
import time
|
5
|
+
|
6
6
|
from collections import Counter
|
7
7
|
from pathlib import Path
|
8
8
|
|
9
|
-
import requests
|
10
9
|
import yaml
|
11
10
|
from docker.models.containers import Container
|
12
|
-
from requests.adapters import HTTPAdapter
|
13
|
-
from urllib3 import Retry
|
14
11
|
|
15
12
|
from stoobly_agent.app.cli.scaffold.constants import (
|
16
13
|
FIXTURES_FOLDER_NAME,
|
17
14
|
STOOBLY_DATA_DIR,
|
18
|
-
STOOBLY_HOME_DIR,
|
19
15
|
VIRTUAL_HOST_ENV,
|
20
16
|
VIRTUAL_PORT_ENV,
|
21
17
|
VIRTUAL_PROTO_ENV,
|
22
18
|
WORKFLOW_RECORD_TYPE,
|
23
19
|
WORKFLOW_TEST_TYPE,
|
24
20
|
)
|
25
|
-
from stoobly_agent.app.cli.scaffold.
|
21
|
+
from stoobly_agent.app.cli.scaffold.hosts_file_manager import HostsFileManager
|
26
22
|
from stoobly_agent.app.cli.scaffold.service_command import ServiceCommand
|
27
23
|
from stoobly_agent.app.cli.scaffold.service_docker_compose import ServiceDockerCompose
|
28
24
|
from stoobly_agent.app.cli.scaffold.validate_command import ValidateCommand
|
@@ -39,7 +35,9 @@ class ServiceWorkflowValidateCommand(ServiceCommand, ValidateCommand):
|
|
39
35
|
|
40
36
|
self.workflow_name = kwargs['workflow_name']
|
41
37
|
self.hostname = self.service_config.hostname
|
42
|
-
self.service_docker_compose = ServiceDockerCompose(
|
38
|
+
self.service_docker_compose = ServiceDockerCompose(
|
39
|
+
app_dir_path=app.dir_path, target_workflow_name=self.workflow_name, service_name=self.service_name, hostname=self.hostname
|
40
|
+
)
|
43
41
|
|
44
42
|
@property
|
45
43
|
def fixtures_dir_path(self):
|
@@ -48,7 +46,7 @@ class ServiceWorkflowValidateCommand(ServiceCommand, ValidateCommand):
|
|
48
46
|
@property
|
49
47
|
def workflow_path(self):
|
50
48
|
return os.path.join(
|
51
|
-
self.
|
49
|
+
self.data_dir_path,
|
52
50
|
self.workflow_relative_path
|
53
51
|
)
|
54
52
|
@property
|
@@ -91,8 +89,8 @@ class ServiceWorkflowValidateCommand(ServiceCommand, ValidateCommand):
|
|
91
89
|
def hostname_exists(self, hostname: str) -> bool:
|
92
90
|
print(f"Validating hostname exists in hosts file for hostname: {hostname}")
|
93
91
|
|
94
|
-
|
95
|
-
host_mapping =
|
92
|
+
hosts_file_manager = HostsFileManager()
|
93
|
+
host_mapping = hosts_file_manager.find_host(hostname)
|
96
94
|
if host_mapping:
|
97
95
|
print(f"Correct hosts mapping found for {hostname}")
|
98
96
|
return True
|
@@ -151,7 +149,7 @@ class ServiceWorkflowValidateCommand(ServiceCommand, ValidateCommand):
|
|
151
149
|
return
|
152
150
|
|
153
151
|
# Check contents of fixtures folder to confirm it's shared
|
154
|
-
fixtures_folder_path = f"{
|
152
|
+
fixtures_folder_path = f"{FIXTURES_FOLDER_NAME}"
|
155
153
|
exec_result = container.exec_run(f"ls -A {fixtures_folder_path}")
|
156
154
|
output = exec_result.output
|
157
155
|
|
@@ -1,8 +1,6 @@
|
|
1
|
-
FROM stoobly/agent:1.
|
1
|
+
FROM stoobly/agent:1.4
|
2
2
|
|
3
3
|
ARG USER_ID
|
4
4
|
|
5
5
|
# Change user id of stoobly user to that of host's user id
|
6
|
-
RUN if [ -n "$USER_ID" ]; then usermod -u $USER_ID stoobly; fi
|
7
|
-
|
8
|
-
COPY --chown=stoobly:stoobly . .
|
6
|
+
RUN if [ -n "$USER_ID" ]; then usermod -u $USER_ID stoobly; fi
|
@@ -4,11 +4,13 @@
|
|
4
4
|
# STOOBLY_CA_CERTS_DIR: path to folder where ca certs are stored
|
5
5
|
# STOOBLY_CERTS_DIR: path to a folder to store certs
|
6
6
|
# STOOBLY_CONTEXT_DIR: path to the folder containing the .stoobly folder
|
7
|
-
#
|
7
|
+
# STOOBLY_WORKFLOW_SERVICE_OPTIONS: extra --service options to pass 'stoobly-agent scaffold workflow' commands
|
8
8
|
|
9
9
|
# Constants
|
10
10
|
DIR := $(dir $(realpath $(lastword $(MAKEFILE_LIST))))
|
11
11
|
EXEC_WORKFLOW_NAME := exec
|
12
|
+
PULL_OPTION := $(if $(STOOBLY_IMAGE_USE_LOCAL),,--pull)
|
13
|
+
USER_ID := $(shell id -u)
|
12
14
|
|
13
15
|
CONTEXT_DIR_DEFAULT := $(realpath $(DIR)/../..)
|
14
16
|
|
@@ -17,36 +19,57 @@ app_dir=$$(realpath "$${STOOBLY_APP_DIR:-$(CONTEXT_DIR_DEFAULT)}")
|
|
17
19
|
ca_certs_dir=$$(realpath "$${STOOBLY_CA_CERTS_DIR:-$$(realpath ~)/.mitmproxy}")
|
18
20
|
certs_dir=$$(realpath "$${STOOBLY_CERTS_DIR:-$(app_data_dir)/certs}")
|
19
21
|
context_dir=$$(realpath "$${STOOBLY_CONTEXT_DIR:-$(CONTEXT_DIR_DEFAULT)}")
|
20
|
-
|
22
|
+
|
23
|
+
context_dir_option=--context-dir-path $(context_dir)
|
24
|
+
user_id_option=--user-id $(USER_ID)
|
25
|
+
stoobly_exec_options=--profile $(EXEC_WORKFLOW_NAME) -p $(EXEC_WORKFLOW_NAME)
|
26
|
+
workflow_down_options=$(user_id_option)
|
27
|
+
workflow_service_options=$(shell echo $$STOOBLY_WORKFLOW_SERVICE_OPTIONS)
|
28
|
+
workflow_up_options=$(context_dir_option) --ca-certs-dir-path $(ca_certs_dir) --certs-dir-path $(certs_dir) --from-make $(user_id_option)
|
21
29
|
|
22
30
|
app_data_dir=$(app_dir)/.stoobly
|
23
|
-
|
31
|
+
app_namespace_dir=$(app_data_dir)/docker
|
24
32
|
app_tmp_dir=$(app_data_dir)/tmp
|
33
|
+
data_dir=$(context_dir)/.stoobly
|
34
|
+
dockerfile_path=$(app_namespace_dir)/.Dockerfile.context
|
35
|
+
docker_compose_file_path=$(app_namespace_dir)/stoobly-ui/exec/.docker-compose.exec.yml
|
36
|
+
workflow_run_script=$(app_data_dir)/tmp/run.sh
|
25
37
|
|
26
38
|
# Commands
|
27
|
-
|
39
|
+
docker_command=docker
|
40
|
+
docker_compose_command=$(docker_command) compose
|
41
|
+
exec_env=export CA_CERTS_DIR="$(ca_certs_dir)" && export USER_ID=$(USER_ID)
|
42
|
+
exec_up=$(docker_compose_command) -f "$(docker_compose_file_path)" $(stoobly_exec_options) up --remove-orphans
|
28
43
|
source_env=set -a; [ -f .env ] && source .env; set +a
|
29
44
|
|
30
|
-
|
45
|
+
# Build base image
|
46
|
+
stoobly_exec_build=$(docker_command) build $(stoobly_exec_build_args) $(app_namespace_dir)
|
47
|
+
stoobly_exec_build_args=-f "$(dockerfile_path)" -t stoobly.$(USER_ID) --build-arg USER_ID=$(USER_ID) $(PULL_OPTION) --quiet
|
31
48
|
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
stoobly_exec_env=export CONTEXT_DIR=$(context_dir) && export USER_ID=$$UID && export CA_CERTS_DIR="$(ca_certs_dir)"
|
49
|
+
# Exec any
|
50
|
+
stoobly_exec=$(stoobly_exec_build) && $(stoobly_exec_env) && $(exec_up)
|
51
|
+
stoobly_exec_env=$(source_env) && $(exec_env) && export CONTEXT_DIR="$(context_dir)"
|
36
52
|
|
37
|
-
|
53
|
+
# Exec workflow run
|
54
|
+
# Because scaffold is stored in the application source code directory,
|
55
|
+
# when running a scaffold command from within a container,
|
56
|
+
# it needs access to $(app_dir) rather than $(context_dir)
|
57
|
+
stoobly_exec_run=$(stoobly_exec_build) && $(stoobly_exec_run_env) && $(exec_up)
|
58
|
+
stoobly_exec_run_env=$(source_env) && $(exec_env) && export CONTEXT_DIR="$(app_dir)"
|
38
59
|
|
39
|
-
#
|
40
|
-
|
41
|
-
stoobly_exec_run=$(stoobly_exec_env) && $(source_env) && $(stoobly_exec_build) && CONTEXT_DIR=$(app_dir) $(docker_compose_command) -f "$(docker_compose_file_path)" $(stoobly_exec_args)
|
42
|
-
|
43
|
-
workflow_run_script=$(app_data_dir)/tmp/run.sh
|
44
|
-
workflow_run_env=export APP_DIR="$(app_dir)" && export CERTS_DIR="$(certs_dir)" && export CONTEXT_DIR="$(context_dir)"
|
45
|
-
workflow_run=$(workflow_run_env) && $(source_env) && bash "$(workflow_run_script)"
|
60
|
+
# Workflow run
|
61
|
+
workflow_run=$(source_env) && bash "$(workflow_run_script)"
|
46
62
|
|
63
|
+
ca-cert/install: stoobly/install
|
64
|
+
@echo "Running stoobly-agent ca-cert install..."; \
|
65
|
+
stoobly-agent ca-cert install
|
47
66
|
certs:
|
48
67
|
@export EXEC_COMMAND=bin/.mkcert && \
|
49
68
|
$(stoobly_exec)
|
69
|
+
command/install:
|
70
|
+
$(eval COMMAND=install)
|
71
|
+
command/uninstall:
|
72
|
+
$(eval COMMAND=uninstall)
|
50
73
|
nameservers: tmpdir
|
51
74
|
@if [ -f /etc/resolv.conf ]; then \
|
52
75
|
nameserver=$$(grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' /etc/resolv.conf) && \
|
@@ -56,7 +79,8 @@ nameservers: tmpdir
|
|
56
79
|
fi; \
|
57
80
|
echo "$$nameserver" > $(app_tmp_dir)/.nameservers; \
|
58
81
|
else \
|
59
|
-
echo "/etc/resolv.conf not found."; \
|
82
|
+
echo "/etc/resolv.conf not found." >&2; \
|
83
|
+
exit 1; \
|
60
84
|
fi
|
61
85
|
intercept/disable:
|
62
86
|
@export EXEC_COMMAND=bin/.disable && \
|
@@ -65,42 +89,24 @@ intercept/enable:
|
|
65
89
|
@export EXEC_COMMAND=bin/.enable && \
|
66
90
|
export EXEC_ARGS=$(scenario_key) && \
|
67
91
|
$(stoobly_exec)
|
68
|
-
mock: nameservers
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
record: nameservers
|
87
|
-
@export EXEC_COMMAND=bin/.up && \
|
88
|
-
export EXEC_OPTIONS="$(workflow_options)$(options)" && \
|
89
|
-
export EXEC_ARGS="record" && \
|
90
|
-
$(stoobly_exec_run) && \
|
91
|
-
$(workflow_run)
|
92
|
-
record/logs:
|
93
|
-
@export EXEC_COMMAND=bin/.logs && \
|
94
|
-
export EXEC_OPTIONS="$(workflow_options)$(options)" && \
|
95
|
-
export EXEC_ARGS="record" && \
|
96
|
-
$(stoobly_exec_run) && \
|
97
|
-
$(workflow_run)
|
98
|
-
record/down:
|
99
|
-
@export EXEC_COMMAND=bin/.down && \
|
100
|
-
export EXEC_OPTIONS="$(workflow_options)$(options)" && \
|
101
|
-
export EXEC_ARGS="record" && \
|
102
|
-
$(stoobly_exec_run) && \
|
103
|
-
$(workflow_run)
|
92
|
+
mock: workflow/mock workflow/hostname/install nameservers workflow/up
|
93
|
+
mock/services: workflow/mock workflow/services
|
94
|
+
mock/logs: workflow/mock workflow/logs
|
95
|
+
mock/down: workflow/mock workflow/down workflow/hostname/uninstall
|
96
|
+
pipx/install:
|
97
|
+
@if ! command -v pipx >/dev/null 2>&1; then \
|
98
|
+
echo "pipx is not installed. Installing pipx..."; \
|
99
|
+
python3 -m pip install --user pipx && python3 -m pipx ensurepath; \
|
100
|
+
fi
|
101
|
+
python/validate:
|
102
|
+
@if ! python3 --version | grep -Eq 'Python 3\.(10|11|12)'; then \
|
103
|
+
echo "Error: Python 3.10, 3.11, or 3.12 is required."; \
|
104
|
+
exit 1; \
|
105
|
+
fi
|
106
|
+
record: workflow/record workflow/hostname/install nameservers workflow/up
|
107
|
+
record/down: workflow/record workflow/down workflow/hostname/uninstall
|
108
|
+
record/services: workflow/record workflow/services
|
109
|
+
record/logs: workflow/record workflow/logs
|
104
110
|
scenario/create:
|
105
111
|
# Create a scenario
|
106
112
|
@export EXEC_COMMAND=bin/.create && \
|
@@ -130,23 +136,57 @@ scenario/snapshot:
|
|
130
136
|
export EXEC_OPTIONS="$(options)" && \
|
131
137
|
export EXEC_ARGS="$(key)" && \
|
132
138
|
$(stoobly_exec)
|
133
|
-
|
134
|
-
@
|
135
|
-
|
136
|
-
|
139
|
+
stoobly/install: python/validate pipx/install
|
140
|
+
@if ! pipx list 2> /dev/null | grep -q 'stoobly-agent'; then \
|
141
|
+
echo "stoobly-agent not found. Installing..."; \
|
142
|
+
pipx install stoobly-agent; \
|
143
|
+
fi
|
144
|
+
test: workflow/test workflow/up
|
145
|
+
test/services: workflow/test workflow/services
|
146
|
+
test/logs: workflow/test workflow/logs
|
147
|
+
test/down: workflow/test workflow/down
|
148
|
+
tmpdir:
|
149
|
+
@mkdir -p $(app_tmp_dir)
|
150
|
+
workflow/down:
|
151
|
+
@export EXEC_COMMAND=bin/.down && \
|
152
|
+
export EXEC_OPTIONS="$(workflow_down_options) $(workflow_service_options) $(options)" && \
|
153
|
+
export EXEC_ARGS="$(WORKFLOW)" && \
|
137
154
|
$(stoobly_exec_run) && \
|
138
155
|
$(workflow_run)
|
139
|
-
|
156
|
+
workflow/hostname: stoobly/install
|
157
|
+
@read -p "Do you want to $(COMMAND) hostname(s) in /etc/hosts? (y/N) " confirm && \
|
158
|
+
if [ "$$confirm" = "y" ] || [ "$$confirm" = "Y" ]; then \
|
159
|
+
CURRENT_VERSION=$$(stoobly-agent --version); \
|
160
|
+
REQUIRED_VERSION="1.4.0"; \
|
161
|
+
if [ "$$(printf '%s\n' "$$REQUIRED_VERSION" "$$CURRENT_VERSION" | sort -V | tail -n 1)" = "$$CURRENT_VERSION" ]; then \
|
162
|
+
echo "stoobly-agent version $$REQUIRED_VERSION required. Please run: pipx upgrade stoobly-agent"; \
|
163
|
+
exit 1; \
|
164
|
+
fi; \
|
165
|
+
echo "Running stoobly-agent scaffold hostname $(COMMAND)..."; \
|
166
|
+
stoobly-agent scaffold hostname $(COMMAND) --app-dir-path $(app_dir) --workflow $(WORKFLOW); \
|
167
|
+
fi
|
168
|
+
workflow/hostname/install: command/install workflow/hostname
|
169
|
+
workflow/hostname/uninstall: command/uninstall workflow/hostname
|
170
|
+
workflow/logs:
|
140
171
|
@export EXEC_COMMAND=bin/.logs && \
|
141
|
-
export EXEC_OPTIONS="$(
|
142
|
-
export EXEC_ARGS="
|
172
|
+
export EXEC_OPTIONS="$(workflow_service_options) $(options)" && \
|
173
|
+
export EXEC_ARGS="$(WORKFLOW)" && \
|
143
174
|
$(stoobly_exec_run) && \
|
144
175
|
$(workflow_run)
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
176
|
+
workflow/mock:
|
177
|
+
$(eval WORKFLOW=mock)
|
178
|
+
workflow/record:
|
179
|
+
$(eval WORKFLOW=record)
|
180
|
+
workflow/services:
|
181
|
+
@export EXEC_COMMAND=bin/.services && \
|
182
|
+
export EXEC_OPTIONS="$(workflow_service_options) $(options)" && \
|
183
|
+
export EXEC_ARGS="$(WORKFLOW)" && \
|
184
|
+
$(stoobly_exec_run)
|
185
|
+
workflow/test:
|
186
|
+
$(eval WORKFLOW=test)
|
187
|
+
workflow/up:
|
188
|
+
@export EXEC_COMMAND=bin/.up && \
|
189
|
+
export EXEC_OPTIONS="$(workflow_up_options) $(workflow_service_options) $(options)" && \
|
190
|
+
export EXEC_ARGS="$(WORKFLOW)" && \
|
149
191
|
$(stoobly_exec_run) && \
|
150
|
-
$(workflow_run)
|
151
|
-
tmpdir:
|
152
|
-
@mkdir -p $(app_tmp_dir)
|
192
|
+
$(workflow_run)
|
@@ -1,18 +1,13 @@
|
|
1
1
|
services:
|
2
|
-
ui_base:
|
3
|
-
extends:
|
4
|
-
service: context_base
|
5
|
-
volumes:
|
6
|
-
- ${CA_CERTS_DIR}:/home/stoobly/.mitmproxy
|
7
2
|
context_base:
|
8
|
-
|
9
|
-
|
10
|
-
USER_ID: ${USER_ID}
|
11
|
-
dockerfile: ./.Dockerfile.context
|
3
|
+
extends:
|
4
|
+
service: stoobly_base
|
12
5
|
volumes:
|
13
6
|
- ${CONTEXT_DIR}/.stoobly:/home/stoobly/.stoobly
|
14
|
-
|
15
|
-
|
16
|
-
|
7
|
+
stoobly_base:
|
8
|
+
image: stoobly.${USER_ID}
|
9
|
+
ui_base:
|
17
10
|
extends:
|
18
|
-
service: context_base
|
11
|
+
service: context_base
|
12
|
+
volumes:
|
13
|
+
- ${CA_CERTS_DIR}:/home/stoobly/.mitmproxy
|
@@ -1,11 +1,15 @@
|
|
1
1
|
services:
|
2
2
|
build.init_base:
|
3
|
-
command:
|
3
|
+
command:
|
4
|
+
- bin/.init
|
4
5
|
extends:
|
5
6
|
file: ../.docker-compose.base.yml
|
6
|
-
service:
|
7
|
+
service: context_base
|
8
|
+
working_dir: /home/stoobly/.stoobly/docker/build/${WORKFLOW_NAME}
|
7
9
|
build.configure_base:
|
8
|
-
command:
|
10
|
+
command:
|
11
|
+
- bin/.configure
|
9
12
|
extends:
|
10
13
|
file: ../.docker-compose.base.yml
|
11
|
-
service:
|
14
|
+
service: context_base
|
15
|
+
working_dir: /home/stoobly/.stoobly/docker/build/${WORKFLOW_NAME}
|
@@ -1,22 +1,18 @@
|
|
1
1
|
services:
|
2
2
|
build.init:
|
3
|
-
build:
|
4
|
-
context: ./
|
5
|
-
dockerfile: ../../.Dockerfile.context
|
6
3
|
extends:
|
7
4
|
file: ../.docker-compose.base.yml
|
8
5
|
service: build.init_base
|
6
|
+
image: stoobly.${USER_ID}
|
9
7
|
profiles:
|
10
8
|
- mock
|
11
9
|
build.configure:
|
12
|
-
build:
|
13
|
-
context: ./
|
14
|
-
dockerfile: ../../.Dockerfile.context
|
15
10
|
depends_on:
|
16
11
|
build.init:
|
17
12
|
condition: service_completed_successfully
|
18
13
|
extends:
|
19
14
|
file: ../.docker-compose.base.yml
|
20
15
|
service: build.configure_base
|
16
|
+
image: stoobly.${USER_ID}
|
21
17
|
profiles:
|
22
18
|
- mock
|
@@ -1,22 +1,18 @@
|
|
1
1
|
services:
|
2
2
|
build.init:
|
3
|
-
build:
|
4
|
-
context: ./
|
5
|
-
dockerfile: ../../.Dockerfile.context
|
6
3
|
extends:
|
7
4
|
file: ../.docker-compose.base.yml
|
8
5
|
service: build.init_base
|
6
|
+
image: stoobly.${USER_ID}
|
9
7
|
profiles:
|
10
8
|
- record
|
11
9
|
build.configure:
|
12
|
-
build:
|
13
|
-
context: ./
|
14
|
-
dockerfile: ../../.Dockerfile.context
|
15
10
|
depends_on:
|
16
11
|
build.init:
|
17
12
|
condition: service_completed_successfully
|
18
13
|
extends:
|
19
14
|
file: ../.docker-compose.base.yml
|
20
15
|
service: build.configure_base
|
16
|
+
image: stoobly.${USER_ID}
|
21
17
|
profiles:
|
22
18
|
- record
|