stoobly-agent 0.34.13__py3-none-any.whl → 1.0.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/cli/__init__.py +1 -0
- stoobly_agent/app/cli/helpers/openapi_endpoint_adapter.py +9 -7
- stoobly_agent/app/cli/helpers/shell.py +28 -0
- stoobly_agent/app/cli/main_group.py +1 -1
- stoobly_agent/app/cli/scaffold/__init__.py +0 -0
- stoobly_agent/app/cli/scaffold/app.py +106 -0
- stoobly_agent/app/cli/scaffold/app_command.py +82 -0
- stoobly_agent/app/cli/scaffold/app_config.py +32 -0
- stoobly_agent/app/cli/scaffold/app_create_command.py +24 -0
- stoobly_agent/app/cli/scaffold/command.py +15 -0
- stoobly_agent/app/cli/scaffold/config.py +35 -0
- stoobly_agent/app/cli/scaffold/constants.py +35 -0
- stoobly_agent/app/cli/scaffold/docker/__init__.py +0 -0
- stoobly_agent/app/cli/scaffold/docker/app_builder.py +26 -0
- stoobly_agent/app/cli/scaffold/docker/builder.py +117 -0
- stoobly_agent/app/cli/scaffold/docker/constants.py +7 -0
- stoobly_agent/app/cli/scaffold/docker/service/__init__.py +0 -0
- stoobly_agent/app/cli/scaffold/docker/service/build_decorator.py +37 -0
- stoobly_agent/app/cli/scaffold/docker/service/builder.py +117 -0
- stoobly_agent/app/cli/scaffold/docker/service/set_gateway_ports.py +47 -0
- stoobly_agent/app/cli/scaffold/docker/service/types.py +4 -0
- stoobly_agent/app/cli/scaffold/docker/workflow/__init__.py +0 -0
- stoobly_agent/app/cli/scaffold/docker/workflow/build_decorator.py +28 -0
- stoobly_agent/app/cli/scaffold/docker/workflow/builder.py +259 -0
- stoobly_agent/app/cli/scaffold/docker/workflow/decorators_factory.py +17 -0
- stoobly_agent/app/cli/scaffold/docker/workflow/mock_decorator.py +40 -0
- stoobly_agent/app/cli/scaffold/docker/workflow/reverse_proxy_decorator.py +51 -0
- stoobly_agent/app/cli/scaffold/env.py +49 -0
- stoobly_agent/app/cli/scaffold/service.py +25 -0
- stoobly_agent/app/cli/scaffold/service_command.py +50 -0
- stoobly_agent/app/cli/scaffold/service_config.py +207 -0
- stoobly_agent/app/cli/scaffold/service_create_command.py +77 -0
- stoobly_agent/app/cli/scaffold/service_workflow.py +18 -0
- stoobly_agent/app/cli/scaffold/templates/__init__.py +0 -0
- stoobly_agent/app/cli/scaffold/templates/app/.Dockerfile.context +8 -0
- stoobly_agent/app/cli/scaffold/templates/app/.Dockerfile.proxy +35 -0
- stoobly_agent/app/cli/scaffold/templates/app/.Makefile +118 -0
- stoobly_agent/app/cli/scaffold/templates/app/.docker-compose.base.yml +15 -0
- stoobly_agent/app/cli/scaffold/templates/app/Makefile +3 -0
- stoobly_agent/app/cli/scaffold/templates/app/build/.config.yml +1 -0
- stoobly_agent/app/cli/scaffold/templates/app/build/.docker-compose.base.yml +11 -0
- stoobly_agent/app/cli/scaffold/templates/app/build/mock/.docker-compose.mock.yml +22 -0
- stoobly_agent/app/cli/scaffold/templates/app/build/mock/bin/.configure +5 -0
- stoobly_agent/app/cli/scaffold/templates/app/build/mock/bin/.init +5 -0
- stoobly_agent/app/cli/scaffold/templates/app/build/mock/bin/configure +1 -0
- stoobly_agent/app/cli/scaffold/templates/app/build/mock/bin/init +1 -0
- stoobly_agent/app/cli/scaffold/templates/app/build/record/.docker-compose.record.yml +22 -0
- stoobly_agent/app/cli/scaffold/templates/app/build/record/bin/.configure +5 -0
- stoobly_agent/app/cli/scaffold/templates/app/build/record/bin/.init +5 -0
- stoobly_agent/app/cli/scaffold/templates/app/build/record/bin/configure +1 -0
- stoobly_agent/app/cli/scaffold/templates/app/build/record/bin/init +1 -0
- stoobly_agent/app/cli/scaffold/templates/app/build/test/.docker-compose.test.yml +22 -0
- stoobly_agent/app/cli/scaffold/templates/app/build/test/bin/.configure +5 -0
- stoobly_agent/app/cli/scaffold/templates/app/build/test/bin/.init +5 -0
- stoobly_agent/app/cli/scaffold/templates/app/build/test/bin/configure +1 -0
- stoobly_agent/app/cli/scaffold/templates/app/build/test/bin/init +1 -0
- stoobly_agent/app/cli/scaffold/templates/app/entrypoint/.config.yml +1 -0
- stoobly_agent/app/cli/scaffold/templates/app/entrypoint/.docker-compose.base.yml +17 -0
- stoobly_agent/app/cli/scaffold/templates/app/entrypoint/mock/.docker-compose.mock.yml +31 -0
- stoobly_agent/app/cli/scaffold/templates/app/entrypoint/mock/bin/.configure +10 -0
- stoobly_agent/app/cli/scaffold/templates/app/entrypoint/mock/bin/.init +5 -0
- stoobly_agent/app/cli/scaffold/templates/app/entrypoint/mock/bin/configure +1 -0
- stoobly_agent/app/cli/scaffold/templates/app/entrypoint/mock/bin/init +4 -0
- stoobly_agent/app/cli/scaffold/templates/app/entrypoint/mock/docker-compose.yml +1 -0
- stoobly_agent/app/cli/scaffold/templates/app/entrypoint/record/.docker-compose.record.yml +31 -0
- stoobly_agent/app/cli/scaffold/templates/app/entrypoint/record/bin/.configure +10 -0
- stoobly_agent/app/cli/scaffold/templates/app/entrypoint/record/bin/.init +5 -0
- stoobly_agent/app/cli/scaffold/templates/app/entrypoint/record/bin/configure +1 -0
- stoobly_agent/app/cli/scaffold/templates/app/entrypoint/record/bin/init +4 -0
- stoobly_agent/app/cli/scaffold/templates/app/entrypoint/record/docker-compose.yml +1 -0
- stoobly_agent/app/cli/scaffold/templates/app/entrypoint/test/.docker-compose.test.yml +31 -0
- stoobly_agent/app/cli/scaffold/templates/app/entrypoint/test/bin/.configure +10 -0
- stoobly_agent/app/cli/scaffold/templates/app/entrypoint/test/bin/.init +3 -0
- stoobly_agent/app/cli/scaffold/templates/app/entrypoint/test/bin/configure +1 -0
- stoobly_agent/app/cli/scaffold/templates/app/entrypoint/test/bin/init +4 -0
- stoobly_agent/app/cli/scaffold/templates/app/entrypoint/test/docker-compose.yml +1 -0
- stoobly_agent/app/cli/scaffold/templates/app/gateway/.config.yml +1 -0
- stoobly_agent/app/cli/scaffold/templates/app/gateway/.docker-compose.base.yml +11 -0
- stoobly_agent/app/cli/scaffold/templates/app/gateway/mock/.docker-compose.mock.yml +13 -0
- stoobly_agent/app/cli/scaffold/templates/app/gateway/record/.docker-compose.record.yml +13 -0
- stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/.config.yml +1 -0
- stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/.docker-compose.base.yml +5 -0
- stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/exec/.docker-compose.exec.yml +12 -0
- stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/exec/bin/.create +11 -0
- stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/exec/bin/.delete +12 -0
- stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/exec/bin/.disable +3 -0
- stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/exec/bin/.enable +10 -0
- stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/exec/bin/.reset +12 -0
- stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/exec/bin/.run +11 -0
- stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/exec/bin/.snapshot +12 -0
- stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/exec/bin/.stop +11 -0
- stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/mock/.docker-compose.mock.yml +12 -0
- stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/record/.docker-compose.record.yml +13 -0
- stoobly_agent/app/cli/scaffold/templates/constants.py +63 -0
- stoobly_agent/app/cli/scaffold/templates/factory.py +46 -0
- stoobly_agent/app/cli/scaffold/templates/workflow/mock/bin/.configure +3 -0
- stoobly_agent/app/cli/scaffold/templates/workflow/mock/bin/.init +3 -0
- stoobly_agent/app/cli/scaffold/templates/workflow/mock/bin/configure +18 -0
- stoobly_agent/app/cli/scaffold/templates/workflow/mock/bin/init +4 -0
- stoobly_agent/app/cli/scaffold/templates/workflow/mock/fixtures/.keep +0 -0
- stoobly_agent/app/cli/scaffold/templates/workflow/mock/fixtures.yml +5 -0
- stoobly_agent/app/cli/scaffold/templates/workflow/mock/lifecycle_hooks.py +12 -0
- stoobly_agent/app/cli/scaffold/templates/workflow/record/bin/.configure +3 -0
- stoobly_agent/app/cli/scaffold/templates/workflow/record/bin/.init +3 -0
- stoobly_agent/app/cli/scaffold/templates/workflow/record/bin/configure +29 -0
- stoobly_agent/app/cli/scaffold/templates/workflow/record/bin/init +4 -0
- stoobly_agent/app/cli/scaffold/templates/workflow/record/lifecycle_hooks.py +12 -0
- stoobly_agent/app/cli/scaffold/templates/workflow/test/bin/.configure +3 -0
- stoobly_agent/app/cli/scaffold/templates/workflow/test/bin/.init +3 -0
- stoobly_agent/app/cli/scaffold/templates/workflow/test/bin/configure +18 -0
- stoobly_agent/app/cli/scaffold/templates/workflow/test/bin/init +4 -0
- stoobly_agent/app/cli/scaffold/templates/workflow/test/fixtures/.keep +0 -0
- stoobly_agent/app/cli/scaffold/templates/workflow/test/fixtures.yml +5 -0
- stoobly_agent/app/cli/scaffold/templates/workflow/test/lifecycle_hooks.py +12 -0
- stoobly_agent/app/cli/scaffold/workflow.py +49 -0
- stoobly_agent/app/cli/scaffold/workflow_command.py +137 -0
- stoobly_agent/app/cli/scaffold/workflow_copy_command.py +45 -0
- stoobly_agent/app/cli/scaffold/workflow_create_command.py +94 -0
- stoobly_agent/app/cli/scaffold/workflow_log_command.py +21 -0
- stoobly_agent/app/cli/scaffold/workflow_run_command.py +134 -0
- stoobly_agent/app/cli/scaffold_cli.py +392 -0
- stoobly_agent/cli.py +3 -2
- stoobly_agent/config/data_dir.py +40 -14
- stoobly_agent/lib/logger.py +3 -2
- stoobly_agent/test/app/models/schemas/.stoobly/db/VERSION +1 -1
- stoobly_agent/test/config/data_dir_test.py +4 -4
- {stoobly_agent-0.34.13.dist-info → stoobly_agent-1.0.0.dist-info}/METADATA +8 -7
- {stoobly_agent-0.34.13.dist-info → stoobly_agent-1.0.0.dist-info}/RECORD +132 -14
- {stoobly_agent-0.34.13.dist-info → stoobly_agent-1.0.0.dist-info}/WHEEL +1 -1
- {stoobly_agent-0.34.13.dist-info → stoobly_agent-1.0.0.dist-info}/LICENSE +0 -0
- {stoobly_agent-0.34.13.dist-info → stoobly_agent-1.0.0.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,117 @@
|
|
1
|
+
import os
|
2
|
+
import pdb
|
3
|
+
|
4
|
+
from ...app_config import AppConfig
|
5
|
+
from ...constants import SERVICE_HOSTNAME, SERVICE_HOSTNAME_ENV, SERVICE_PORT, SERVICE_PORT_ENV, SERVICE_SCHEME, SERVICE_SCHEME_ENV
|
6
|
+
from ...service_config import ServiceConfig
|
7
|
+
from ..app_builder import AppBuilder
|
8
|
+
from ..builder import Builder
|
9
|
+
from ..constants import DOCKER_COMPOSE_BASE
|
10
|
+
|
11
|
+
class ServiceBuilder(Builder):
|
12
|
+
|
13
|
+
def __init__(self, config: ServiceConfig, app_builder: AppBuilder = None):
|
14
|
+
service_path = config.dir
|
15
|
+
super().__init__(service_path, DOCKER_COMPOSE_BASE)
|
16
|
+
|
17
|
+
if not app_builder:
|
18
|
+
app_dir = os.path.dirname(service_path)
|
19
|
+
app_builder = AppBuilder(AppConfig(app_dir))
|
20
|
+
self.app_builder = app_builder
|
21
|
+
|
22
|
+
self.__config = config
|
23
|
+
self.__service_name = os.path.basename(service_path)
|
24
|
+
|
25
|
+
self.load()
|
26
|
+
|
27
|
+
@property
|
28
|
+
def app_base(self):
|
29
|
+
return f"{self.service_name}.app_base"
|
30
|
+
|
31
|
+
@property
|
32
|
+
def init_base(self):
|
33
|
+
return f"{self.service_name}.init_base"
|
34
|
+
|
35
|
+
@property
|
36
|
+
def config(self):
|
37
|
+
return self.__config
|
38
|
+
|
39
|
+
@property
|
40
|
+
def configure_base(self):
|
41
|
+
return f"{self.service_name}.configure_base"
|
42
|
+
|
43
|
+
@property
|
44
|
+
def configure_base_service(self):
|
45
|
+
return self.services.get(self.configure_base)
|
46
|
+
|
47
|
+
@property
|
48
|
+
def proxy_base(self):
|
49
|
+
return f"{self.service_name}.proxy_base"
|
50
|
+
|
51
|
+
@property
|
52
|
+
def proxy_base_service(self):
|
53
|
+
return self.services.get(self.proxy_base)
|
54
|
+
|
55
|
+
@property
|
56
|
+
def service_name(self):
|
57
|
+
return self.__service_name
|
58
|
+
|
59
|
+
def build_extends_init_base(self, source_dir: str):
|
60
|
+
return self.build_extends(self.init_base, source_dir)
|
61
|
+
|
62
|
+
def build_extends_configure_base(self, source_dir: str):
|
63
|
+
return self.build_extends(self.configure_base, source_dir)
|
64
|
+
|
65
|
+
def build_extends_proxy_base(self, source_dir: str):
|
66
|
+
return self.build_extends(self.proxy_base, source_dir)
|
67
|
+
|
68
|
+
def build_proxy_base(self):
|
69
|
+
if not self.config.hostname:
|
70
|
+
return
|
71
|
+
|
72
|
+
args = {}
|
73
|
+
self.with_service(self.proxy_base, {
|
74
|
+
'build': {
|
75
|
+
'args': args,
|
76
|
+
},
|
77
|
+
'extends': {
|
78
|
+
'file': os.path.relpath(self.app_builder.compose_file_path, self.dir_path),
|
79
|
+
'service': self.app_builder.proxy_base
|
80
|
+
}
|
81
|
+
})
|
82
|
+
|
83
|
+
args[SERVICE_HOSTNAME_ENV] = f"{SERVICE_HOSTNAME}"
|
84
|
+
|
85
|
+
def build_init_base(self):
|
86
|
+
environment = {}
|
87
|
+
self.with_service(self.init_base, {
|
88
|
+
'command': ['bin/.init', 'dist'],
|
89
|
+
'environment': environment,
|
90
|
+
'extends': {
|
91
|
+
'file': os.path.relpath(self.app_builder.compose_file_path, self.dir_path),
|
92
|
+
'service': self.app_builder.context_base
|
93
|
+
}
|
94
|
+
})
|
95
|
+
|
96
|
+
def build_configure_base(self):
|
97
|
+
environment = {}
|
98
|
+
self.with_service(self.configure_base, {
|
99
|
+
'command': ['bin/.configure'],
|
100
|
+
'environment': environment,
|
101
|
+
'extends': {
|
102
|
+
'file': os.path.relpath(self.app_builder.compose_file_path, self.dir_path),
|
103
|
+
'service': self.app_builder.context_base
|
104
|
+
}
|
105
|
+
})
|
106
|
+
|
107
|
+
def write(self):
|
108
|
+
self.build_init_base()
|
109
|
+
self.build_configure_base()
|
110
|
+
|
111
|
+
if self.config.hostname:
|
112
|
+
self.build_proxy_base()
|
113
|
+
|
114
|
+
super().write({
|
115
|
+
'networks': self.networks,
|
116
|
+
'services': self.services,
|
117
|
+
})
|
@@ -0,0 +1,47 @@
|
|
1
|
+
import os
|
2
|
+
import pdb
|
3
|
+
import yaml
|
4
|
+
|
5
|
+
from typing import List
|
6
|
+
|
7
|
+
from stoobly_agent.app.cli.scaffold.service_config import ServiceConfig
|
8
|
+
from stoobly_agent.app.cli.scaffold.docker.constants import DOCKER_COMPOSE_BASE
|
9
|
+
from stoobly_agent.app.cli.scaffold.templates.constants import CORE_GATEWAY_SERVICE_NAME
|
10
|
+
|
11
|
+
def set_gateway_ports(service_paths: List[str]):
|
12
|
+
if len(service_paths) == 0:
|
13
|
+
return
|
14
|
+
|
15
|
+
ports = []
|
16
|
+
for path in service_paths:
|
17
|
+
config = ServiceConfig(path)
|
18
|
+
|
19
|
+
try:
|
20
|
+
port = int(config.port)
|
21
|
+
except Exception:
|
22
|
+
continue
|
23
|
+
|
24
|
+
if port > 0 and port <= 65535 and port not in ports:
|
25
|
+
ports.append(port)
|
26
|
+
|
27
|
+
app_dir_path = os.path.dirname(service_paths[0])
|
28
|
+
gateway_service_path = os.path.join(app_dir_path, CORE_GATEWAY_SERVICE_NAME)
|
29
|
+
docker_compose_path = os.path.join(gateway_service_path, DOCKER_COMPOSE_BASE)
|
30
|
+
|
31
|
+
with open(docker_compose_path, 'r+') as fp:
|
32
|
+
compose = yaml.safe_load(fp)
|
33
|
+
services = compose.get('services')
|
34
|
+
|
35
|
+
if not services:
|
36
|
+
return
|
37
|
+
|
38
|
+
gateway_base = services.get('gateway_base')
|
39
|
+
|
40
|
+
if not gateway_base:
|
41
|
+
return
|
42
|
+
|
43
|
+
gateway_base['ports'] = list(map(lambda port: f"{port}:{port}", ports))
|
44
|
+
|
45
|
+
fp.seek(0)
|
46
|
+
yaml.dump(compose, fp)
|
47
|
+
fp.truncate()
|
File without changes
|
@@ -0,0 +1,28 @@
|
|
1
|
+
import pdb
|
2
|
+
|
3
|
+
from .builder import WorkflowBuilder
|
4
|
+
|
5
|
+
class BuildDecorator():
|
6
|
+
|
7
|
+
def __init__(self, workflow_builder: WorkflowBuilder):
|
8
|
+
self.__workflow_builder = workflow_builder
|
9
|
+
|
10
|
+
@property
|
11
|
+
def workflow_builder(self):
|
12
|
+
return self.__workflow_builder
|
13
|
+
|
14
|
+
def decorate(self):
|
15
|
+
service_builder = self.__workflow_builder.service_builder
|
16
|
+
|
17
|
+
services = self.workflow_builder.services
|
18
|
+
app_name = self.workflow_builder.app
|
19
|
+
app_service = services.get(app_name) or {}
|
20
|
+
|
21
|
+
services[app_name] = {
|
22
|
+
**app_service,
|
23
|
+
**{
|
24
|
+
'extends': service_builder.build_extends_proxy_base(self.workflow_builder.dir_path),
|
25
|
+
'hostname': service_builder.config.hostname,
|
26
|
+
'profiles': self.workflow_builder.profiles,
|
27
|
+
}
|
28
|
+
}
|
@@ -0,0 +1,259 @@
|
|
1
|
+
import os
|
2
|
+
import pdb
|
3
|
+
|
4
|
+
from typing import List
|
5
|
+
|
6
|
+
from ...constants import (
|
7
|
+
COMPOSE_TEMPLATE, SERVICE_HOSTNAME, SERVICE_HOSTNAME_ENV, SERVICE_NAME_ENV, SERVICE_PORT, SERVICE_PORT_ENV, SERVICE_SCHEME,
|
8
|
+
SERVICE_SCHEME_ENV, STOOBLY_HOME_DIR, WORKFLOW_NAME_ENV
|
9
|
+
)
|
10
|
+
from ..builder import Builder
|
11
|
+
from ..service.builder import ServiceBuilder
|
12
|
+
from ...templates.constants import SERVICE_HOSTNAME_BUILD_ARG
|
13
|
+
|
14
|
+
class WorkflowBuilder(Builder):
|
15
|
+
|
16
|
+
def __init__(self, workflow_path: str, service_builder: ServiceBuilder):
|
17
|
+
self._env = [SERVICE_NAME_ENV, WORKFLOW_NAME_ENV]
|
18
|
+
self.__workflow_name = os.path.basename(workflow_path)
|
19
|
+
super().__init__(workflow_path, COMPOSE_TEMPLATE.format(workflow=self.__workflow_name))
|
20
|
+
|
21
|
+
self.__context = '../'
|
22
|
+
self.__profiles = [self.__workflow_name]
|
23
|
+
self.__workdir = os.path.join(STOOBLY_HOME_DIR, self.workflow_name)
|
24
|
+
|
25
|
+
if not service_builder:
|
26
|
+
service_path = os.path.dirname(workflow_path)
|
27
|
+
service_builder = ServiceBuilder(service_path)
|
28
|
+
|
29
|
+
self.__service_builder = service_builder
|
30
|
+
|
31
|
+
if self.config.hostname:
|
32
|
+
self.with_public_network()
|
33
|
+
|
34
|
+
self.load()
|
35
|
+
|
36
|
+
@property
|
37
|
+
def app(self):
|
38
|
+
return f"{self.namespace}.app"
|
39
|
+
|
40
|
+
@property
|
41
|
+
def base_compose_file_path(self):
|
42
|
+
return os.path.relpath(self.service_builder.compose_file_path, self.dir_path)
|
43
|
+
|
44
|
+
@property
|
45
|
+
def init(self):
|
46
|
+
return f"{self.namespace}.init"
|
47
|
+
|
48
|
+
@property
|
49
|
+
def config(self):
|
50
|
+
return self.service_builder.config
|
51
|
+
|
52
|
+
@property
|
53
|
+
def configure(self):
|
54
|
+
return f"{self.namespace}.configure"
|
55
|
+
|
56
|
+
@property
|
57
|
+
def context(self):
|
58
|
+
return self.__context
|
59
|
+
|
60
|
+
@property
|
61
|
+
def context_build(self):
|
62
|
+
return {
|
63
|
+
'context': self.context,
|
64
|
+
'dockerfile': self.context_docker_file_path,
|
65
|
+
}
|
66
|
+
|
67
|
+
@property
|
68
|
+
def context_docker_file_path(self):
|
69
|
+
return os.path.relpath(self.service_builder.app_builder.context_docker_file_path, self.service_path)
|
70
|
+
|
71
|
+
@property
|
72
|
+
def namespace(self):
|
73
|
+
return f"{self.service_builder.service_name}"
|
74
|
+
|
75
|
+
@property
|
76
|
+
def profiles(self):
|
77
|
+
return self.__profiles
|
78
|
+
|
79
|
+
@property
|
80
|
+
def proxy(self):
|
81
|
+
return f"{self.namespace}.proxy"
|
82
|
+
|
83
|
+
@property
|
84
|
+
def proxy_build(self):
|
85
|
+
args = {}
|
86
|
+
args[SERVICE_HOSTNAME_BUILD_ARG] = SERVICE_HOSTNAME
|
87
|
+
|
88
|
+
return {
|
89
|
+
'args': args,
|
90
|
+
'context': self.context,
|
91
|
+
'dockerfile': self.proxy_docker_file_path,
|
92
|
+
}
|
93
|
+
|
94
|
+
@property
|
95
|
+
def proxy_docker_file_path(self):
|
96
|
+
return os.path.relpath(self.service_builder.app_builder.proxy_docker_file_path, self.service_path)
|
97
|
+
|
98
|
+
@property
|
99
|
+
def service_builder(self):
|
100
|
+
return self.__service_builder
|
101
|
+
|
102
|
+
@property
|
103
|
+
def service_path(self):
|
104
|
+
return self.__service_builder.dir_path
|
105
|
+
|
106
|
+
@property
|
107
|
+
def workflow_name(self):
|
108
|
+
return self.__workflow_name
|
109
|
+
|
110
|
+
def build_all(self):
|
111
|
+
# Resources
|
112
|
+
self.with_network(self.service_builder.service_name)
|
113
|
+
|
114
|
+
if self.config.detached:
|
115
|
+
self.with_volume(self.service_builder.service_name)
|
116
|
+
|
117
|
+
# Services
|
118
|
+
self.build_init()
|
119
|
+
self.build_configure()
|
120
|
+
|
121
|
+
if self.config.hostname:
|
122
|
+
self.with_public_network()
|
123
|
+
self.build_proxy() # Depends on configure, must call build_configure first
|
124
|
+
|
125
|
+
def build_init(self):
|
126
|
+
environment = { **self.env_dict() }
|
127
|
+
volumes = []
|
128
|
+
|
129
|
+
service = {
|
130
|
+
'build': self.context_build,
|
131
|
+
'environment': environment,
|
132
|
+
'extends': self.service_builder.build_extends_init_base(self.dir_path),
|
133
|
+
'profiles': self.profiles,
|
134
|
+
'volumes': volumes,
|
135
|
+
'working_dir': self.__workdir,
|
136
|
+
}
|
137
|
+
|
138
|
+
if self.config.hostname:
|
139
|
+
self.__with_url_environment(environment)
|
140
|
+
|
141
|
+
if self.config.detached:
|
142
|
+
volumes.append(f"{self.service_builder.service_name}:{STOOBLY_HOME_DIR}/.stoobly")
|
143
|
+
|
144
|
+
self.with_service(self.init, service)
|
145
|
+
|
146
|
+
def build_configure(self):
|
147
|
+
# If the configure_base service does not exist, we can't extend from it, return
|
148
|
+
if not self.service_builder.configure_base_service:
|
149
|
+
return
|
150
|
+
|
151
|
+
depends_on = {}
|
152
|
+
environment = { **self.env_dict() }
|
153
|
+
volumes = []
|
154
|
+
|
155
|
+
service = {
|
156
|
+
'build': self.context_build,
|
157
|
+
'depends_on': depends_on,
|
158
|
+
'environment': environment,
|
159
|
+
'extends': self.service_builder.build_extends_configure_base(self.dir_path),
|
160
|
+
'profiles': self.profiles,
|
161
|
+
'volumes': volumes,
|
162
|
+
'working_dir': self.__workdir,
|
163
|
+
}
|
164
|
+
|
165
|
+
if self.config.hostname:
|
166
|
+
self.__with_url_environment(environment)
|
167
|
+
|
168
|
+
if self.init in self.services:
|
169
|
+
depends_on[self.init] = {
|
170
|
+
'condition': 'service_completed_successfully',
|
171
|
+
}
|
172
|
+
|
173
|
+
if self.config.detached:
|
174
|
+
volumes.append(f"{self.service_builder.service_name}:{STOOBLY_HOME_DIR}/.stoobly")
|
175
|
+
|
176
|
+
self.with_service(self.configure, service)
|
177
|
+
|
178
|
+
def build_proxy(self):
|
179
|
+
# If the proxy_base service does not exist, we can't extend from it, return
|
180
|
+
if not self.service_builder.proxy_base_service:
|
181
|
+
return
|
182
|
+
|
183
|
+
depends_on = {}
|
184
|
+
environment = { **self.env_dict() }
|
185
|
+
extra_hosts = []
|
186
|
+
networks = [self.service_builder.service_name]
|
187
|
+
volumes = []
|
188
|
+
|
189
|
+
service = {
|
190
|
+
'build': self.proxy_build,
|
191
|
+
'depends_on': depends_on,
|
192
|
+
'environment': environment,
|
193
|
+
'extra_hosts': extra_hosts,
|
194
|
+
'extends': self.service_builder.build_extends_proxy_base(self.dir_path),
|
195
|
+
'networks': networks,
|
196
|
+
'profiles': self.profiles,
|
197
|
+
'volumes': volumes,
|
198
|
+
'working_dir': self.__workdir,
|
199
|
+
}
|
200
|
+
|
201
|
+
if self.configure in self.services:
|
202
|
+
depends_on[self.init] = {
|
203
|
+
'condition': 'service_completed_successfully',
|
204
|
+
}
|
205
|
+
|
206
|
+
depends_on[self.configure] = {
|
207
|
+
'condition': 'service_completed_successfully',
|
208
|
+
}
|
209
|
+
|
210
|
+
if self.config.hostname:
|
211
|
+
environment['VIRTUAL_HOST'] = SERVICE_HOSTNAME
|
212
|
+
environment['VIRTUAL_PORT'] = SERVICE_PORT
|
213
|
+
environment['VIRTUAL_PROTO'] = SERVICE_SCHEME
|
214
|
+
|
215
|
+
# Expose this container service to the public network
|
216
|
+
# so that it is accessible to other Stoobly services
|
217
|
+
networks.append(self.public_network_name)
|
218
|
+
|
219
|
+
if self.config.detached:
|
220
|
+
volumes.append(f"{self.service_builder.service_name}:{STOOBLY_HOME_DIR}/.stoobly")
|
221
|
+
|
222
|
+
self.with_service(self.proxy, service)
|
223
|
+
|
224
|
+
def env_dict(self):
|
225
|
+
env = {}
|
226
|
+
for e in self._env:
|
227
|
+
env[e] = '${' + e + '}'
|
228
|
+
return env
|
229
|
+
|
230
|
+
def initialize_custom_file(self):
|
231
|
+
dest = self.custom_compose_file_path
|
232
|
+
|
233
|
+
if not os.path.exists(dest):
|
234
|
+
super().write({
|
235
|
+
'networks': self.networks,
|
236
|
+
'services': {}
|
237
|
+
}, dest)
|
238
|
+
|
239
|
+
def with_env(self, v: List[str]):
|
240
|
+
if not isinstance(v, list):
|
241
|
+
return self
|
242
|
+
self._env += v
|
243
|
+
return self
|
244
|
+
|
245
|
+
def write(self):
|
246
|
+
super().write({
|
247
|
+
'networks': self.networks,
|
248
|
+
'services': self.services,
|
249
|
+
'volumes': self.volumes,
|
250
|
+
})
|
251
|
+
|
252
|
+
def __with_url_environment(self, environment):
|
253
|
+
environment[SERVICE_HOSTNAME_ENV] = SERVICE_HOSTNAME
|
254
|
+
|
255
|
+
if self.config.scheme:
|
256
|
+
environment[SERVICE_SCHEME_ENV] = SERVICE_SCHEME
|
257
|
+
|
258
|
+
if self.config.port:
|
259
|
+
environment[SERVICE_PORT_ENV] = SERVICE_PORT
|
@@ -0,0 +1,17 @@
|
|
1
|
+
from stoobly_agent.app.cli.scaffold.service_config import ServiceConfig
|
2
|
+
|
3
|
+
from ...constants import WORKFLOW_MOCK_TYPE, WORKFLOW_RECORD_TYPE
|
4
|
+
from .mock_decorator import MockDecorator
|
5
|
+
from .reverse_proxy_decorator import ReverseProxyDecorator
|
6
|
+
|
7
|
+
def get_workflow_decorators(workflow: str, service_config: ServiceConfig):
|
8
|
+
workflow_decorators = []
|
9
|
+
|
10
|
+
if workflow == WORKFLOW_RECORD_TYPE:
|
11
|
+
if service_config.hostname:
|
12
|
+
workflow_decorators.append(ReverseProxyDecorator)
|
13
|
+
else:
|
14
|
+
if service_config.hostname:
|
15
|
+
workflow_decorators.append(ReverseProxyDecorator if service_config.detached else MockDecorator)
|
16
|
+
|
17
|
+
return workflow_decorators
|
@@ -0,0 +1,40 @@
|
|
1
|
+
import pdb
|
2
|
+
|
3
|
+
from ...constants import SERVICE_HOSTNAME, SERVICE_PORT, SERVICE_SCHEME
|
4
|
+
from .builder import WorkflowBuilder
|
5
|
+
|
6
|
+
class MockDecorator():
|
7
|
+
|
8
|
+
def __init__(self, workflow_builder: WorkflowBuilder):
|
9
|
+
self.__workflow_builder = workflow_builder
|
10
|
+
|
11
|
+
@property
|
12
|
+
def workflow_builder(self):
|
13
|
+
return self.__workflow_builder
|
14
|
+
|
15
|
+
@property
|
16
|
+
def service_builder(self):
|
17
|
+
return self.workflow_builder.service_builder
|
18
|
+
|
19
|
+
def decorate(self):
|
20
|
+
config = self.service_builder.config
|
21
|
+
|
22
|
+
command = [
|
23
|
+
'--certs', f"../{SERVICE_HOSTNAME}-joined.pem",
|
24
|
+
'--headless',
|
25
|
+
'--intercept',
|
26
|
+
'--lifecycle-hooks-path', 'lifecycle_hooks.py',
|
27
|
+
'--proxy-mode', config.proxy_mode,
|
28
|
+
'--proxy-port', f"{SERVICE_PORT}",
|
29
|
+
'--response-fixtures-path', 'fixtures.yml',
|
30
|
+
'--ssl-insecure'
|
31
|
+
]
|
32
|
+
|
33
|
+
services = self.__workflow_builder.services
|
34
|
+
proxy_name = self.__workflow_builder.proxy
|
35
|
+
proxy_service = services.get(proxy_name) or {}
|
36
|
+
|
37
|
+
services[proxy_name] = {
|
38
|
+
**proxy_service,
|
39
|
+
**{ 'command': command, 'hostname': f"{SERVICE_HOSTNAME}" },
|
40
|
+
}
|
@@ -0,0 +1,51 @@
|
|
1
|
+
import pdb
|
2
|
+
|
3
|
+
from ...constants import SERVICE_DNS, SERVICE_HOSTNAME, SERVICE_PORT
|
4
|
+
from .builder import WorkflowBuilder
|
5
|
+
|
6
|
+
class ReverseProxyDecorator():
|
7
|
+
|
8
|
+
def __init__(self, workflow_builder: WorkflowBuilder):
|
9
|
+
self.__workflow_builder = workflow_builder
|
10
|
+
|
11
|
+
@property
|
12
|
+
def workflow_builder(self):
|
13
|
+
return self.__workflow_builder
|
14
|
+
|
15
|
+
@property
|
16
|
+
def service_builder(self):
|
17
|
+
return self.workflow_builder.service_builder
|
18
|
+
|
19
|
+
def decorate(self):
|
20
|
+
config = self.service_builder.config
|
21
|
+
|
22
|
+
command = [
|
23
|
+
'--certs', f"../{SERVICE_HOSTNAME}-joined.pem",
|
24
|
+
'--headless',
|
25
|
+
'--lifecycle-hooks-path', 'lifecycle_hooks.py',
|
26
|
+
'--proxy-mode', config.proxy_mode,
|
27
|
+
'--proxy-port', f"{SERVICE_PORT}",
|
28
|
+
'--ssl-insecure'
|
29
|
+
]
|
30
|
+
|
31
|
+
services = self.workflow_builder.services
|
32
|
+
proxy_name = self.workflow_builder.proxy
|
33
|
+
proxy_service = services.get(proxy_name) or {}
|
34
|
+
|
35
|
+
# proxying forwards requests to the actual service
|
36
|
+
# If we set the 'hostname' property, this will cause an "infinite loop"
|
37
|
+
|
38
|
+
service = {
|
39
|
+
**proxy_service,
|
40
|
+
**{ 'command': command },
|
41
|
+
}
|
42
|
+
|
43
|
+
# If we are reverse proxying to potentially an external host,
|
44
|
+
# Docker's embedded DNS will use the host's /etc/host file as part of the resolution process
|
45
|
+
# This can lead the hostname to resolve to localhost instead of the service's actual IP address
|
46
|
+
if config.dns and not config.detached:
|
47
|
+
service['dns'] = SERVICE_DNS
|
48
|
+
|
49
|
+
services[proxy_name] = service
|
50
|
+
|
51
|
+
|
@@ -0,0 +1,49 @@
|
|
1
|
+
import os
|
2
|
+
|
3
|
+
from .constants import ENV_FILE
|
4
|
+
|
5
|
+
DELIMITTER = "\n"
|
6
|
+
|
7
|
+
class Env():
|
8
|
+
|
9
|
+
def __init__(self, path: str):
|
10
|
+
self.__path = path
|
11
|
+
|
12
|
+
@property
|
13
|
+
def path(self):
|
14
|
+
return self.__path
|
15
|
+
|
16
|
+
def read(self, source: str = None):
|
17
|
+
if not source:
|
18
|
+
source = self.path
|
19
|
+
|
20
|
+
env_vars = {}
|
21
|
+
|
22
|
+
if os.path.exists(source):
|
23
|
+
with open(source, 'r') as fp:
|
24
|
+
contents = fp.read()
|
25
|
+
|
26
|
+
rows = contents.split(DELIMITTER)
|
27
|
+
for row in rows:
|
28
|
+
el = row.split("=")
|
29
|
+
if len(el) != 2:
|
30
|
+
continue
|
31
|
+
env_vars[el[0]] = el[1]
|
32
|
+
|
33
|
+
return env_vars
|
34
|
+
|
35
|
+
# Priority: env_vars, source_env_vars, dest_env_vars
|
36
|
+
def merge(self, defaults_path: str, env_vars: dict = {}):
|
37
|
+
source_env_vars = self.read()
|
38
|
+
dest_env_vars = self.read(defaults_path)
|
39
|
+
dest_env_vars.update(source_env_vars)
|
40
|
+
dest_env_vars.update(env_vars)
|
41
|
+
return env_vars
|
42
|
+
|
43
|
+
def write(self, env_vars: dict):
|
44
|
+
env_file_path = os.path.join(self.path)
|
45
|
+
with open(env_file_path, 'w') as fp:
|
46
|
+
contents = []
|
47
|
+
for var in env_vars:
|
48
|
+
contents.append(f"{var}={env_vars[var]}")
|
49
|
+
fp.write(DELIMITTER.join(contents))
|
@@ -0,0 +1,25 @@
|
|
1
|
+
import os
|
2
|
+
|
3
|
+
from .app import App
|
4
|
+
|
5
|
+
class Service():
|
6
|
+
|
7
|
+
def __init__(self, service_name: str, app: App):
|
8
|
+
self.__app = app
|
9
|
+
self.__service_name = service_name
|
10
|
+
|
11
|
+
@property
|
12
|
+
def app(self):
|
13
|
+
return self.__app
|
14
|
+
|
15
|
+
@property
|
16
|
+
def dir_path(self):
|
17
|
+
return os.path.join(self.app.namespace_path, self.service_name)
|
18
|
+
|
19
|
+
@property
|
20
|
+
def service_name(self):
|
21
|
+
return self.__service_name
|
22
|
+
|
23
|
+
@property
|
24
|
+
def workflow_dir_path(self, workflow_name: str):
|
25
|
+
return os.path.join(self.dir_path, workflow_name)
|
@@ -0,0 +1,50 @@
|
|
1
|
+
import os
|
2
|
+
import pdb
|
3
|
+
|
4
|
+
from .app import App
|
5
|
+
from .app_command import AppCommand
|
6
|
+
from .service_config import ServiceConfig
|
7
|
+
|
8
|
+
class ServiceCommand(AppCommand):
|
9
|
+
|
10
|
+
def __init__(self, app: App, **kwargs):
|
11
|
+
super().__init__(app)
|
12
|
+
self.__service_name = kwargs.get('service_name')
|
13
|
+
|
14
|
+
self.__config = ServiceConfig(self.service_path, **kwargs)
|
15
|
+
|
16
|
+
@property
|
17
|
+
def service_config(self):
|
18
|
+
return self.__config
|
19
|
+
|
20
|
+
@property
|
21
|
+
def service_config_path(self):
|
22
|
+
return self.__config.path
|
23
|
+
|
24
|
+
@property
|
25
|
+
def service_name(self):
|
26
|
+
return self.__service_name
|
27
|
+
|
28
|
+
@property
|
29
|
+
def service_exists(self):
|
30
|
+
return os.path.exists(self.service_path)
|
31
|
+
|
32
|
+
@property
|
33
|
+
def service_path(self):
|
34
|
+
return os.path.join(
|
35
|
+
self.scaffold_dir_path,
|
36
|
+
self.service_relative_path
|
37
|
+
)
|
38
|
+
|
39
|
+
@property
|
40
|
+
def service_relative_path(self):
|
41
|
+
return os.path.join(
|
42
|
+
self.namespace,
|
43
|
+
self.service_name,
|
44
|
+
)
|
45
|
+
|
46
|
+
def config(self, _c: dict):
|
47
|
+
_config = self.app_config.read()
|
48
|
+
_config.update(self.service_config.read())
|
49
|
+
_config.update(_c)
|
50
|
+
return _config
|