uber-compose 2.3.1a2002__tar.gz → 2.3.3__tar.gz
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.
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/PKG-INFO +1 -1
- uber_compose-2.3.3/setup.cfg +28 -0
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/core/constants.py +3 -1
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/core/docker_compose.py +15 -12
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/core/docker_compose_shell/interface.py +62 -73
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/core/docker_compose_shell/types.py +2 -2
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/core/system_docker_compose.py +8 -6
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/core/utils/compose_files.py +16 -8
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/core/utils/env_files.py +2 -2
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/core/utils/state_waiting.py +2 -2
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/env_description/env_types.py +8 -2
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/helpers/broken_services.py +2 -1
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/helpers/exec_result.py +0 -1
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/output/console.py +6 -3
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/uber_compose.py +73 -23
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/utils/services_construction.py +0 -1
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/vedro_plugin/base_structures/common_json_cli.py +7 -7
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/vedro_plugin/helpers/test_env_vars_setter.py +0 -1
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/vedro_plugin/plugin.py +19 -7
- uber_compose-2.3.3/uber_compose/version +1 -0
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/version.py +1 -0
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose.egg-info/PKG-INFO +1 -1
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose.egg-info/SOURCES.txt +1 -0
- uber_compose-2.3.1a2002/setup.cfg +0 -4
- uber_compose-2.3.1a2002/uber_compose/version +0 -1
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/LICENSE +0 -0
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/README.md +0 -0
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/setup.py +0 -0
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/__init__.py +1 -1
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/core/__init__.py +0 -0
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/core/docker_compose_shell/__init__.py +0 -0
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/core/sequence_run_types.py +1 -1
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/core/utils/__init__.py +0 -0
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/core/utils/compose_instance_cfg.py +0 -0
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/core/utils/process_command_output.py +0 -0
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/core/utils/shell_process.py +0 -0
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/env_description/__init__.py +0 -0
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/env_description/env_tools.py +0 -0
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/errors/__init__.py +0 -0
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/errors/migrations.py +0 -0
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/errors/up.py +0 -0
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/helpers/__init__.py +0 -0
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/helpers/bytes_pickle.py +0 -0
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/helpers/countdown_counter.py +0 -0
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/helpers/exec_record.py +0 -0
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/helpers/health_policy.py +0 -0
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/helpers/jobs_result.py +0 -0
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/helpers/labels.py +0 -0
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/helpers/singleton.py +0 -0
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/helpers/state_keeper.py +0 -0
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/output/__init__.py +0 -0
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/output/styles.py +0 -0
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/utils/__init__.py +0 -0
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/utils/docker_compose_files_path.py +0 -0
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/utils/docker_compose_service_deps.py +0 -0
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/utils/search_docker_compose_files.py +0 -0
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/vedro_plugin/__init__.py +0 -0
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/vedro_plugin/base_structures/__init__.py +0 -0
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/vedro_plugin/helpers/__init__.py +0 -0
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/vedro_plugin/helpers/scenario_ordering.py +0 -0
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/vedro_plugin/helpers/scenario_tag_processing.py +0 -0
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose.egg-info/dependency_links.txt +0 -0
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose.egg-info/requires.txt +0 -0
- {uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose.egg-info/top_level.txt +0 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
[isort]
|
|
2
|
+
line_length = 120
|
|
3
|
+
force_single_line = True
|
|
4
|
+
force_sort_within_sections = True
|
|
5
|
+
known_first_party = uber_compose
|
|
6
|
+
skip = .venv,.git,build,dist,tests/mnt,tests/.vedro,tests/deps,tests/up,tests/build
|
|
7
|
+
skip_glob = **/__pycache__/*,**/*.egg-info/*
|
|
8
|
+
|
|
9
|
+
[flake8]
|
|
10
|
+
max-line-length = 120
|
|
11
|
+
exclude =
|
|
12
|
+
.venv,
|
|
13
|
+
.git,
|
|
14
|
+
build,
|
|
15
|
+
dist,
|
|
16
|
+
*.egg-info,
|
|
17
|
+
tests/mnt,
|
|
18
|
+
tests/.vedro,
|
|
19
|
+
tests/deps,
|
|
20
|
+
tests/up,
|
|
21
|
+
tests/build,
|
|
22
|
+
__pycache__
|
|
23
|
+
extend-ignore = E203,W503,E731
|
|
24
|
+
|
|
25
|
+
[egg_info]
|
|
26
|
+
tag_build =
|
|
27
|
+
tag_date = 0
|
|
28
|
+
|
|
@@ -11,7 +11,9 @@ class Constants:
|
|
|
11
11
|
|
|
12
12
|
self.project: str = os.environ.get('COMPOSE_PROJECT_NAME')
|
|
13
13
|
self.compose_project_name = os.environ.get('COMPOSE_PROJECT_NAME')
|
|
14
|
-
assert self.compose_project_name,
|
|
14
|
+
assert self.compose_project_name, (
|
|
15
|
+
'COMPOSE_PROJECT_NAME environment variable is not set: - COMPOSE_PROJECT_NAME=${PWD##*/}'
|
|
16
|
+
)
|
|
15
17
|
|
|
16
18
|
self.docker_host = os.environ.get('DOCKER_HOST', 'unix:///var/run/docker.sock')
|
|
17
19
|
|
|
@@ -1,11 +1,10 @@
|
|
|
1
|
-
import os
|
|
2
|
-
import sys
|
|
3
1
|
from asyncio import sleep
|
|
2
|
+
import os
|
|
4
3
|
from pathlib import Path
|
|
4
|
+
import sys
|
|
5
5
|
|
|
6
6
|
from rich.text import Text
|
|
7
7
|
|
|
8
|
-
from uber_compose.core.constants import Constants
|
|
9
8
|
from uber_compose.core.docker_compose_shell.interface import ComposeShellInterface
|
|
10
9
|
from uber_compose.core.sequence_run_types import ComposeInstanceFiles
|
|
11
10
|
from uber_compose.core.sequence_run_types import EnvInstanceConfig
|
|
@@ -118,7 +117,8 @@ class ComposeInstance:
|
|
|
118
117
|
kill_after=False,
|
|
119
118
|
)
|
|
120
119
|
|
|
121
|
-
|
|
120
|
+
# empty or none skips
|
|
121
|
+
migration_errors = [error for error in migration_result.stderr.split(b'\n') if error]
|
|
122
122
|
skipper_migration_errors = [
|
|
123
123
|
current_error
|
|
124
124
|
for current_error in migration_errors
|
|
@@ -142,20 +142,25 @@ class ComposeInstance:
|
|
|
142
142
|
]
|
|
143
143
|
if not migration_result.finished or critical_migration_errors:
|
|
144
144
|
services_status = await self.compose_executor.dc_state()
|
|
145
|
-
error = Text(
|
|
146
|
-
|
|
145
|
+
error = Text(
|
|
146
|
+
f"Can't migrate service {target_service}, with {substituted_cmd}", style=Style.bad,
|
|
147
|
+
).append(
|
|
148
|
+
Text(f"\n{migration_result.stdout=}\n", style=Style.regular)
|
|
147
149
|
).append(
|
|
148
150
|
Text(f"{migration_result.stderr=}\n", style=Style.bad)
|
|
149
151
|
).append(
|
|
150
|
-
Text(
|
|
152
|
+
Text("Services status:\n", style=Style.info)
|
|
151
153
|
).append(
|
|
152
154
|
services_status.as_rich_text()
|
|
153
155
|
)
|
|
154
156
|
self.logger.error(error)
|
|
155
157
|
self.logger.error_details(f"\nServices logs:\n {await self.logs(services)}")
|
|
156
|
-
raise ServicesUpError(
|
|
157
|
-
|
|
158
|
-
|
|
158
|
+
raise ServicesUpError(
|
|
159
|
+
f"Can't migrate service {target_service}, with {substituted_cmd}: "
|
|
160
|
+
f"{migration_result.finished=}"
|
|
161
|
+
f"\n{migration_result.stdout=}\n{migration_result.stderr=}\n"
|
|
162
|
+
f"\nServices status:\n {services_status.as_rich_text()}"
|
|
163
|
+
) from None
|
|
159
164
|
|
|
160
165
|
async def run_services_pack(self, services: list[str], migrations):
|
|
161
166
|
|
|
@@ -184,7 +189,6 @@ class ComposeInstance:
|
|
|
184
189
|
f"\nServices status:\n {services_status.as_rich_text()}"
|
|
185
190
|
) from None
|
|
186
191
|
|
|
187
|
-
|
|
188
192
|
await self.run_migration(
|
|
189
193
|
[EventStage.AFTER_SERVICE_START],
|
|
190
194
|
services,
|
|
@@ -275,7 +279,6 @@ class ComposeInstance:
|
|
|
275
279
|
f"\nServices status:\n {services_status.as_rich_text()}"
|
|
276
280
|
) from None
|
|
277
281
|
|
|
278
|
-
|
|
279
282
|
async def logs(self, services=None) -> str:
|
|
280
283
|
job_result, log = await self.compose_executor.dc_logs(services, logs_param='')
|
|
281
284
|
return log.decode('utf-8') if job_result == JobResult.GOOD else ''
|
{uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/core/docker_compose_shell/interface.py
RENAMED
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
+
from asyncio import subprocess
|
|
3
|
+
from dataclasses import dataclass
|
|
2
4
|
import os
|
|
5
|
+
from pathlib import Path
|
|
3
6
|
import pprint
|
|
4
7
|
import shlex
|
|
5
8
|
import sys
|
|
6
|
-
from asyncio import subprocess
|
|
7
|
-
from dataclasses import dataclass
|
|
8
|
-
from pathlib import Path
|
|
9
9
|
from typing import Callable
|
|
10
|
-
from typing import Coroutine
|
|
11
10
|
from typing import NamedTuple
|
|
12
11
|
|
|
13
12
|
from rich.text import Text
|
|
@@ -19,13 +18,9 @@ from uber_compose.core.utils.process_command_output import process_output_till_d
|
|
|
19
18
|
from uber_compose.core.utils.shell_process import parse_process_command_name
|
|
20
19
|
from uber_compose.helpers.jobs_result import JobResult
|
|
21
20
|
from uber_compose.helpers.jobs_result import OperationError
|
|
22
|
-
from uber_compose.output.console import CONSOLE
|
|
23
21
|
from uber_compose.output.console import Logger
|
|
24
22
|
from uber_compose.output.styles import Style
|
|
25
23
|
|
|
26
|
-
DC_BIN = '/usr/bin/docker'
|
|
27
|
-
COMPOSE = f'{DC_BIN} compose'
|
|
28
|
-
|
|
29
24
|
|
|
30
25
|
class NoDockerCompose(BaseException):
|
|
31
26
|
...
|
|
@@ -68,28 +63,25 @@ class ComposeShellInterface:
|
|
|
68
63
|
self.execution_envs |= execution_envs
|
|
69
64
|
self.extra_exec_params = self.cfg_constants.docker_compose_extra_exec_params
|
|
70
65
|
|
|
66
|
+
self.compose_bin = '/usr/bin/docker'
|
|
67
|
+
self.compose_cmd = f'{self.compose_bin} compose'
|
|
68
|
+
|
|
71
69
|
if self.cfg_constants.cli_compose_util_override:
|
|
72
70
|
logger.system_commands(
|
|
73
71
|
f'Using overridden {self.cfg_constants.cli_compose_util_override} CLI compose command'
|
|
74
72
|
)
|
|
73
|
+
self.compose_bin = self.cfg_constants.cli_compose_util_override
|
|
74
|
+
self.compose_cmd = self.cfg_constants.cli_compose_util_override
|
|
75
75
|
|
|
76
|
-
|
|
77
|
-
global DC_BIN
|
|
78
|
-
DC_BIN = self.cfg_constants.cli_compose_util_override
|
|
79
|
-
|
|
80
|
-
# for binary usage
|
|
81
|
-
global COMPOSE
|
|
82
|
-
COMPOSE = self.cfg_constants.cli_compose_util_override
|
|
83
|
-
|
|
84
|
-
# check if DC_BIN exists
|
|
85
|
-
if not Path(DC_BIN).exists():
|
|
76
|
+
if not Path(self.compose_bin).exists():
|
|
86
77
|
raise NoDockerCompose(
|
|
87
|
-
f'Docker Compose binary not found at {
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
78
|
+
f'Docker Compose binary not found at {self.compose_bin}. '
|
|
79
|
+
'Please install Docker Client with compose: \n'
|
|
80
|
+
' Alpine - apk add docker-cli docker-cli-compose\n'
|
|
81
|
+
' Debian/Ubuntu - apt install docker-ce-cli docker-compose-plugin'
|
|
82
|
+
)
|
|
92
83
|
|
|
84
|
+
def _prepare_env_root(self, env: dict = None, root: Path | str = None) -> tuple[dict, str]:
|
|
93
85
|
if env is None:
|
|
94
86
|
env = {}
|
|
95
87
|
env = self.execution_envs | env
|
|
@@ -97,8 +89,16 @@ class ComposeShellInterface:
|
|
|
97
89
|
if root is None:
|
|
98
90
|
root = self.in_docker_project_root
|
|
99
91
|
|
|
92
|
+
return env, root
|
|
93
|
+
|
|
94
|
+
@retry(attempts=10, delay=1, until=lambda x: x == JobResult.BAD)
|
|
95
|
+
async def dc_state(self, env: dict = None, root: Path | str = None) -> ServicesComposeState | OperationError:
|
|
96
|
+
sys.stdout.flush()
|
|
97
|
+
|
|
98
|
+
env, root = self._prepare_env_root(env, root)
|
|
99
|
+
|
|
100
100
|
process = await asyncio.create_subprocess_shell(
|
|
101
|
-
cmd := f"{
|
|
101
|
+
cmd := f"{self.compose_cmd} --project-directory {root}" + " ps -a --format='{{json .}}'",
|
|
102
102
|
env=env,
|
|
103
103
|
cwd=root,
|
|
104
104
|
stdout=subprocess.PIPE,
|
|
@@ -122,15 +122,10 @@ class ComposeShellInterface:
|
|
|
122
122
|
async def dc_up(self, services: list[str], env: dict = None, root: Path | str = None) -> JobResult | OperationError:
|
|
123
123
|
sys.stdout.flush()
|
|
124
124
|
|
|
125
|
-
|
|
126
|
-
env = {}
|
|
127
|
-
env = self.execution_envs | env
|
|
128
|
-
|
|
129
|
-
if root is None:
|
|
130
|
-
root = self.in_docker_project_root
|
|
125
|
+
env, root = self._prepare_env_root(env, root)
|
|
131
126
|
|
|
132
127
|
process = await asyncio.create_subprocess_shell(
|
|
133
|
-
cmd := f'{
|
|
128
|
+
cmd := f'{self.compose_cmd} --project-directory {root} up --timestamps --no-deps --pull missing '
|
|
134
129
|
'--timeout 300 -d ' + ' '.join(services),
|
|
135
130
|
env=env,
|
|
136
131
|
cwd=root,
|
|
@@ -157,19 +152,14 @@ class ComposeShellInterface:
|
|
|
157
152
|
) -> tuple[JobResult, bytes] | tuple[OperationError, None]:
|
|
158
153
|
sys.stdout.flush()
|
|
159
154
|
|
|
160
|
-
|
|
161
|
-
env = {}
|
|
162
|
-
env = self.execution_envs | env
|
|
163
|
-
|
|
164
|
-
if root is None:
|
|
165
|
-
root = self.in_docker_project_root
|
|
155
|
+
env, root = self._prepare_env_root(env, root)
|
|
166
156
|
|
|
167
157
|
if services is None:
|
|
168
158
|
services = []
|
|
169
159
|
services = ' '.join(services)
|
|
170
160
|
|
|
171
161
|
process = await asyncio.create_subprocess_shell(
|
|
172
|
-
cmd := f'{
|
|
162
|
+
cmd := f'{self.compose_cmd} --project-directory {root} logs {logs_param} {services}',
|
|
173
163
|
env=env,
|
|
174
164
|
cwd=root,
|
|
175
165
|
stdout=asyncio.subprocess.PIPE,
|
|
@@ -186,9 +176,12 @@ class ComposeShellInterface:
|
|
|
186
176
|
state_result = await self.dc_state()
|
|
187
177
|
if state_result == JobResult.GOOD:
|
|
188
178
|
return OperationError(
|
|
189
|
-
f'Command: {cmd}\nStdout:\n{stdout}\n\nStderr:\n{stderr}\n\
|
|
179
|
+
f'Command: {cmd}\nStdout:\n{stdout}\n\nStderr:\n{stderr}\n\n'
|
|
180
|
+
f'ComposeState:\n{state_result.as_rich_text()}'
|
|
190
181
|
), None
|
|
191
|
-
return OperationError(
|
|
182
|
+
return OperationError(
|
|
183
|
+
f'Command: {cmd}\nStdout:\n{stdout}\n\nStderr:\n{stderr}\n\nComposeState:\n{state_result}'
|
|
184
|
+
), None
|
|
192
185
|
|
|
193
186
|
return JobResult.GOOD, stdout
|
|
194
187
|
|
|
@@ -205,19 +198,17 @@ class ComposeShellInterface:
|
|
|
205
198
|
f'-e {key}={shlex.quote(str(value))}' for key, value in extra_env.items()
|
|
206
199
|
)
|
|
207
200
|
|
|
208
|
-
|
|
209
|
-
env = {}
|
|
210
|
-
env = self.execution_envs | env
|
|
211
|
-
|
|
212
|
-
if root is None:
|
|
213
|
-
root = self.in_docker_project_root
|
|
201
|
+
env, root = self._prepare_env_root(env, root)
|
|
214
202
|
|
|
215
203
|
detached_param_str = '-d' if detached else ''
|
|
216
204
|
detached_end_str = ' &' if detached else ''
|
|
217
205
|
detached_end_str = ''
|
|
218
206
|
|
|
219
207
|
process = await asyncio.create_subprocess_shell(
|
|
220
|
-
cmd :=
|
|
208
|
+
cmd := (
|
|
209
|
+
f'{self.compose_cmd} --project-directory {root} exec {extra_env_str} '
|
|
210
|
+
f'{detached_param_str} {self.extra_exec_params} {container} {cmd} {detached_end_str}'
|
|
211
|
+
),
|
|
221
212
|
env=env,
|
|
222
213
|
cwd=root,
|
|
223
214
|
stdout=asyncio.subprocess.PIPE,
|
|
@@ -237,7 +228,8 @@ class ComposeShellInterface:
|
|
|
237
228
|
state_result = await self.dc_state()
|
|
238
229
|
if state_result == JobResult.GOOD:
|
|
239
230
|
return OperationError(
|
|
240
|
-
f'Command: {cmd}\nStdout:\n{stdout}\n\nStderr:\n{stderr}\n\
|
|
231
|
+
f'Command: {cmd}\nStdout:\n{stdout}\n\nStderr:\n{stderr}\n\n'
|
|
232
|
+
f'ComposeState:\n{state_result.as_rich_text()}'
|
|
241
233
|
), stdout, stderr
|
|
242
234
|
return OperationError(
|
|
243
235
|
f'Command: {cmd}\nStdout:\n{stdout}\n\nStderr:\n{stderr}\n\nComposeState:\n{state_result}'
|
|
@@ -249,17 +241,19 @@ class ComposeShellInterface:
|
|
|
249
241
|
cmd: str,
|
|
250
242
|
env: dict = None,
|
|
251
243
|
root: Path | str = None,
|
|
252
|
-
) ->
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
root = self.in_docker_project_root
|
|
244
|
+
) -> (
|
|
245
|
+
tuple[JobResult, bytes, bytes]
|
|
246
|
+
| list[int]
|
|
247
|
+
| tuple[OperationError, bytes, bytes]
|
|
248
|
+
):
|
|
249
|
+
env, root = self._prepare_env_root(env, root)
|
|
259
250
|
|
|
260
251
|
cmd = parse_process_command_name(cmd)
|
|
261
252
|
process_state = await asyncio.create_subprocess_shell(
|
|
262
|
-
check_cmd :=
|
|
253
|
+
check_cmd := (
|
|
254
|
+
f'{self.compose_cmd} --project-directory {root} exec '
|
|
255
|
+
f'{self.extra_exec_params} {container} pidof {cmd}'
|
|
256
|
+
),
|
|
263
257
|
env=env,
|
|
264
258
|
cwd=root,
|
|
265
259
|
stdout=asyncio.subprocess.PIPE,
|
|
@@ -272,7 +266,9 @@ class ComposeShellInterface:
|
|
|
272
266
|
check_output = stdout.decode('utf-8')
|
|
273
267
|
sys_error = stderr.decode("utf-8")
|
|
274
268
|
|
|
275
|
-
self.logger.system_commands_debug(
|
|
269
|
+
self.logger.system_commands_debug(
|
|
270
|
+
f'Pids of command "{cmd}" in "{container}":\n {check_output} \nErr: {sys_error}'
|
|
271
|
+
)
|
|
276
272
|
|
|
277
273
|
if check_output != '':
|
|
278
274
|
try:
|
|
@@ -295,15 +291,10 @@ class ComposeShellInterface:
|
|
|
295
291
|
env: dict = None,
|
|
296
292
|
root: Path | str = None,
|
|
297
293
|
) -> None:
|
|
298
|
-
|
|
299
|
-
env = {}
|
|
300
|
-
env = self.execution_envs | env
|
|
301
|
-
|
|
302
|
-
if root is None:
|
|
303
|
-
root = self.in_docker_project_root
|
|
294
|
+
env, root = self._prepare_env_root(env, root)
|
|
304
295
|
|
|
305
296
|
processes_state = await asyncio.create_subprocess_shell(
|
|
306
|
-
|
|
297
|
+
f'{self.compose_cmd} --project-directory {root} exec {self.extra_exec_params} {container} top -n 1',
|
|
307
298
|
env=env,
|
|
308
299
|
cwd=root,
|
|
309
300
|
stdout=asyncio.subprocess.PIPE,
|
|
@@ -403,15 +394,10 @@ class ComposeShellInterface:
|
|
|
403
394
|
self.logger.stage_info(f'Downing {services} containers')
|
|
404
395
|
sys.stdout.flush()
|
|
405
396
|
|
|
406
|
-
|
|
407
|
-
env = {}
|
|
408
|
-
env = self.execution_envs | env
|
|
409
|
-
|
|
410
|
-
if root is None:
|
|
411
|
-
root = self.in_docker_project_root
|
|
397
|
+
env, root = self._prepare_env_root(env, root)
|
|
412
398
|
|
|
413
399
|
process = await asyncio.create_subprocess_shell(
|
|
414
|
-
cmd := f'{
|
|
400
|
+
cmd := f'{self.compose_cmd} --project-directory {root} down ' + ' '.join(services),
|
|
415
401
|
env=env,
|
|
416
402
|
cwd=root,
|
|
417
403
|
stdout=asyncio.subprocess.PIPE,
|
|
@@ -433,8 +419,11 @@ class ComposeShellInterface:
|
|
|
433
419
|
state_result = await self.dc_state()
|
|
434
420
|
if state_result == JobResult.GOOD:
|
|
435
421
|
return OperationError(
|
|
436
|
-
f'Command: {cmd}\nStdout:\n{stdout}\n\nStderr:\n{stderr}\n\
|
|
422
|
+
f'Command: {cmd}\nStdout:\n{stdout}\n\nStderr:\n{stderr}\n\n'
|
|
423
|
+
f'ComposeState:\n{state_result.as_rich_text()}'
|
|
437
424
|
)
|
|
438
|
-
return OperationError(
|
|
425
|
+
return OperationError(
|
|
426
|
+
f'Command: {cmd}\nStdout:\n{stdout}\n\nStderr:\n{stderr}\n\nComposeState:\n{state_result}'
|
|
427
|
+
)
|
|
439
428
|
|
|
440
429
|
return JobResult.GOOD
|
{uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/core/docker_compose_shell/types.py
RENAMED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import json
|
|
2
1
|
from dataclasses import dataclass
|
|
2
|
+
import json
|
|
3
3
|
from typing import Callable
|
|
4
4
|
from typing import Iterator
|
|
5
5
|
|
|
@@ -173,7 +173,7 @@ class ServicesComposeState:
|
|
|
173
173
|
if service_state.check(label, value):
|
|
174
174
|
return service_state
|
|
175
175
|
|
|
176
|
-
return
|
|
176
|
+
return None
|
|
177
177
|
|
|
178
178
|
def __len__(self):
|
|
179
179
|
return len(self._services)
|
|
@@ -1,17 +1,15 @@
|
|
|
1
|
-
import pprint
|
|
2
|
-
from itertools import groupby
|
|
3
1
|
from pathlib import Path
|
|
2
|
+
import pprint
|
|
4
3
|
|
|
5
4
|
from rich.text import Text
|
|
5
|
+
|
|
6
6
|
from uber_compose.core.constants import Constants
|
|
7
7
|
from uber_compose.core.docker_compose_shell.interface import ComposeShellInterface
|
|
8
8
|
from uber_compose.core.docker_compose_shell.types import ServicesComposeState
|
|
9
|
-
from uber_compose.core.utils.compose_instance_cfg import get_service_map
|
|
10
9
|
from uber_compose.env_description.env_types import Environment
|
|
11
10
|
from uber_compose.helpers.bytes_pickle import base64_pickled
|
|
12
11
|
from uber_compose.helpers.bytes_pickle import debase64_pickled
|
|
13
12
|
from uber_compose.helpers.labels import Label
|
|
14
|
-
from uber_compose.output.console import CONSOLE
|
|
15
13
|
from uber_compose.output.console import Logger
|
|
16
14
|
from uber_compose.output.styles import Style
|
|
17
15
|
from uber_compose.utils.docker_compose_files_path import get_absolute_compose_files
|
|
@@ -29,7 +27,9 @@ class SystemDockerCompose:
|
|
|
29
27
|
scan_for_compose_files(inner_project_root, self.cfg_constants.docker_compose_files_scan_depth)
|
|
30
28
|
)
|
|
31
29
|
self.logger.commands(f'All found compose files: {self.default_compose_files}')
|
|
32
|
-
assert self.default_compose_files,
|
|
30
|
+
assert self.default_compose_files, (
|
|
31
|
+
f'No docker-compose files found in the project root {inner_project_root} directory.'
|
|
32
|
+
)
|
|
33
33
|
|
|
34
34
|
self.default_environment = make_default_environment(
|
|
35
35
|
compose_files=get_absolute_compose_files(self.default_compose_files, inner_project_root),
|
|
@@ -54,7 +54,9 @@ class SystemDockerCompose:
|
|
|
54
54
|
services_state = await self.dc_shell.dc_state()
|
|
55
55
|
services_states = services_state.get_all_for(
|
|
56
56
|
lambda service_state: (
|
|
57
|
-
service_state.check(
|
|
57
|
+
service_state.check(
|
|
58
|
+
Label.ENV_CONFIG_TEMPLATE, base64_pickled(Environment.from_environment(config_template))
|
|
59
|
+
)
|
|
58
60
|
and service_state.check(Label.COMPOSE_FILES, compose_files)
|
|
59
61
|
)
|
|
60
62
|
)
|
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import collections
|
|
2
|
-
import os
|
|
3
|
-
import shutil
|
|
4
|
-
from _warnings import warn
|
|
5
2
|
from copy import deepcopy
|
|
3
|
+
import os
|
|
6
4
|
from pathlib import Path
|
|
5
|
+
import shutil
|
|
7
6
|
|
|
8
7
|
import yaml
|
|
9
|
-
from uber_compose.env_description.env_types import OverridenService
|
|
10
8
|
|
|
11
9
|
from uber_compose.core.sequence_run_types import ComposeInstanceFiles
|
|
12
10
|
from uber_compose.core.sequence_run_types import EnvInstanceConfig
|
|
@@ -14,6 +12,7 @@ from uber_compose.core.utils.compose_instance_cfg import made_up_instance_compos
|
|
|
14
12
|
from uber_compose.env_description.env_types import Environment
|
|
15
13
|
from uber_compose.env_description.env_types import EventStage
|
|
16
14
|
from uber_compose.env_description.env_types import Handler
|
|
15
|
+
from uber_compose.env_description.env_types import OverridenService
|
|
17
16
|
from uber_compose.errors.migrations import ServicesMigrationsError
|
|
18
17
|
from uber_compose.helpers.bytes_pickle import base64_pickled
|
|
19
18
|
from uber_compose.helpers.labels import Label
|
|
@@ -110,7 +109,12 @@ def patch_service_set(dc_cfg: dict, services_map: dict[str, str] | None):
|
|
|
110
109
|
return new_dc_cfg
|
|
111
110
|
|
|
112
111
|
|
|
113
|
-
def patch_envs(
|
|
112
|
+
def patch_envs(
|
|
113
|
+
dc_cfg: dict,
|
|
114
|
+
services_environment_vars: Environment,
|
|
115
|
+
run_id,
|
|
116
|
+
overridden_services: list[OverridenService] | None = None,
|
|
117
|
+
) -> dict:
|
|
114
118
|
# TODO envs order and override question!!
|
|
115
119
|
# if we overrides env, should we save order? or insert before, for allow to override codegen
|
|
116
120
|
new_dc_cfg = deepcopy(dc_cfg)
|
|
@@ -160,7 +164,11 @@ def patch_envs(dc_cfg: dict, services_environment_vars: Environment, run_id, ove
|
|
|
160
164
|
return new_dc_cfg
|
|
161
165
|
|
|
162
166
|
|
|
163
|
-
def patch_services_names(
|
|
167
|
+
def patch_services_names(
|
|
168
|
+
dc_cfg: dict,
|
|
169
|
+
services_map: dict[str, str],
|
|
170
|
+
overridden_services: list[OverridenService] | None = None,
|
|
171
|
+
) -> dict:
|
|
164
172
|
new_service_dc_cfg = deepcopy(dc_cfg)
|
|
165
173
|
new_service_dc_cfg['services'] = {}
|
|
166
174
|
|
|
@@ -438,7 +446,8 @@ def parse_docker_compose(file_paths):
|
|
|
438
446
|
|
|
439
447
|
|
|
440
448
|
def topological_sort(services_dict):
|
|
441
|
-
from collections import defaultdict
|
|
449
|
+
from collections import defaultdict
|
|
450
|
+
from collections import deque
|
|
442
451
|
|
|
443
452
|
# Prepare the adjacency list
|
|
444
453
|
graph = defaultdict(list)
|
|
@@ -456,7 +465,6 @@ def topological_sort(services_dict):
|
|
|
456
465
|
topologically_sorted_services = []
|
|
457
466
|
|
|
458
467
|
while zero_in_degree_queue:
|
|
459
|
-
level_services = list(zero_in_degree_queue)
|
|
460
468
|
for _ in range(len(zero_in_degree_queue)):
|
|
461
469
|
node = zero_in_degree_queue.popleft()
|
|
462
470
|
topologically_sorted_services.append(node)
|
|
@@ -12,9 +12,9 @@ def make_debug_bash_env(env_config_compose_instance: ComposeInstanceFiles,
|
|
|
12
12
|
)
|
|
13
13
|
|
|
14
14
|
with open(env_config_compose_instance.directory / '.env', 'w') as f:
|
|
15
|
-
f.write(
|
|
15
|
+
f.write('alias dc="docker-compose --project-directory ."\n')
|
|
16
16
|
f.write(f'export COMPOSE_FILE={new_external_compose_file}\n')
|
|
17
|
-
f.write(
|
|
17
|
+
f.write('alias deactivate-env="unset COMPOSE_FILE"\n')
|
|
18
18
|
# TODO Envs
|
|
19
19
|
# for k,value in updated_envs.items():
|
|
20
20
|
# f.write(f'{k}={value}\n')
|
|
@@ -42,7 +42,7 @@ async def wait_all_services_up(attempts, delay_s, logger_func: Callable, get_com
|
|
|
42
42
|
]
|
|
43
43
|
|
|
44
44
|
if not non_ready_services:
|
|
45
|
-
logger_func(Text(
|
|
45
|
+
logger_func(Text(' ✔ All services up\n', style=output_style.mark_neutral))
|
|
46
46
|
return JobResult.GOOD
|
|
47
47
|
|
|
48
48
|
# some service not ready routine
|
|
@@ -54,7 +54,7 @@ async def wait_all_services_up(attempts, delay_s, logger_func: Callable, get_com
|
|
|
54
54
|
return JobResult.BAD
|
|
55
55
|
|
|
56
56
|
if state_keeper.not_in_state(services_state):
|
|
57
|
-
logger_func(Text(
|
|
57
|
+
logger_func(Text(' ✗ Still not ready services:', style=output_style.bad))
|
|
58
58
|
logger_func(services_state.as_rich_text(
|
|
59
59
|
filter=is_service_in_bad_state,
|
|
60
60
|
style=output_style
|
|
@@ -84,6 +84,8 @@ class Service(NamedTuple):
|
|
|
84
84
|
mode: ServiceMode = ServiceMode.ON
|
|
85
85
|
|
|
86
86
|
def __eq__(self, other):
|
|
87
|
+
if not isinstance(other, Service):
|
|
88
|
+
return False
|
|
87
89
|
return self.name == other.name
|
|
88
90
|
|
|
89
91
|
def __repr__(self):
|
|
@@ -129,7 +131,9 @@ def remove_dups(*services: Service) -> List[Service]:
|
|
|
129
131
|
|
|
130
132
|
class Environment: # TODO rename Environment
|
|
131
133
|
@classmethod
|
|
132
|
-
def from_environment(
|
|
134
|
+
def from_environment(
|
|
135
|
+
cls, env: 'Environment', *services: Service, description='', services_override=None,
|
|
136
|
+
) -> 'Environment':
|
|
133
137
|
# TODO duplicated services merging
|
|
134
138
|
description = description or env._description
|
|
135
139
|
services_overrides = []
|
|
@@ -139,7 +143,9 @@ class Environment: # TODO rename Environment
|
|
|
139
143
|
services_overrides += env._services_override
|
|
140
144
|
return Environment(*env._services, *services, description=description, services_override=services_overrides)
|
|
141
145
|
|
|
142
|
-
def __init__(
|
|
146
|
+
def __init__(
|
|
147
|
+
self, *services: Service | str, description='', services_override: List[OverridenService] | None = None,
|
|
148
|
+
):
|
|
143
149
|
# TODO duplicated services merging
|
|
144
150
|
self._description = description
|
|
145
151
|
services = [
|
|
@@ -24,5 +24,6 @@ def calc_broken_services(
|
|
|
24
24
|
if service.service.name not in services_state.get_services_names()
|
|
25
25
|
]
|
|
26
26
|
return list(
|
|
27
|
-
set(non_ready_services) | set(not_included_services)
|
|
27
|
+
set(non_ready_services) | set(not_included_services)
|
|
28
|
+
- set(excluded_from_check) - set(skipped_overridden_services)
|
|
28
29
|
)
|
|
@@ -37,7 +37,10 @@ class LogPolicySet:
|
|
|
37
37
|
|
|
38
38
|
class LogPolicy:
|
|
39
39
|
DEFAULT = LogPolicySet(LogEvents.STAGE, LogEvents.ERROR)
|
|
40
|
-
VERBOSE = LogPolicySet(
|
|
40
|
+
VERBOSE = LogPolicySet(
|
|
41
|
+
LogEvents.STAGE, LogEvents.STAGE_INFO, LogEvents.STAGE_DETAILS,
|
|
42
|
+
LogEvents.ERROR, LogEvents.COMMANDS, LogEvents.COMMAND_OUTPUT,
|
|
43
|
+
)
|
|
41
44
|
DEBUG = LogPolicySet(LogEvents.GLOBAL_DEBUG)
|
|
42
45
|
|
|
43
46
|
@staticmethod
|
|
@@ -51,7 +54,7 @@ class LogPolicy:
|
|
|
51
54
|
|
|
52
55
|
# TODO collect all into file and on level in stdout
|
|
53
56
|
class Logger:
|
|
54
|
-
def __init__(self, log_policy: LogPolicySet = None, cfg_constants
|
|
57
|
+
def __init__(self, log_policy: LogPolicySet = None, cfg_constants=None):
|
|
55
58
|
if cfg_constants is None:
|
|
56
59
|
cfg_constants = Constants()
|
|
57
60
|
if log_policy is None:
|
|
@@ -64,7 +67,7 @@ class Logger:
|
|
|
64
67
|
if LogEvents.GLOBAL_DEBUG in self.log_policy:
|
|
65
68
|
if line_no == 0:
|
|
66
69
|
self.stream.print(f'{level}:', style='bold grey15 on green', end='')
|
|
67
|
-
self.stream.print(
|
|
70
|
+
self.stream.print('', style='white on default')
|
|
68
71
|
self.stream.print(text)
|
|
69
72
|
return
|
|
70
73
|
|
|
@@ -1,19 +1,17 @@
|
|
|
1
|
-
import shlex
|
|
2
1
|
from dataclasses import dataclass
|
|
3
|
-
|
|
2
|
+
import shlex
|
|
4
3
|
from typing import Callable
|
|
5
4
|
from uuid import uuid4
|
|
6
5
|
|
|
7
6
|
from rich.text import Text
|
|
8
7
|
|
|
9
|
-
from uber_compose.core.docker_compose_shell.types import ExecLifeCyclePolicy
|
|
10
|
-
from uber_compose.core.docker_compose_shell.interface import TimeOutCheck
|
|
11
|
-
from uber_compose.core.docker_compose_shell.types import ServicesComposeState
|
|
12
|
-
|
|
13
8
|
from uber_compose.core.constants import Constants
|
|
14
9
|
from uber_compose.core.docker_compose import ComposeInstance
|
|
15
10
|
from uber_compose.core.docker_compose_shell.interface import ComposeShellInterface
|
|
16
11
|
from uber_compose.core.docker_compose_shell.interface import ProcessExit
|
|
12
|
+
from uber_compose.core.docker_compose_shell.interface import TimeOutCheck
|
|
13
|
+
from uber_compose.core.docker_compose_shell.types import ExecLifeCyclePolicy
|
|
14
|
+
from uber_compose.core.docker_compose_shell.types import ServicesComposeState
|
|
17
15
|
from uber_compose.core.sequence_run_types import DEFAULT_ENV_ID
|
|
18
16
|
from uber_compose.core.system_docker_compose import SystemDockerCompose
|
|
19
17
|
from uber_compose.core.utils.compose_instance_cfg import get_new_env_id
|
|
@@ -71,9 +69,30 @@ class SystemUberCompose:
|
|
|
71
69
|
services_override: list[OverridenService] | None = None,
|
|
72
70
|
) -> ReadyEnv:
|
|
73
71
|
|
|
72
|
+
compose_files = self._resolve_compose_files(compose_files)
|
|
73
|
+
config_template = self._resolve_config_template(config_template, compose_files, services_override)
|
|
74
|
+
|
|
75
|
+
services_state = await self._get_current_state(config_template, compose_files)
|
|
76
|
+
|
|
77
|
+
reusable_env = self._find_reusable_env(services_state, config_template, force_restart)
|
|
78
|
+
if reusable_env is not None:
|
|
79
|
+
return reusable_env
|
|
80
|
+
|
|
81
|
+
self._log_inflight_and_restart_info(services_state, force_restart)
|
|
82
|
+
|
|
83
|
+
return await self._start_new_environment(config_template, compose_files, release_id, parallelism_limit)
|
|
84
|
+
|
|
85
|
+
def _resolve_compose_files(self, compose_files: str | None) -> str:
|
|
74
86
|
if not compose_files:
|
|
75
87
|
compose_files = self.system_docker_compose.get_default_compose_files()
|
|
76
|
-
|
|
88
|
+
return compose_files
|
|
89
|
+
|
|
90
|
+
def _resolve_config_template(
|
|
91
|
+
self,
|
|
92
|
+
config_template: Environment | None,
|
|
93
|
+
compose_files: str,
|
|
94
|
+
services_override: list[OverridenService] | None,
|
|
95
|
+
) -> Environment:
|
|
77
96
|
if not config_template:
|
|
78
97
|
config_template = make_default_environment(
|
|
79
98
|
compose_files=get_absolute_compose_files(compose_files, self.cfg_constants.in_docker_project_root_path),
|
|
@@ -82,6 +101,9 @@ class SystemUberCompose:
|
|
|
82
101
|
if services_override:
|
|
83
102
|
config_template = config_template.from_environment(config_template, services_override=services_override)
|
|
84
103
|
|
|
104
|
+
return config_template
|
|
105
|
+
|
|
106
|
+
async def _get_current_state(self, config_template: Environment, compose_files: str) -> ServicesComposeState:
|
|
85
107
|
self.logger.stage_debug(
|
|
86
108
|
f'Searching for environment {config_template} with template: {base64_pickled(config_template)}'
|
|
87
109
|
)
|
|
@@ -92,28 +114,50 @@ class SystemUberCompose:
|
|
|
92
114
|
self.logger.stage_debug(
|
|
93
115
|
f'Found environments containers: {services_state}'
|
|
94
116
|
)
|
|
117
|
+
return services_state
|
|
118
|
+
|
|
119
|
+
def _should_restart(self, services_state: ServicesComposeState, broken_services: list, force_restart: bool) -> bool:
|
|
120
|
+
return len(services_state) == 0 or len(broken_services) != 0 or force_restart
|
|
121
|
+
|
|
122
|
+
def _find_reusable_env(
|
|
123
|
+
self,
|
|
124
|
+
services_state: ServicesComposeState,
|
|
125
|
+
config_template: Environment,
|
|
126
|
+
force_restart: bool,
|
|
127
|
+
) -> ReadyEnv | None:
|
|
95
128
|
broken_services = calc_broken_services(services_state, config_template, self.cfg_constants.non_stop_containers)
|
|
96
129
|
if broken_services:
|
|
97
130
|
self.logger.stage(
|
|
98
131
|
Text(
|
|
99
|
-
|
|
132
|
+
'Not started or not ready containers in current environment: ', style=Style.suspicious
|
|
100
133
|
).append(Text(f'{broken_services}', style=Style.regular))
|
|
101
134
|
)
|
|
102
|
-
if len(services_state) != 0 and len(broken_services) == 0 and not force_restart:
|
|
103
|
-
existing_env_id = services_state.get_any().labels.get(Label.ENV_ID, None)
|
|
104
|
-
env_config = debase64_pickled(services_state.get_any().labels.get(Label.ENV_CONFIG))
|
|
105
|
-
_for = f' for {config_template.description}' if config_template.description else ''
|
|
106
|
-
self.logger.stage_details(Text(
|
|
107
|
-
f'Found suitable{_for} ready env: ', style=Style.info
|
|
108
|
-
).append(Text(existing_env_id, style=Style.mark)))
|
|
109
|
-
|
|
110
|
-
last_release_id = services_state.get_any().labels.get(Label.RELEASE_ID)
|
|
111
|
-
self.last_release_id = last_release_id
|
|
112
|
-
return ReadyEnv(existing_env_id, env_config, last_release_id)
|
|
113
135
|
|
|
136
|
+
if self._should_restart(services_state, broken_services, force_restart):
|
|
137
|
+
return None
|
|
138
|
+
|
|
139
|
+
existing_env_id = services_state.get_any().labels.get(Label.ENV_ID, None)
|
|
140
|
+
env_config = debase64_pickled(services_state.get_any().labels.get(Label.ENV_CONFIG))
|
|
141
|
+
_for = f' for {config_template.description}' if config_template.description else ''
|
|
142
|
+
self.logger.stage_details(Text(
|
|
143
|
+
f'Found suitable{_for} ready env: ', style=Style.info
|
|
144
|
+
).append(Text(existing_env_id, style=Style.mark)))
|
|
145
|
+
|
|
146
|
+
last_release_id = services_state.get_any().labels.get(Label.RELEASE_ID)
|
|
147
|
+
self.last_release_id = last_release_id
|
|
148
|
+
return ReadyEnv(existing_env_id, env_config, last_release_id)
|
|
149
|
+
|
|
150
|
+
def _log_inflight_and_restart_info(self, services_state: ServicesComposeState, force_restart: bool) -> None:
|
|
151
|
+
in_flight_templates = [
|
|
152
|
+
(
|
|
153
|
+
debase64_pickled(service["labels"][Label.ENV_CONFIG_TEMPLATE]),
|
|
154
|
+
service["labels"][Label.ENV_CONFIG_TEMPLATE],
|
|
155
|
+
)
|
|
156
|
+
for service in services_state.as_json()
|
|
157
|
+
]
|
|
114
158
|
self.logger.stage_debug(
|
|
115
159
|
f'In-flight containers:\n'
|
|
116
|
-
f'{
|
|
160
|
+
f'{in_flight_templates}'
|
|
117
161
|
)
|
|
118
162
|
if force_restart:
|
|
119
163
|
self.logger.stage_details(Text(
|
|
@@ -123,7 +167,13 @@ class SystemUberCompose:
|
|
|
123
167
|
f'Previous state {services_state.as_json()}', style=Style.info
|
|
124
168
|
))
|
|
125
169
|
|
|
126
|
-
|
|
170
|
+
async def _start_new_environment(
|
|
171
|
+
self,
|
|
172
|
+
config_template: Environment,
|
|
173
|
+
compose_files: str,
|
|
174
|
+
release_id: str | None,
|
|
175
|
+
parallelism_limit: int,
|
|
176
|
+
) -> ReadyEnv:
|
|
127
177
|
_for = f' {config_template.description}' if config_template.description else ''
|
|
128
178
|
self.logger.stage(Text(f'Starting new{_for} environment', style=Style.info))
|
|
129
179
|
self.logger.stage_details(f'Use compose files: {compose_files}')
|
|
@@ -164,7 +214,7 @@ class SystemUberCompose:
|
|
|
164
214
|
|
|
165
215
|
# TODO check if ready by state checking
|
|
166
216
|
|
|
167
|
-
self.logger.stage_info(Text(
|
|
217
|
+
self.logger.stage_info(Text('New environment started'))
|
|
168
218
|
|
|
169
219
|
return ReadyEnv(
|
|
170
220
|
new_env_id,
|
|
@@ -191,7 +241,7 @@ class SystemUberCompose:
|
|
|
191
241
|
|
|
192
242
|
service_state = dc_state.get_all_for(
|
|
193
243
|
lambda service_state: service_state.check(Label.ENV_ID, env_id)
|
|
194
|
-
|
|
244
|
+
and service_state.check(Label.TEMPLATE_SERVICE_NAME, container)
|
|
195
245
|
)
|
|
196
246
|
if len(service_state.as_json()) != 1:
|
|
197
247
|
raise ValueError(f'Container {container} not found in environment {env_id}')
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
from uber_compose.env_description.env_types import Environment
|
|
2
2
|
from uber_compose.env_description.env_types import Service
|
|
3
|
-
from uber_compose.utils.docker_compose_files_path import get_absolute_compose_files
|
|
4
3
|
from uber_compose.utils.docker_compose_service_deps import parse_docker_compose_services_deps
|
|
5
4
|
|
|
6
5
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
from dataclasses import dataclass
|
|
1
2
|
import json
|
|
2
3
|
import re
|
|
3
|
-
from dataclasses import dataclass
|
|
4
4
|
from typing import Callable
|
|
5
5
|
from typing import Generic
|
|
6
6
|
from typing import List
|
|
@@ -9,13 +9,10 @@ from typing import Type
|
|
|
9
9
|
from typing import TypeVar
|
|
10
10
|
from warnings import warn
|
|
11
11
|
|
|
12
|
-
from uber_compose.core.docker_compose_shell.
|
|
13
|
-
from uber_compose.helpers.exec_result import ExecResult
|
|
14
|
-
|
|
12
|
+
from uber_compose.core.docker_compose_shell.interface import ProcessExit
|
|
15
13
|
from uber_compose.core.docker_compose_shell.interface import TimeOutCheck
|
|
14
|
+
from uber_compose.core.docker_compose_shell.types import ExecLifeCyclePolicy
|
|
16
15
|
from uber_compose.uber_compose import SystemUberCompose
|
|
17
|
-
|
|
18
|
-
from uber_compose.core.docker_compose_shell.interface import ProcessExit
|
|
19
16
|
from uber_compose.uber_compose import TheUberCompose
|
|
20
17
|
|
|
21
18
|
|
|
@@ -246,7 +243,10 @@ class CommonJsonCli(Generic[TCommandResult]):
|
|
|
246
243
|
timeout = self._timeout
|
|
247
244
|
|
|
248
245
|
if container is None:
|
|
249
|
-
assert self._container is not None,
|
|
246
|
+
assert self._container is not None, (
|
|
247
|
+
'No container specified. Container must be specified either in method call '
|
|
248
|
+
'or in CommonJsonCli initialization'
|
|
249
|
+
)
|
|
250
250
|
container = self._container
|
|
251
251
|
|
|
252
252
|
if life_cycle_policy is None:
|
|
@@ -1,24 +1,22 @@
|
|
|
1
|
-
from collections import OrderedDict
|
|
2
1
|
import sys
|
|
3
2
|
from typing import Type
|
|
4
3
|
from typing import Union
|
|
5
4
|
from uuid import uuid4
|
|
6
5
|
|
|
7
|
-
import vedro.events
|
|
8
6
|
from rich.text import Text
|
|
9
7
|
from vedro.core import ConfigType
|
|
10
8
|
from vedro.core import Dispatcher
|
|
11
9
|
from vedro.core import Plugin
|
|
12
10
|
from vedro.core import PluginConfig
|
|
11
|
+
import vedro.events
|
|
13
12
|
from vedro.events import ArgParseEvent
|
|
14
13
|
from vedro.events import ArgParsedEvent
|
|
15
14
|
from vedro.events import ConfigLoadedEvent
|
|
16
15
|
from vedro.events import ScenarioRunEvent
|
|
17
16
|
from vedro.events import StartupEvent
|
|
18
17
|
|
|
19
|
-
from uber_compose import SystemUberCompose
|
|
20
|
-
from uber_compose.output.styles import Style
|
|
21
18
|
from uber_compose import Environment
|
|
19
|
+
from uber_compose import SystemUberCompose
|
|
22
20
|
from uber_compose.core.constants import Constants
|
|
23
21
|
from uber_compose.core.sequence_run_types import ComposeConfig
|
|
24
22
|
from uber_compose.env_description.env_types import DEFAULT_ENV_DESCRIPTION
|
|
@@ -26,6 +24,7 @@ from uber_compose.env_description.env_types import OverridenService
|
|
|
26
24
|
from uber_compose.helpers.health_policy import UpHealthPolicy
|
|
27
25
|
from uber_compose.output.console import LogPolicy
|
|
28
26
|
from uber_compose.output.console import Logger
|
|
27
|
+
from uber_compose.output.styles import Style
|
|
29
28
|
from uber_compose.uber_compose import TheUberCompose as TheUberCompose
|
|
30
29
|
from uber_compose.vedro_plugin.helpers.scenario_ordering import EnvTagsOrderer
|
|
31
30
|
from uber_compose.vedro_plugin.helpers.scenario_tag_processing import extract_scenario_config
|
|
@@ -35,12 +34,15 @@ from uber_compose.vedro_plugin.helpers.test_env_vars_setter import setup_env_for
|
|
|
35
34
|
|
|
36
35
|
DEFAULT_COMPOSE = 'default'
|
|
37
36
|
|
|
37
|
+
|
|
38
38
|
class VedroUberComposePlugin(Plugin):
|
|
39
39
|
def __init__(self, config: Type["VedroUberCompose"], client: SystemUberCompose = None) -> None:
|
|
40
40
|
super().__init__(config)
|
|
41
41
|
self._enabled = config.enabled
|
|
42
42
|
if config.default_env:
|
|
43
|
-
assert config.default_env._description == DEFAULT_ENV_DESCRIPTION,
|
|
43
|
+
assert config.default_env._description == DEFAULT_ENV_DESCRIPTION, (
|
|
44
|
+
'default_env must have description set to DEFAULT_ENV_DESCRIPTION'
|
|
45
|
+
)
|
|
44
46
|
self._default_env: Environment = config.default_env
|
|
45
47
|
|
|
46
48
|
# cli args
|
|
@@ -94,8 +96,18 @@ class VedroUberComposePlugin(Plugin):
|
|
|
94
96
|
try:
|
|
95
97
|
if self._compose_choice.parallel_env_limit >= len(needed_configs):
|
|
96
98
|
for env_config in list(needed_configs):
|
|
97
|
-
if env_config
|
|
99
|
+
if self._default_env is None and env_config is None:
|
|
100
|
+
self._logger.stage(
|
|
101
|
+
Text(
|
|
102
|
+
'[UberCompose] Warning: some scenarios has no "env" field set.\n'
|
|
103
|
+
'Consider adding "env = Envs.DEFAULT" to your scenario.',
|
|
104
|
+
style=Style.suspicious
|
|
105
|
+
)
|
|
106
|
+
)
|
|
107
|
+
|
|
108
|
+
if env_config is None:
|
|
98
109
|
env_config = self._default_env
|
|
110
|
+
|
|
99
111
|
await self._uber_compose_client.up(
|
|
100
112
|
config_template=env_config,
|
|
101
113
|
compose_files=self._compose_choice.compose_files,
|
|
@@ -115,7 +127,7 @@ class VedroUberComposePlugin(Plugin):
|
|
|
115
127
|
if self._just_up:
|
|
116
128
|
self._logger.stage(
|
|
117
129
|
Text(
|
|
118
|
-
|
|
130
|
+
'Environment has been started successfully. Exiting as requested.',
|
|
119
131
|
style=Style.good
|
|
120
132
|
)
|
|
121
133
|
)
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2.3.3
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
2.3.1
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
from uber_compose.core.docker_compose_shell.types import ExecLifeCyclePolicy
|
|
2
1
|
from uber_compose.core.docker_compose_shell.interface import ProcessExit
|
|
3
2
|
from uber_compose.core.docker_compose_shell.interface import TimeOutCheck
|
|
3
|
+
from uber_compose.core.docker_compose_shell.types import ExecLifeCyclePolicy
|
|
4
4
|
from uber_compose.core.sequence_run_types import ComposeConfig
|
|
5
5
|
from uber_compose.core.sequence_run_types import DEFAULT_ENV_ID
|
|
6
6
|
from uber_compose.env_description.env_types import DEFAULT_ENV_DESCRIPTION
|
|
File without changes
|
{uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/core/docker_compose_shell/__init__.py
RENAMED
|
File without changes
|
|
@@ -2,8 +2,8 @@ from pathlib import Path
|
|
|
2
2
|
from typing import NamedTuple
|
|
3
3
|
from typing import Union
|
|
4
4
|
|
|
5
|
-
from uber_compose.env_description.env_types import OverridenService
|
|
6
5
|
from uber_compose.env_description.env_types import Environment
|
|
6
|
+
from uber_compose.env_description.env_types import OverridenService
|
|
7
7
|
|
|
8
8
|
DEFAULT_ENV_ID = 'default_env_id'
|
|
9
9
|
|
|
File without changes
|
{uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/core/utils/compose_instance_cfg.py
RENAMED
|
File without changes
|
{uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/core/utils/process_command_output.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/utils/docker_compose_files_path.py
RENAMED
|
File without changes
|
{uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/utils/docker_compose_service_deps.py
RENAMED
|
File without changes
|
{uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/utils/search_docker_compose_files.py
RENAMED
|
File without changes
|
|
File without changes
|
{uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/vedro_plugin/base_structures/__init__.py
RENAMED
|
File without changes
|
{uber_compose-2.3.1a2002 → uber_compose-2.3.3}/uber_compose/vedro_plugin/helpers/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|