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.
Files changed (132) hide show
  1. stoobly_agent/__init__.py +1 -1
  2. stoobly_agent/app/cli/__init__.py +1 -0
  3. stoobly_agent/app/cli/helpers/openapi_endpoint_adapter.py +9 -7
  4. stoobly_agent/app/cli/helpers/shell.py +28 -0
  5. stoobly_agent/app/cli/main_group.py +1 -1
  6. stoobly_agent/app/cli/scaffold/__init__.py +0 -0
  7. stoobly_agent/app/cli/scaffold/app.py +106 -0
  8. stoobly_agent/app/cli/scaffold/app_command.py +82 -0
  9. stoobly_agent/app/cli/scaffold/app_config.py +32 -0
  10. stoobly_agent/app/cli/scaffold/app_create_command.py +24 -0
  11. stoobly_agent/app/cli/scaffold/command.py +15 -0
  12. stoobly_agent/app/cli/scaffold/config.py +35 -0
  13. stoobly_agent/app/cli/scaffold/constants.py +35 -0
  14. stoobly_agent/app/cli/scaffold/docker/__init__.py +0 -0
  15. stoobly_agent/app/cli/scaffold/docker/app_builder.py +26 -0
  16. stoobly_agent/app/cli/scaffold/docker/builder.py +117 -0
  17. stoobly_agent/app/cli/scaffold/docker/constants.py +7 -0
  18. stoobly_agent/app/cli/scaffold/docker/service/__init__.py +0 -0
  19. stoobly_agent/app/cli/scaffold/docker/service/build_decorator.py +37 -0
  20. stoobly_agent/app/cli/scaffold/docker/service/builder.py +117 -0
  21. stoobly_agent/app/cli/scaffold/docker/service/set_gateway_ports.py +47 -0
  22. stoobly_agent/app/cli/scaffold/docker/service/types.py +4 -0
  23. stoobly_agent/app/cli/scaffold/docker/workflow/__init__.py +0 -0
  24. stoobly_agent/app/cli/scaffold/docker/workflow/build_decorator.py +28 -0
  25. stoobly_agent/app/cli/scaffold/docker/workflow/builder.py +259 -0
  26. stoobly_agent/app/cli/scaffold/docker/workflow/decorators_factory.py +17 -0
  27. stoobly_agent/app/cli/scaffold/docker/workflow/mock_decorator.py +40 -0
  28. stoobly_agent/app/cli/scaffold/docker/workflow/reverse_proxy_decorator.py +51 -0
  29. stoobly_agent/app/cli/scaffold/env.py +49 -0
  30. stoobly_agent/app/cli/scaffold/service.py +25 -0
  31. stoobly_agent/app/cli/scaffold/service_command.py +50 -0
  32. stoobly_agent/app/cli/scaffold/service_config.py +207 -0
  33. stoobly_agent/app/cli/scaffold/service_create_command.py +77 -0
  34. stoobly_agent/app/cli/scaffold/service_workflow.py +18 -0
  35. stoobly_agent/app/cli/scaffold/templates/__init__.py +0 -0
  36. stoobly_agent/app/cli/scaffold/templates/app/.Dockerfile.context +8 -0
  37. stoobly_agent/app/cli/scaffold/templates/app/.Dockerfile.proxy +35 -0
  38. stoobly_agent/app/cli/scaffold/templates/app/.Makefile +118 -0
  39. stoobly_agent/app/cli/scaffold/templates/app/.docker-compose.base.yml +15 -0
  40. stoobly_agent/app/cli/scaffold/templates/app/Makefile +3 -0
  41. stoobly_agent/app/cli/scaffold/templates/app/build/.config.yml +1 -0
  42. stoobly_agent/app/cli/scaffold/templates/app/build/.docker-compose.base.yml +11 -0
  43. stoobly_agent/app/cli/scaffold/templates/app/build/mock/.docker-compose.mock.yml +22 -0
  44. stoobly_agent/app/cli/scaffold/templates/app/build/mock/bin/.configure +5 -0
  45. stoobly_agent/app/cli/scaffold/templates/app/build/mock/bin/.init +5 -0
  46. stoobly_agent/app/cli/scaffold/templates/app/build/mock/bin/configure +1 -0
  47. stoobly_agent/app/cli/scaffold/templates/app/build/mock/bin/init +1 -0
  48. stoobly_agent/app/cli/scaffold/templates/app/build/record/.docker-compose.record.yml +22 -0
  49. stoobly_agent/app/cli/scaffold/templates/app/build/record/bin/.configure +5 -0
  50. stoobly_agent/app/cli/scaffold/templates/app/build/record/bin/.init +5 -0
  51. stoobly_agent/app/cli/scaffold/templates/app/build/record/bin/configure +1 -0
  52. stoobly_agent/app/cli/scaffold/templates/app/build/record/bin/init +1 -0
  53. stoobly_agent/app/cli/scaffold/templates/app/build/test/.docker-compose.test.yml +22 -0
  54. stoobly_agent/app/cli/scaffold/templates/app/build/test/bin/.configure +5 -0
  55. stoobly_agent/app/cli/scaffold/templates/app/build/test/bin/.init +5 -0
  56. stoobly_agent/app/cli/scaffold/templates/app/build/test/bin/configure +1 -0
  57. stoobly_agent/app/cli/scaffold/templates/app/build/test/bin/init +1 -0
  58. stoobly_agent/app/cli/scaffold/templates/app/entrypoint/.config.yml +1 -0
  59. stoobly_agent/app/cli/scaffold/templates/app/entrypoint/.docker-compose.base.yml +17 -0
  60. stoobly_agent/app/cli/scaffold/templates/app/entrypoint/mock/.docker-compose.mock.yml +31 -0
  61. stoobly_agent/app/cli/scaffold/templates/app/entrypoint/mock/bin/.configure +10 -0
  62. stoobly_agent/app/cli/scaffold/templates/app/entrypoint/mock/bin/.init +5 -0
  63. stoobly_agent/app/cli/scaffold/templates/app/entrypoint/mock/bin/configure +1 -0
  64. stoobly_agent/app/cli/scaffold/templates/app/entrypoint/mock/bin/init +4 -0
  65. stoobly_agent/app/cli/scaffold/templates/app/entrypoint/mock/docker-compose.yml +1 -0
  66. stoobly_agent/app/cli/scaffold/templates/app/entrypoint/record/.docker-compose.record.yml +31 -0
  67. stoobly_agent/app/cli/scaffold/templates/app/entrypoint/record/bin/.configure +10 -0
  68. stoobly_agent/app/cli/scaffold/templates/app/entrypoint/record/bin/.init +5 -0
  69. stoobly_agent/app/cli/scaffold/templates/app/entrypoint/record/bin/configure +1 -0
  70. stoobly_agent/app/cli/scaffold/templates/app/entrypoint/record/bin/init +4 -0
  71. stoobly_agent/app/cli/scaffold/templates/app/entrypoint/record/docker-compose.yml +1 -0
  72. stoobly_agent/app/cli/scaffold/templates/app/entrypoint/test/.docker-compose.test.yml +31 -0
  73. stoobly_agent/app/cli/scaffold/templates/app/entrypoint/test/bin/.configure +10 -0
  74. stoobly_agent/app/cli/scaffold/templates/app/entrypoint/test/bin/.init +3 -0
  75. stoobly_agent/app/cli/scaffold/templates/app/entrypoint/test/bin/configure +1 -0
  76. stoobly_agent/app/cli/scaffold/templates/app/entrypoint/test/bin/init +4 -0
  77. stoobly_agent/app/cli/scaffold/templates/app/entrypoint/test/docker-compose.yml +1 -0
  78. stoobly_agent/app/cli/scaffold/templates/app/gateway/.config.yml +1 -0
  79. stoobly_agent/app/cli/scaffold/templates/app/gateway/.docker-compose.base.yml +11 -0
  80. stoobly_agent/app/cli/scaffold/templates/app/gateway/mock/.docker-compose.mock.yml +13 -0
  81. stoobly_agent/app/cli/scaffold/templates/app/gateway/record/.docker-compose.record.yml +13 -0
  82. stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/.config.yml +1 -0
  83. stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/.docker-compose.base.yml +5 -0
  84. stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/exec/.docker-compose.exec.yml +12 -0
  85. stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/exec/bin/.create +11 -0
  86. stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/exec/bin/.delete +12 -0
  87. stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/exec/bin/.disable +3 -0
  88. stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/exec/bin/.enable +10 -0
  89. stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/exec/bin/.reset +12 -0
  90. stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/exec/bin/.run +11 -0
  91. stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/exec/bin/.snapshot +12 -0
  92. stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/exec/bin/.stop +11 -0
  93. stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/mock/.docker-compose.mock.yml +12 -0
  94. stoobly_agent/app/cli/scaffold/templates/app/stoobly-ui/record/.docker-compose.record.yml +13 -0
  95. stoobly_agent/app/cli/scaffold/templates/constants.py +63 -0
  96. stoobly_agent/app/cli/scaffold/templates/factory.py +46 -0
  97. stoobly_agent/app/cli/scaffold/templates/workflow/mock/bin/.configure +3 -0
  98. stoobly_agent/app/cli/scaffold/templates/workflow/mock/bin/.init +3 -0
  99. stoobly_agent/app/cli/scaffold/templates/workflow/mock/bin/configure +18 -0
  100. stoobly_agent/app/cli/scaffold/templates/workflow/mock/bin/init +4 -0
  101. stoobly_agent/app/cli/scaffold/templates/workflow/mock/fixtures/.keep +0 -0
  102. stoobly_agent/app/cli/scaffold/templates/workflow/mock/fixtures.yml +5 -0
  103. stoobly_agent/app/cli/scaffold/templates/workflow/mock/lifecycle_hooks.py +12 -0
  104. stoobly_agent/app/cli/scaffold/templates/workflow/record/bin/.configure +3 -0
  105. stoobly_agent/app/cli/scaffold/templates/workflow/record/bin/.init +3 -0
  106. stoobly_agent/app/cli/scaffold/templates/workflow/record/bin/configure +29 -0
  107. stoobly_agent/app/cli/scaffold/templates/workflow/record/bin/init +4 -0
  108. stoobly_agent/app/cli/scaffold/templates/workflow/record/lifecycle_hooks.py +12 -0
  109. stoobly_agent/app/cli/scaffold/templates/workflow/test/bin/.configure +3 -0
  110. stoobly_agent/app/cli/scaffold/templates/workflow/test/bin/.init +3 -0
  111. stoobly_agent/app/cli/scaffold/templates/workflow/test/bin/configure +18 -0
  112. stoobly_agent/app/cli/scaffold/templates/workflow/test/bin/init +4 -0
  113. stoobly_agent/app/cli/scaffold/templates/workflow/test/fixtures/.keep +0 -0
  114. stoobly_agent/app/cli/scaffold/templates/workflow/test/fixtures.yml +5 -0
  115. stoobly_agent/app/cli/scaffold/templates/workflow/test/lifecycle_hooks.py +12 -0
  116. stoobly_agent/app/cli/scaffold/workflow.py +49 -0
  117. stoobly_agent/app/cli/scaffold/workflow_command.py +137 -0
  118. stoobly_agent/app/cli/scaffold/workflow_copy_command.py +45 -0
  119. stoobly_agent/app/cli/scaffold/workflow_create_command.py +94 -0
  120. stoobly_agent/app/cli/scaffold/workflow_log_command.py +21 -0
  121. stoobly_agent/app/cli/scaffold/workflow_run_command.py +134 -0
  122. stoobly_agent/app/cli/scaffold_cli.py +392 -0
  123. stoobly_agent/cli.py +3 -2
  124. stoobly_agent/config/data_dir.py +40 -14
  125. stoobly_agent/lib/logger.py +3 -2
  126. stoobly_agent/test/app/models/schemas/.stoobly/db/VERSION +1 -1
  127. stoobly_agent/test/config/data_dir_test.py +4 -4
  128. {stoobly_agent-0.34.13.dist-info → stoobly_agent-1.0.0.dist-info}/METADATA +8 -7
  129. {stoobly_agent-0.34.13.dist-info → stoobly_agent-1.0.0.dist-info}/RECORD +132 -14
  130. {stoobly_agent-0.34.13.dist-info → stoobly_agent-1.0.0.dist-info}/WHEEL +1 -1
  131. {stoobly_agent-0.34.13.dist-info → stoobly_agent-1.0.0.dist-info}/LICENSE +0 -0
  132. {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()
@@ -0,0 +1,4 @@
1
+ from typing import TypedDict
2
+
3
+ class BuildDecoratorOptions(TypedDict):
4
+ build_args: list
@@ -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