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
stoobly_agent/__init__.py
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
COMMAND = 'stoobly-agent'
|
2
|
-
VERSION = '1.
|
2
|
+
VERSION = '1.4.0'
|
@@ -7,7 +7,7 @@ from mitmproxy.coretypes.multidict import MultiDict
|
|
7
7
|
from urllib.parse import urlparse, parse_qs
|
8
8
|
|
9
9
|
from stoobly_agent.app.proxy.replay.body_parser_service import decode_response
|
10
|
-
from stoobly_agent.config.constants import headers
|
10
|
+
from stoobly_agent.config.constants import custom_headers, headers
|
11
11
|
|
12
12
|
from .routes import ROUTES
|
13
13
|
from .simple_http_request_handler import SimpleHTTPRequestHandler
|
@@ -102,11 +102,11 @@ class ApplicationHTTPRequestHandler(SimpleHTTPRequestHandler):
|
|
102
102
|
'Content-Type',
|
103
103
|
headers.ACCESS_TOKEN.title(),
|
104
104
|
headers.CLIENT.title(),
|
105
|
-
|
105
|
+
custom_headers.DO_PROXY.title(),
|
106
106
|
headers.EXPIRY.title(),
|
107
107
|
headers.PROXY_HEADERS.title(),
|
108
108
|
headers.REQUEST_PATH.title(),
|
109
|
-
|
109
|
+
custom_headers.SERVICE_URL.title(),
|
110
110
|
headers.TOKEN_TYPE.title(),
|
111
111
|
headers.UID.title(),
|
112
112
|
])
|
@@ -3,7 +3,8 @@ import requests
|
|
3
3
|
from http.cookies import SimpleCookie
|
4
4
|
from urllib3.exceptions import InsecureRequestWarning
|
5
5
|
|
6
|
-
from stoobly_agent.
|
6
|
+
from stoobly_agent.app.api.simple_http_request_handler import SimpleHTTPRequestHandler
|
7
|
+
from stoobly_agent.config.constants import custom_headers, headers
|
7
8
|
from stoobly_agent.config.mitmproxy import MitmproxyConfig
|
8
9
|
from stoobly_agent.lib.logger import Logger
|
9
10
|
|
@@ -46,7 +47,7 @@ class ProxyController:
|
|
46
47
|
def do_PUT(self, context):
|
47
48
|
self.__proxy(context, requests.put)
|
48
49
|
|
49
|
-
def __proxy(self, context, method):
|
50
|
+
def __proxy(self, context: SimpleHTTPRequestHandler, method):
|
50
51
|
url = self.__get_url(context)
|
51
52
|
|
52
53
|
if url:
|
@@ -110,7 +111,7 @@ class ProxyController:
|
|
110
111
|
status = status,
|
111
112
|
)
|
112
113
|
|
113
|
-
def __get_headers(self, context):
|
114
|
+
def __get_headers(self, context: SimpleHTTPRequestHandler):
|
114
115
|
request_headers = dict(context.headers)
|
115
116
|
|
116
117
|
headers_white_list = []
|
@@ -125,12 +126,12 @@ class ProxyController:
|
|
125
126
|
|
126
127
|
return white_listed_headers
|
127
128
|
|
128
|
-
def __get_url(self, context):
|
129
|
-
service_url = context.headers.get(
|
129
|
+
def __get_url(self, context: SimpleHTTPRequestHandler):
|
130
|
+
service_url = context.headers.get(custom_headers.SERVICE_URL)
|
130
131
|
|
131
132
|
if not service_url:
|
132
133
|
context.render(
|
133
|
-
plain = f"Invalid {
|
134
|
+
plain = f"Invalid {custom_headers.SERVICE_URL} header {service_url}",
|
134
135
|
status = 400
|
135
136
|
)
|
136
137
|
return None
|
@@ -146,7 +147,7 @@ class ProxyController:
|
|
146
147
|
|
147
148
|
return f"{service_url}{request_path}"
|
148
149
|
|
149
|
-
def __get_cookies(self, context):
|
150
|
+
def __get_cookies(self, context: SimpleHTTPRequestHandler):
|
150
151
|
return SimpleCookie(context.headers.get('Cookie'))
|
151
152
|
|
152
153
|
def __get_body(self, context):
|
@@ -180,7 +180,7 @@ def rewrite(ctx):
|
|
180
180
|
@click.option('--project-key', help='Project to add rewrite rule to.')
|
181
181
|
@click.option(
|
182
182
|
'--type',
|
183
|
-
type=click.Choice([request_component.BODY_PARAM, request_component.HEADER, request_component.QUERY_PARAM]),
|
183
|
+
type=click.Choice([request_component.BODY_PARAM, request_component.HEADER, request_component.QUERY_PARAM, request_component.RESPONSE_HEADER, request_component.RESPONSE_PARAM]),
|
184
184
|
help='Request component type.'
|
185
185
|
)
|
186
186
|
@click.option(
|
@@ -23,10 +23,6 @@ class CertificateAuthority():
|
|
23
23
|
|
24
24
|
def __init__(self, certs_dir = DataDir.instance().mitmproxy_conf_dir_path, cn = MITMPROXY_CN):
|
25
25
|
self.certs_dir = certs_dir
|
26
|
-
|
27
|
-
if not os.path.exists(certs_dir):
|
28
|
-
self.generate_certs()
|
29
|
-
|
30
26
|
self.cn = cn
|
31
27
|
self.key_size = 2048
|
32
28
|
|
@@ -98,6 +94,9 @@ class CertificateAuthority():
|
|
98
94
|
subprocess.run("sudo update-ca-trust extract".split(), check=True)
|
99
95
|
|
100
96
|
def install(self):
|
97
|
+
if not self.certs_generated:
|
98
|
+
self.generate_certs()
|
99
|
+
|
101
100
|
distro_name = distro.name(pretty=True)
|
102
101
|
|
103
102
|
# Ubuntu or other Debian based
|
@@ -109,7 +108,9 @@ class CertificateAuthority():
|
|
109
108
|
# elif distro.id() == 'rhel':
|
110
109
|
# return
|
111
110
|
else:
|
112
|
-
raise TypeError(
|
111
|
+
raise TypeError(
|
112
|
+
f"{distro_name} is not supported yet for automatic installation. For manual installation, see https://docs.mitmproxy.org/stable/concepts-certificates/"
|
113
|
+
)
|
113
114
|
|
114
115
|
def sign(self, hostname: str, dest = None, port = 443):
|
115
116
|
dest = dest or self.certs_dir
|
@@ -201,5 +202,5 @@ class CertificateAuthority():
|
|
201
202
|
return org_name, alt_names
|
202
203
|
|
203
204
|
except Exception as e:
|
204
|
-
Logger.instance(LOG_ID).
|
205
|
+
Logger.instance(LOG_ID).debug(f"Could not retrieve certificate for {domain}: {e}")
|
205
206
|
return None, alt_names
|
@@ -89,6 +89,23 @@ def print_scenarios(scenarios, **kwargs: TabulatePrintOptions):
|
|
89
89
|
select=kwargs.get('select') or []
|
90
90
|
)
|
91
91
|
|
92
|
+
def print_services(services, **kwargs: TabulatePrintOptions):
|
93
|
+
filter = []
|
94
|
+
format = kwargs.get('format')
|
95
|
+
|
96
|
+
if format == JSON_FORMAT:
|
97
|
+
json_print(services, **{
|
98
|
+
'filter': filter,
|
99
|
+
**kwargs
|
100
|
+
})
|
101
|
+
else:
|
102
|
+
tabulate_print(
|
103
|
+
services,
|
104
|
+
filter=filter,
|
105
|
+
headers=not kwargs.get('without_headers'),
|
106
|
+
select=kwargs.get('select') or []
|
107
|
+
)
|
108
|
+
|
92
109
|
def print_snapshots(snapshots, **kwargs: TabulatePrintOptions):
|
93
110
|
filter = ['resource_uuid']
|
94
111
|
format = kwargs.get('format')
|
@@ -5,20 +5,18 @@ from stoobly_agent.config.data_dir import DataDir, DATA_DIR_NAME
|
|
5
5
|
|
6
6
|
class App():
|
7
7
|
|
8
|
-
def __init__(self, path: str,
|
8
|
+
def __init__(self, path: str, scaffold_namespace: str, **kwargs):
|
9
9
|
path = os.path.abspath(path) or os.getcwd()
|
10
10
|
data_dir: DataDir = DataDir.instance(path)
|
11
11
|
|
12
|
-
self.
|
12
|
+
self.__data_dir_path = data_dir.path
|
13
13
|
self.__ca_certs_dir_path = kwargs.get('ca_certs_dir_path') or data_dir.mitmproxy_conf_dir_path
|
14
|
-
self.__certs_dir_path = data_dir.certs_dir_path
|
15
|
-
self.__context_dir_path = data_dir.context_dir_path
|
14
|
+
self.__certs_dir_path = kwargs.get('certs_dir_path') or data_dir.certs_dir_path
|
15
|
+
self.__context_dir_path = kwargs.get('context_dir_path') or data_dir.context_dir_path
|
16
16
|
self.__data_dir = data_dir
|
17
17
|
self.__dir_path = path
|
18
|
-
self.
|
19
|
-
self.
|
20
|
-
self.__namespace = namespace
|
21
|
-
self.__skip_validate_path = not not kwargs.get('skip_validate_path')
|
18
|
+
self.__scaffold_namespace = scaffold_namespace
|
19
|
+
self.__skip_validate_path = not not kwargs.get('dry_run')
|
22
20
|
|
23
21
|
@property
|
24
22
|
def ca_certs_dir_path(self):
|
@@ -56,44 +54,28 @@ class App():
|
|
56
54
|
return os.path.join(self.context_dir_path, DATA_DIR_NAME)
|
57
55
|
|
58
56
|
@property
|
59
|
-
def
|
60
|
-
return os.path.exists(self.
|
57
|
+
def valid(self):
|
58
|
+
return os.path.exists(self.scaffold_namespace_path)
|
61
59
|
|
62
60
|
@property
|
63
|
-
def
|
64
|
-
return self.
|
65
|
-
|
66
|
-
@name.setter
|
67
|
-
def name(self, v: str):
|
68
|
-
self.__name = v
|
69
|
-
|
70
|
-
@property
|
71
|
-
def network(self):
|
72
|
-
return self.__network
|
73
|
-
|
74
|
-
@network.setter
|
75
|
-
def network(self, v: str):
|
76
|
-
self.__network = v
|
61
|
+
def scaffold_namespace(self):
|
62
|
+
return self.__scaffold_namespace
|
77
63
|
|
78
64
|
@property
|
79
|
-
def
|
80
|
-
return self.
|
81
|
-
|
82
|
-
@property
|
83
|
-
def namespace_path(self):
|
84
|
-
return os.path.join(self.data_dir_path, self.namespace)
|
65
|
+
def scaffold_namespace_path(self):
|
66
|
+
return os.path.join(self.data_dir_path, self.scaffold_namespace)
|
85
67
|
|
86
68
|
@property
|
87
69
|
def dir_path(self):
|
88
70
|
return self.__dir_path
|
89
71
|
|
90
72
|
@property
|
91
|
-
def
|
92
|
-
return self.
|
73
|
+
def data_dir_path(self):
|
74
|
+
return self.__data_dir_path
|
93
75
|
|
94
76
|
@property
|
95
77
|
def scaffold_namespace_path(self):
|
96
|
-
return os.path.join(self.
|
78
|
+
return os.path.join(self.data_dir_path, self.scaffold_namespace)
|
97
79
|
|
98
80
|
@property
|
99
81
|
def services(self):
|
@@ -101,7 +83,7 @@ class App():
|
|
101
83
|
|
102
84
|
@property
|
103
85
|
def service_paths(self):
|
104
|
-
services_dir = os.path.join(self.
|
86
|
+
services_dir = os.path.join(self.data_dir_path, self.scaffold_namespace)
|
105
87
|
|
106
88
|
services = []
|
107
89
|
for filename in os.listdir(services_dir):
|
@@ -13,9 +13,6 @@ class AppCommand(Command):
|
|
13
13
|
|
14
14
|
self.__config = AppConfig(self.scaffold_namespace_path)
|
15
15
|
|
16
|
-
if not self.__config.network:
|
17
|
-
self.__config.network = app.network
|
18
|
-
|
19
16
|
@property
|
20
17
|
def app_dir_path(self):
|
21
18
|
return self.app.dir_path
|
@@ -33,16 +30,16 @@ class AppCommand(Command):
|
|
33
30
|
return self.__config.path
|
34
31
|
|
35
32
|
@property
|
36
|
-
def
|
37
|
-
return self.app.
|
33
|
+
def scaffold_namespace_path(self):
|
34
|
+
return self.app.scaffold_namespace_path
|
38
35
|
|
39
36
|
@property
|
40
37
|
def app_templates_root_dir(self):
|
41
38
|
return os.path.join(self.templates_root_dir, 'app')
|
42
39
|
|
43
40
|
@property
|
44
|
-
def
|
45
|
-
return self.app.
|
41
|
+
def data_dir_path(self):
|
42
|
+
return self.app.data_dir_path
|
46
43
|
|
47
44
|
@property
|
48
45
|
def scaffold_namespace_path(self):
|
@@ -1,15 +1,24 @@
|
|
1
1
|
from .config import Config
|
2
|
-
from .constants import APP_NETWORK_ENV
|
2
|
+
from .constants import APP_NAME_ENV, APP_NETWORK_ENV
|
3
3
|
|
4
4
|
class AppConfig(Config):
|
5
5
|
|
6
6
|
def __init__(self, dir: str):
|
7
7
|
super().__init__(dir)
|
8
8
|
|
9
|
+
self.__name = None
|
9
10
|
self.__network = None
|
10
11
|
|
11
12
|
self.load()
|
12
13
|
|
14
|
+
@property
|
15
|
+
def name(self):
|
16
|
+
return self.__name
|
17
|
+
|
18
|
+
@name.setter
|
19
|
+
def name(self, v):
|
20
|
+
self.__name = v
|
21
|
+
|
13
22
|
@property
|
14
23
|
def network(self):
|
15
24
|
return self.__network
|
@@ -21,11 +30,15 @@ class AppConfig(Config):
|
|
21
30
|
def load(self, config = None):
|
22
31
|
config = config or self.read()
|
23
32
|
|
24
|
-
self.
|
33
|
+
self.name = config.get(APP_NAME_ENV)
|
34
|
+
self.network = config.get(APP_NETWORK_ENV)
|
25
35
|
|
26
36
|
def write(self):
|
27
37
|
config = {}
|
28
38
|
|
39
|
+
if self.name:
|
40
|
+
config[APP_NAME_ENV] = self.name
|
41
|
+
|
29
42
|
if self.network:
|
30
43
|
config[APP_NETWORK_ENV] = self.network
|
31
44
|
|
@@ -1,17 +1,33 @@
|
|
1
1
|
import os
|
2
2
|
import pdb
|
3
3
|
|
4
|
+
from typing import TypedDict
|
5
|
+
|
4
6
|
from .app import App
|
5
7
|
from .app_command import AppCommand
|
6
8
|
|
9
|
+
class AppCreateOptions(TypedDict):
|
10
|
+
name: str
|
11
|
+
network: str
|
12
|
+
|
7
13
|
class AppCreateCommand(AppCommand):
|
8
14
|
|
9
|
-
def __init__(self, app: App, **kwargs):
|
15
|
+
def __init__(self, app: App, **kwargs: AppCreateOptions):
|
10
16
|
super().__init__(app)
|
11
17
|
|
18
|
+
if kwargs.get('app_name'):
|
19
|
+
self.app_config.name = kwargs['app_name']
|
20
|
+
|
21
|
+
if kwargs.get('network'):
|
22
|
+
self.app_config.network = kwargs['network']
|
23
|
+
|
12
24
|
@property
|
13
25
|
def app_name(self):
|
14
|
-
return self.
|
26
|
+
return self.app_config.name
|
27
|
+
|
28
|
+
@property
|
29
|
+
def app_network(self):
|
30
|
+
return self.app_config.network
|
15
31
|
|
16
32
|
def build(self):
|
17
33
|
dest = self.scaffold_namespace_path
|
@@ -1,9 +1,11 @@
|
|
1
|
-
|
1
|
+
import os
|
2
2
|
|
3
|
-
from
|
3
|
+
from typing import Literal
|
4
4
|
|
5
|
+
from stoobly_agent.config.data_dir import CERTS_DIR_NAME, DATA_DIR_NAME
|
5
6
|
|
6
7
|
APP_NETWORK_ENV = 'APP_NETWORK'
|
8
|
+
APP_NAME_ENV = 'APP_NAME'
|
7
9
|
BIN_FOLDER_NAME = 'bin'
|
8
10
|
CA_CERTS_DIR_ENV = 'CA_CERTS_DIR'
|
9
11
|
CERTS_DIR_ENV = 'CERTS_DIR'
|
@@ -16,12 +18,11 @@ FIXTURES_FOLDER_NAME = 'fixtures'
|
|
16
18
|
NAMESERVERS_FILE = '.nameservers'
|
17
19
|
SERVICE_DETACHED = '${SERVICE_DETACHED}'
|
18
20
|
SERVICE_DETACHED_ENV = 'SERVICE_DETACHED'
|
19
|
-
SERVICE_DOCKER_COMPOSE_PATH = '${SERVICE_DOCKER_COMPOSE_PATH}'
|
20
|
-
SERVICE_DOCKER_COMPOSE_PATH_ENV = 'SERVICE_DOCKER_COMPOSE_PATH'
|
21
21
|
SERVICE_HOSTNAME = '${SERVICE_HOSTNAME}'
|
22
22
|
SERVICE_HOSTNAME_ENV = 'SERVICE_HOSTNAME'
|
23
23
|
SERVICE_DNS = '${SERVICE_DNS}'
|
24
24
|
SERVICE_DNS_ENV = 'SERVICE_DNS'
|
25
|
+
SERVICE_NAME = '${SERVICE_NAME}'
|
25
26
|
SERVICE_NAME_ENV = 'SERVICE_NAME'
|
26
27
|
SERVICE_PROXY_MODE = '${SERVICE_PROXY_MODE}'
|
27
28
|
SERVICE_PROXY_MODE_ENV = 'SERVICE_PROXY_MODE'
|
@@ -31,7 +32,8 @@ SERVICE_PORT = '${SERVICE_PORT}'
|
|
31
32
|
SERVICE_PORT_ENV = 'SERVICE_PORT'
|
32
33
|
SERVICE_PRIORITY_ENV = 'SERVICE_PRIORITY'
|
33
34
|
STOOBLY_HOME_DIR = '/home/stoobly'
|
34
|
-
STOOBLY_DATA_DIR =
|
35
|
+
STOOBLY_DATA_DIR = os.path.join(STOOBLY_HOME_DIR, DATA_DIR_NAME)
|
36
|
+
STOOBLY_CERTS_DIR = os.path.join(STOOBLY_DATA_DIR, CERTS_DIR_NAME)
|
35
37
|
USER_ID_ENV = 'USER_ID'
|
36
38
|
VIRTUAL_HOST_ENV = 'VIRTUAL_HOST'
|
37
39
|
VIRTUAL_PORT_ENV = 'VIRTUAL_PORT'
|
@@ -44,7 +46,9 @@ WORKFLOW_CONTAINER_CONFIGURE_TEMPLATE = '{service_name}.' + WORKFLOW_CONTAINER_C
|
|
44
46
|
WORKFLOW_CONTAINER_INIT_TEMPLATE = '{service_name}.' + WORKFLOW_CONTAINER_INIT
|
45
47
|
WORKFLOW_CONTAINER_PROXY_TEMPLATE = '{service_name}.' + WORKFLOW_CONTAINER_PROXY
|
46
48
|
WORKFLOW_MOCK_TYPE = 'mock'
|
49
|
+
WORKFLOW_NAME = '${WORKFLOW_NAME}'
|
47
50
|
WORKFLOW_NAME_ENV = 'WORKFLOW_NAME'
|
51
|
+
WORKFLOW_NAMESPACE_ENV = 'WORKFLOW_NAMESPACE'
|
48
52
|
WORKFLOW_RECORD_TYPE = 'record'
|
49
53
|
WORKFLOW_TEST_TYPE = 'test'
|
50
54
|
|
@@ -1,7 +1,7 @@
|
|
1
1
|
import os
|
2
2
|
|
3
3
|
from .builder import Builder
|
4
|
-
from .constants import DOCKER_COMPOSE_BASE, DOCKERFILE_CONTEXT
|
4
|
+
from .constants import DOCKER_COMPOSE_BASE, DOCKERFILE_CONTEXT
|
5
5
|
from ..app_config import AppConfig
|
6
6
|
|
7
7
|
class AppBuilder(Builder):
|
@@ -18,9 +18,5 @@ class AppBuilder(Builder):
|
|
18
18
|
return os.path.join(self.dir_path, DOCKERFILE_CONTEXT)
|
19
19
|
|
20
20
|
@property
|
21
|
-
def
|
22
|
-
return '
|
23
|
-
|
24
|
-
@property
|
25
|
-
def proxy_docker_file_path(self):
|
26
|
-
return os.path.join(self.dir_path, DOCKERFILE_PROXY)
|
21
|
+
def stoobly_base(self):
|
22
|
+
return 'stoobly_base'
|
@@ -3,7 +3,6 @@ DOCKER_COMPOSE_BASE = '.docker-compose.base.yml'
|
|
3
3
|
DOCKER_COMPOSE_BASE_TEMPLATE = '.docker-compose.base.template.yml'
|
4
4
|
DOCKER_COMPOSE_CUSTOM = 'docker-compose.yml'
|
5
5
|
DOCKERFILE_CONTEXT = '.Dockerfile.context'
|
6
|
-
DOCKERFILE_PROXY = '.Dockerfile.proxy'
|
7
6
|
DOCKERFILE_SERVICE = 'Dockerfile.source'
|
8
7
|
GATEWAY_NETWORK = 'gateway'
|
9
8
|
|
@@ -2,7 +2,7 @@ import os
|
|
2
2
|
import pdb
|
3
3
|
|
4
4
|
from ...app_config import AppConfig
|
5
|
-
from ...constants import SERVICE_HOSTNAME, SERVICE_HOSTNAME_ENV, STOOBLY_HOME_DIR
|
5
|
+
from ...constants import SERVICE_HOSTNAME, SERVICE_HOSTNAME_ENV, SERVICE_NAME, STOOBLY_HOME_DIR, WORKFLOW_NAME
|
6
6
|
from ...service_config import ServiceConfig
|
7
7
|
from ..app_builder import AppBuilder
|
8
8
|
from ..builder import Builder
|
@@ -44,6 +44,13 @@ class ServiceBuilder(Builder):
|
|
44
44
|
def configure_base_service(self):
|
45
45
|
return self.services.get(self.configure_base)
|
46
46
|
|
47
|
+
@property
|
48
|
+
def extends_service(self):
|
49
|
+
if self.config.detached:
|
50
|
+
return self.app_builder.stoobly_base
|
51
|
+
else:
|
52
|
+
return self.app_builder.context_base
|
53
|
+
|
47
54
|
@property
|
48
55
|
def proxy_base(self):
|
49
56
|
return f"{self.service_name}.proxy_base"
|
@@ -52,10 +59,6 @@ class ServiceBuilder(Builder):
|
|
52
59
|
def proxy_base_service(self):
|
53
60
|
return self.services.get(self.proxy_base)
|
54
61
|
|
55
|
-
@property
|
56
|
-
def service_mount(self):
|
57
|
-
return f".:{STOOBLY_HOME_DIR}"
|
58
|
-
|
59
62
|
@property
|
60
63
|
def service_name(self):
|
61
64
|
return self.__service_name
|
@@ -80,9 +83,8 @@ class ServiceBuilder(Builder):
|
|
80
83
|
},
|
81
84
|
'extends': {
|
82
85
|
'file': os.path.relpath(self.app_builder.compose_file_path, self.dir_path),
|
83
|
-
'service': self.
|
86
|
+
'service': self.extends_service
|
84
87
|
},
|
85
|
-
'volumes': [self.service_mount]
|
86
88
|
})
|
87
89
|
|
88
90
|
args[SERVICE_HOSTNAME_ENV] = f"{SERVICE_HOSTNAME}"
|
@@ -90,13 +92,12 @@ class ServiceBuilder(Builder):
|
|
90
92
|
def build_init_base(self):
|
91
93
|
environment = {}
|
92
94
|
self.with_service(self.init_base, {
|
93
|
-
'command': ['bin/.init'
|
95
|
+
'command': ['bin/.init'],
|
94
96
|
'environment': environment,
|
95
97
|
'extends': {
|
96
98
|
'file': os.path.relpath(self.app_builder.compose_file_path, self.dir_path),
|
97
|
-
'service': self.
|
99
|
+
'service': self.extends_service
|
98
100
|
},
|
99
|
-
'volumes': [self.service_mount]
|
100
101
|
})
|
101
102
|
|
102
103
|
def build_configure_base(self):
|
@@ -106,7 +107,7 @@ class ServiceBuilder(Builder):
|
|
106
107
|
'environment': environment,
|
107
108
|
'extends': {
|
108
109
|
'file': os.path.relpath(self.app_builder.compose_file_path, self.dir_path),
|
109
|
-
'service': self.
|
110
|
+
'service': self.extends_service
|
110
111
|
}
|
111
112
|
})
|
112
113
|
|
@@ -3,12 +3,13 @@ import pdb
|
|
3
3
|
|
4
4
|
from typing import List
|
5
5
|
|
6
|
+
from stoobly_agent.config.data_dir import DATA_DIR_NAME
|
7
|
+
|
6
8
|
from ...constants import (
|
7
|
-
COMPOSE_TEMPLATE, STOOBLY_HOME_DIR, SERVICE_HOSTNAME, SERVICE_HOSTNAME_ENV, SERVICE_NAME_ENV,
|
9
|
+
COMPOSE_TEMPLATE, DOCKER_NAMESPACE, STOOBLY_HOME_DIR, SERVICE_HOSTNAME, SERVICE_HOSTNAME_ENV, SERVICE_NAME_ENV,
|
8
10
|
SERVICE_PORT, SERVICE_PORT_ENV, SERVICE_SCHEME, SERVICE_SCHEME_ENV,
|
9
11
|
WORKFLOW_CONTAINER_CONFIGURE_TEMPLATE, WORKFLOW_CONTAINER_INIT_TEMPLATE, WORKFLOW_CONTAINER_PROXY_TEMPLATE, WORKFLOW_NAME_ENV
|
10
12
|
)
|
11
|
-
from ...templates.constants import SERVICE_HOSTNAME_BUILD_ARG
|
12
13
|
from ..builder import Builder
|
13
14
|
from ..service.builder import ServiceBuilder
|
14
15
|
|
@@ -21,13 +22,15 @@ class WorkflowBuilder(Builder):
|
|
21
22
|
|
22
23
|
self.__context = '../'
|
23
24
|
self.__profiles = [self.__workflow_name]
|
24
|
-
self.__workdir = os.path.join(STOOBLY_HOME_DIR, self.workflow_name)
|
25
25
|
|
26
26
|
if not service_builder:
|
27
27
|
service_path = os.path.dirname(workflow_path)
|
28
28
|
service_builder = ServiceBuilder(service_path)
|
29
29
|
|
30
30
|
self.__service_builder = service_builder
|
31
|
+
self.__working_dir = os.path.join(
|
32
|
+
STOOBLY_HOME_DIR, DATA_DIR_NAME, DOCKER_NAMESPACE, self.service_builder.service_name, self.workflow_name
|
33
|
+
)
|
31
34
|
|
32
35
|
if self.config.hostname:
|
33
36
|
self.with_public_network()
|
@@ -58,13 +61,6 @@ class WorkflowBuilder(Builder):
|
|
58
61
|
def context(self):
|
59
62
|
return self.__context
|
60
63
|
|
61
|
-
@property
|
62
|
-
def context_build(self):
|
63
|
-
return {
|
64
|
-
'context': self.context,
|
65
|
-
'dockerfile': self.context_docker_file_path,
|
66
|
-
}
|
67
|
-
|
68
64
|
@property
|
69
65
|
def context_docker_file_path(self):
|
70
66
|
return os.path.relpath(self.service_builder.app_builder.context_docker_file_path, self.service_path)
|
@@ -81,21 +77,6 @@ class WorkflowBuilder(Builder):
|
|
81
77
|
def proxy(self):
|
82
78
|
return WORKFLOW_CONTAINER_PROXY_TEMPLATE.format(service_name=self.namespace)
|
83
79
|
|
84
|
-
@property
|
85
|
-
def proxy_build(self):
|
86
|
-
args = {}
|
87
|
-
args[SERVICE_HOSTNAME_BUILD_ARG] = SERVICE_HOSTNAME
|
88
|
-
|
89
|
-
return {
|
90
|
-
'args': args,
|
91
|
-
'context': self.context,
|
92
|
-
'dockerfile': self.proxy_docker_file_path,
|
93
|
-
}
|
94
|
-
|
95
|
-
@property
|
96
|
-
def proxy_docker_file_path(self):
|
97
|
-
return os.path.relpath(self.service_builder.app_builder.proxy_docker_file_path, self.service_path)
|
98
|
-
|
99
80
|
@property
|
100
81
|
def service_builder(self):
|
101
82
|
return self.__service_builder
|
@@ -128,19 +109,21 @@ class WorkflowBuilder(Builder):
|
|
128
109
|
volumes = []
|
129
110
|
|
130
111
|
service = {
|
131
|
-
'build': self.context_build,
|
132
112
|
'environment': environment,
|
133
113
|
'extends': self.service_builder.build_extends_init_base(self.dir_path),
|
134
114
|
'profiles': self.profiles,
|
135
115
|
'volumes': volumes,
|
136
|
-
'working_dir': self.
|
116
|
+
'working_dir': self.__working_dir,
|
137
117
|
}
|
138
118
|
|
139
119
|
if self.config.hostname:
|
140
120
|
self.__with_url_environment(environment)
|
141
121
|
|
142
122
|
if self.config.detached:
|
123
|
+
# Mount named volume
|
143
124
|
volumes.append(f"{self.service_builder.service_name}:{STOOBLY_HOME_DIR}/.stoobly")
|
125
|
+
# Mount docker folder
|
126
|
+
volumes.append(f"../..:{STOOBLY_HOME_DIR}/.stoobly/docker")
|
144
127
|
|
145
128
|
self.with_service(self.init, service)
|
146
129
|
|
@@ -154,13 +137,12 @@ class WorkflowBuilder(Builder):
|
|
154
137
|
volumes = []
|
155
138
|
|
156
139
|
service = {
|
157
|
-
'build': self.context_build,
|
158
140
|
'depends_on': depends_on,
|
159
141
|
'environment': environment,
|
160
142
|
'extends': self.service_builder.build_extends_configure_base(self.dir_path),
|
161
143
|
'profiles': self.profiles,
|
162
144
|
'volumes': volumes,
|
163
|
-
'working_dir': self.
|
145
|
+
'working_dir': self.__working_dir,
|
164
146
|
}
|
165
147
|
|
166
148
|
if self.config.hostname:
|
@@ -173,6 +155,7 @@ class WorkflowBuilder(Builder):
|
|
173
155
|
|
174
156
|
if self.config.detached:
|
175
157
|
volumes.append(f"{self.service_builder.service_name}:{STOOBLY_HOME_DIR}/.stoobly")
|
158
|
+
volumes.append(f"../..:{STOOBLY_HOME_DIR}/.stoobly/docker")
|
176
159
|
|
177
160
|
self.with_service(self.configure, service)
|
178
161
|
|
@@ -187,14 +170,13 @@ class WorkflowBuilder(Builder):
|
|
187
170
|
volumes = []
|
188
171
|
|
189
172
|
service = {
|
190
|
-
'build': self.proxy_build,
|
191
173
|
'depends_on': depends_on,
|
192
174
|
'environment': environment,
|
193
175
|
'extends': self.service_builder.build_extends_proxy_base(self.dir_path),
|
194
176
|
'networks': networks,
|
195
177
|
'profiles': self.profiles,
|
196
178
|
'volumes': volumes,
|
197
|
-
'working_dir': self.
|
179
|
+
'working_dir': self.__working_dir,
|
198
180
|
}
|
199
181
|
|
200
182
|
if self.configure in self.services:
|
@@ -217,6 +199,7 @@ class WorkflowBuilder(Builder):
|
|
217
199
|
|
218
200
|
if self.config.detached:
|
219
201
|
volumes.append(f"{self.service_builder.service_name}:{STOOBLY_HOME_DIR}/.stoobly")
|
202
|
+
volumes.append(f"../..:{STOOBLY_HOME_DIR}/.stoobly/docker")
|
220
203
|
|
221
204
|
self.with_service(self.proxy, service)
|
222
205
|
|
@@ -1,6 +1,7 @@
|
|
1
|
+
import os
|
1
2
|
import pdb
|
2
3
|
|
3
|
-
from ...constants import SERVICE_HOSTNAME, SERVICE_PORT
|
4
|
+
from ...constants import SERVICE_HOSTNAME, SERVICE_PORT, STOOBLY_CERTS_DIR
|
4
5
|
from .builder import WorkflowBuilder
|
5
6
|
|
6
7
|
class MockDecorator():
|
@@ -20,7 +21,6 @@ class MockDecorator():
|
|
20
21
|
config = self.service_builder.config
|
21
22
|
|
22
23
|
command = [
|
23
|
-
'--certs', f"/etc/ssl/certs/{SERVICE_HOSTNAME}-joined.pem",
|
24
24
|
'--headless',
|
25
25
|
'--intercept',
|
26
26
|
'--lifecycle-hooks-path', 'lifecycle_hooks.py',
|
@@ -30,6 +30,10 @@ class MockDecorator():
|
|
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 {}
|