devservices 1.2.4__tar.gz → 1.3.1__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.
- {devservices-1.2.4 → devservices-1.3.1}/PKG-INFO +1 -7
- {devservices-1.2.4 → devservices-1.3.1}/README.md +10 -1
- {devservices-1.2.4 → devservices-1.3.1}/devservices/__init__.py +1 -0
- {devservices-1.2.4 → devservices-1.3.1}/devservices/commands/down.py +13 -12
- {devservices-1.2.4 → devservices-1.3.1}/devservices/commands/foreground.py +5 -6
- {devservices-1.2.4 → devservices-1.3.1}/devservices/commands/list_dependencies.py +4 -2
- {devservices-1.2.4 → devservices-1.3.1}/devservices/commands/logs.py +63 -20
- {devservices-1.2.4 → devservices-1.3.1}/devservices/commands/reset.py +5 -6
- {devservices-1.2.4 → devservices-1.3.1}/devservices/commands/serve.py +2 -2
- {devservices-1.2.4 → devservices-1.3.1}/devservices/commands/status.py +17 -17
- {devservices-1.2.4 → devservices-1.3.1}/devservices/commands/toggle.py +8 -9
- {devservices-1.2.4 → devservices-1.3.1}/devservices/commands/up.py +14 -10
- {devservices-1.2.4 → devservices-1.3.1}/devservices/configs/service_config.py +19 -3
- {devservices-1.2.4 → devservices-1.3.1}/devservices/constants.py +2 -2
- {devservices-1.2.4 → devservices-1.3.1}/devservices/main.py +6 -0
- {devservices-1.2.4 → devservices-1.3.1}/devservices/utils/console.py +6 -4
- {devservices-1.2.4 → devservices-1.3.1}/devservices/utils/dependencies.py +10 -12
- {devservices-1.2.4 → devservices-1.3.1}/devservices/utils/docker.py +60 -5
- devservices-1.3.1/devservices/utils/services.py +116 -0
- {devservices-1.2.4 → devservices-1.3.1}/devservices/utils/state.py +16 -0
- {devservices-1.2.4 → devservices-1.3.1}/devservices.egg-info/PKG-INFO +1 -7
- {devservices-1.2.4 → devservices-1.3.1}/devservices.egg-info/SOURCES.txt +0 -1
- {devservices-1.2.4 → devservices-1.3.1}/devservices.egg-info/requires.txt +0 -7
- {devservices-1.2.4 → devservices-1.3.1}/pyproject.toml +26 -5
- devservices-1.3.1/setup.cfg +4 -0
- {devservices-1.2.4 → devservices-1.3.1}/tests/commands/test_down.py +55 -3
- {devservices-1.2.4 → devservices-1.3.1}/tests/commands/test_list_dependencies.py +6 -4
- {devservices-1.2.4 → devservices-1.3.1}/tests/commands/test_logs.py +116 -9
- {devservices-1.2.4 → devservices-1.3.1}/tests/commands/test_status.py +8 -8
- {devservices-1.2.4 → devservices-1.3.1}/tests/commands/test_toggle.py +13 -5
- {devservices-1.2.4 → devservices-1.3.1}/tests/commands/test_up.py +45 -16
- {devservices-1.2.4 → devservices-1.3.1}/tests/configs/test_service_config.py +42 -1
- {devservices-1.2.4 → devservices-1.3.1}/tests/utils/test_dependencies.py +57 -5
- {devservices-1.2.4 → devservices-1.3.1}/tests/utils/test_docker.py +119 -3
- {devservices-1.2.4 → devservices-1.3.1}/tests/utils/test_services.py +27 -1
- devservices-1.2.4/devservices/utils/services.py +0 -75
- devservices-1.2.4/setup.cfg +0 -9
- {devservices-1.2.4 → devservices-1.3.1}/LICENSE.md +0 -0
- {devservices-1.2.4 → devservices-1.3.1}/devservices/commands/__init__.py +0 -0
- {devservices-1.2.4 → devservices-1.3.1}/devservices/commands/list_services.py +1 -1
- {devservices-1.2.4 → devservices-1.3.1}/devservices/commands/purge.py +1 -1
- {devservices-1.2.4 → devservices-1.3.1}/devservices/commands/update.py +1 -1
- {devservices-1.2.4 → devservices-1.3.1}/devservices/exceptions.py +0 -0
- {devservices-1.2.4 → devservices-1.3.1}/devservices/utils/__init__.py +0 -0
- {devservices-1.2.4 → devservices-1.3.1}/devservices/utils/check_for_update.py +0 -0
- {devservices-1.2.4 → devservices-1.3.1}/devservices/utils/devenv.py +0 -0
- {devservices-1.2.4 → devservices-1.3.1}/devservices/utils/docker_compose.py +2 -2
- {devservices-1.2.4 → devservices-1.3.1}/devservices/utils/file_lock.py +0 -0
- {devservices-1.2.4 → devservices-1.3.1}/devservices/utils/git.py +0 -0
- {devservices-1.2.4 → devservices-1.3.1}/devservices/utils/install_binary.py +0 -0
- {devservices-1.2.4 → devservices-1.3.1}/devservices/utils/supervisor.py +0 -0
- {devservices-1.2.4 → devservices-1.3.1}/devservices.egg-info/dependency_links.txt +0 -0
- {devservices-1.2.4 → devservices-1.3.1}/devservices.egg-info/entry_points.txt +0 -0
- {devservices-1.2.4 → devservices-1.3.1}/devservices.egg-info/top_level.txt +0 -0
- {devservices-1.2.4 → devservices-1.3.1}/testing/__init__.py +0 -0
- {devservices-1.2.4 → devservices-1.3.1}/testing/utils.py +0 -0
- {devservices-1.2.4 → devservices-1.3.1}/tests/__init__.py +0 -0
- {devservices-1.2.4 → devservices-1.3.1}/tests/commands/test_foreground.py +1 -1
- {devservices-1.2.4 → devservices-1.3.1}/tests/commands/test_list_services.py +0 -0
- {devservices-1.2.4 → devservices-1.3.1}/tests/commands/test_purge.py +0 -0
- {devservices-1.2.4 → devservices-1.3.1}/tests/commands/test_reset.py +0 -0
- {devservices-1.2.4 → devservices-1.3.1}/tests/commands/test_serve.py +1 -1
- {devservices-1.2.4 → devservices-1.3.1}/tests/commands/test_update.py +0 -0
- {devservices-1.2.4 → devservices-1.3.1}/tests/conftest.py +0 -0
- {devservices-1.2.4 → devservices-1.3.1}/tests/utils/test_check_for_update.py +0 -0
- {devservices-1.2.4 → devservices-1.3.1}/tests/utils/test_docker_compose.py +1 -1
- {devservices-1.2.4 → devservices-1.3.1}/tests/utils/test_git.py +0 -0
- {devservices-1.2.4 → devservices-1.3.1}/tests/utils/test_install_binary.py +0 -0
- {devservices-1.2.4 → devservices-1.3.1}/tests/utils/test_state.py +0 -0
- {devservices-1.2.4 → devservices-1.3.1}/tests/utils/test_supervisor.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: devservices
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.3.1
|
|
4
4
|
Requires-Python: >=3.11
|
|
5
5
|
License-File: LICENSE.md
|
|
6
6
|
Requires-Dist: pyyaml
|
|
@@ -8,10 +8,4 @@ Requires-Dist: sentry-devenv
|
|
|
8
8
|
Requires-Dist: sentry-sdk
|
|
9
9
|
Requires-Dist: packaging
|
|
10
10
|
Requires-Dist: supervisor
|
|
11
|
-
Provides-Extra: dev
|
|
12
|
-
Requires-Dist: black; extra == "dev"
|
|
13
|
-
Requires-Dist: mypy; extra == "dev"
|
|
14
|
-
Requires-Dist: pre-commit; extra == "dev"
|
|
15
|
-
Requires-Dist: pytest; extra == "dev"
|
|
16
|
-
Requires-Dist: types-PyYAML; extra == "dev"
|
|
17
11
|
Dynamic: license-file
|
|
@@ -33,7 +33,7 @@ NOTE: service-name is an optional parameter. If not provided, devservices will a
|
|
|
33
33
|
The recommended way to install devservices is through a virtualenv in the requirements.txt. Once that is installed and a devservices config file is added, you should be able to run `devservices up` to begin local development.
|
|
34
34
|
|
|
35
35
|
```
|
|
36
|
-
devservices==1.
|
|
36
|
+
devservices==1.3.1
|
|
37
37
|
```
|
|
38
38
|
|
|
39
39
|
### 2. Add devservices config files
|
|
@@ -50,6 +50,7 @@ The configuration file is a yaml file that looks like this:
|
|
|
50
50
|
# - local: A dependency that is defined in the config file. These dependencies do not have a remote field and must correspond to either a service defined in the 'services' section or a program defined in the 'x-programs' section.
|
|
51
51
|
# - remote: A dependency that is defined in the devservices directory in a remote repository. These configs are automatically fetched from the remote repository and installed. Any dependency with a remote field will be treated as a remote dependency. Example: https://github.com/getsentry/snuba/blob/59a5258ccbb502827ebc1d3b1bf80c607a3301bf/devservices/config.yml#L8
|
|
52
52
|
# - modes: A list of modes for the service. Each mode includes a list of dependencies that are used in that mode.
|
|
53
|
+
# - healthcheck_timeout: Optional. The number of seconds to wait for all containers to become healthy before failing. Defaults to 180.
|
|
53
54
|
x-sentry-service-config:
|
|
54
55
|
version: 0.1
|
|
55
56
|
service_name: example-service
|
|
@@ -167,3 +168,11 @@ networks:
|
|
|
167
168
|
name: devservices
|
|
168
169
|
external: true
|
|
169
170
|
```
|
|
171
|
+
|
|
172
|
+
## Dev
|
|
173
|
+
|
|
174
|
+
```sh
|
|
175
|
+
uv sync
|
|
176
|
+
direnv allow
|
|
177
|
+
pytest -n4
|
|
178
|
+
```
|
|
@@ -3,19 +3,19 @@ from __future__ import annotations
|
|
|
3
3
|
import concurrent.futures
|
|
4
4
|
import os
|
|
5
5
|
import subprocess
|
|
6
|
-
from argparse import _SubParsersAction
|
|
7
6
|
from argparse import ArgumentParser
|
|
8
7
|
from argparse import Namespace
|
|
8
|
+
from argparse import _SubParsersAction
|
|
9
9
|
|
|
10
10
|
from sentry_sdk import capture_exception
|
|
11
11
|
from sentry_sdk import logger as sentry_logger
|
|
12
12
|
|
|
13
13
|
from devservices.constants import CONFIG_FILE_NAME
|
|
14
14
|
from devservices.constants import DEPENDENCY_CONFIG_VERSION
|
|
15
|
-
from devservices.constants import DependencyType
|
|
16
15
|
from devservices.constants import DEVSERVICES_DEPENDENCIES_CACHE_DIR
|
|
17
16
|
from devservices.constants import DEVSERVICES_DEPENDENCIES_CACHE_DIR_KEY
|
|
18
17
|
from devservices.constants import DEVSERVICES_DIR_NAME
|
|
18
|
+
from devservices.constants import DependencyType
|
|
19
19
|
from devservices.exceptions import ConfigError
|
|
20
20
|
from devservices.exceptions import ConfigNotFoundError
|
|
21
21
|
from devservices.exceptions import DependencyError
|
|
@@ -25,16 +25,17 @@ from devservices.exceptions import SupervisorConfigError
|
|
|
25
25
|
from devservices.exceptions import SupervisorError
|
|
26
26
|
from devservices.utils.console import Console
|
|
27
27
|
from devservices.utils.console import Status
|
|
28
|
-
from devservices.utils.dependencies import construct_dependency_graph
|
|
29
28
|
from devservices.utils.dependencies import DependencyNode
|
|
29
|
+
from devservices.utils.dependencies import InstalledRemoteDependency
|
|
30
|
+
from devservices.utils.dependencies import construct_dependency_graph
|
|
30
31
|
from devservices.utils.dependencies import get_non_shared_remote_dependencies
|
|
31
32
|
from devservices.utils.dependencies import install_and_verify_dependencies
|
|
32
|
-
from devservices.utils.dependencies import InstalledRemoteDependency
|
|
33
33
|
from devservices.utils.docker_compose import DockerComposeCommand
|
|
34
34
|
from devservices.utils.docker_compose import get_docker_compose_commands_to_run
|
|
35
35
|
from devservices.utils.docker_compose import run_cmd
|
|
36
|
-
from devservices.utils.services import find_matching_service
|
|
37
36
|
from devservices.utils.services import Service
|
|
37
|
+
from devservices.utils.services import find_matching_service
|
|
38
|
+
from devservices.utils.services import get_active_service_names
|
|
38
39
|
from devservices.utils.state import ServiceRuntime
|
|
39
40
|
from devservices.utils.state import State
|
|
40
41
|
from devservices.utils.state import StateTables
|
|
@@ -71,7 +72,9 @@ def down(args: Namespace) -> None:
|
|
|
71
72
|
console = Console()
|
|
72
73
|
service_name = args.service_name
|
|
73
74
|
try:
|
|
74
|
-
service = find_matching_service(
|
|
75
|
+
service = find_matching_service(
|
|
76
|
+
service_name, config_path=getattr(args, "config", None)
|
|
77
|
+
)
|
|
75
78
|
except ConfigNotFoundError as e:
|
|
76
79
|
capture_exception(e, level="info")
|
|
77
80
|
console.failure(
|
|
@@ -90,9 +93,7 @@ def down(args: Namespace) -> None:
|
|
|
90
93
|
exclude_local = getattr(args, "exclude_local", False)
|
|
91
94
|
|
|
92
95
|
state = State()
|
|
93
|
-
|
|
94
|
-
started_services = set(state.get_service_entries(StateTables.STARTED_SERVICES))
|
|
95
|
-
active_services = starting_services.union(started_services)
|
|
96
|
+
active_services = get_active_service_names(clean_stale_entries=True)
|
|
96
97
|
if service.name not in active_services:
|
|
97
98
|
console.warning(f"{service.name} is not running")
|
|
98
99
|
return # Since exit(0) is captured as an internal_error by sentry
|
|
@@ -232,9 +233,9 @@ def bring_down_service(
|
|
|
232
233
|
|
|
233
234
|
# Set the environment variable for the local dependencies directory to be used by docker compose
|
|
234
235
|
current_env = os.environ.copy()
|
|
235
|
-
current_env[
|
|
236
|
-
|
|
237
|
-
|
|
236
|
+
current_env[DEVSERVICES_DEPENDENCIES_CACHE_DIR_KEY] = (
|
|
237
|
+
relative_local_dependency_directory
|
|
238
|
+
)
|
|
238
239
|
state = State()
|
|
239
240
|
|
|
240
241
|
# We want to ignore any dependencies that are set to run locally if we are excluding local dependencies
|
|
@@ -3,15 +3,15 @@ from __future__ import annotations
|
|
|
3
3
|
import os
|
|
4
4
|
import pty
|
|
5
5
|
import shlex
|
|
6
|
-
from argparse import _SubParsersAction
|
|
7
6
|
from argparse import ArgumentParser
|
|
8
7
|
from argparse import Namespace
|
|
8
|
+
from argparse import _SubParsersAction
|
|
9
9
|
|
|
10
10
|
from sentry_sdk import capture_exception
|
|
11
11
|
|
|
12
12
|
from devservices.constants import CONFIG_FILE_NAME
|
|
13
|
-
from devservices.constants import DependencyType
|
|
14
13
|
from devservices.constants import DEVSERVICES_DIR_NAME
|
|
14
|
+
from devservices.constants import DependencyType
|
|
15
15
|
from devservices.exceptions import ConfigError
|
|
16
16
|
from devservices.exceptions import ConfigNotFoundError
|
|
17
17
|
from devservices.exceptions import ServiceNotFoundError
|
|
@@ -19,6 +19,7 @@ from devservices.exceptions import SupervisorConfigError
|
|
|
19
19
|
from devservices.exceptions import SupervisorProcessError
|
|
20
20
|
from devservices.utils.console import Console
|
|
21
21
|
from devservices.utils.services import find_matching_service
|
|
22
|
+
from devservices.utils.services import get_active_service_names
|
|
22
23
|
from devservices.utils.state import State
|
|
23
24
|
from devservices.utils.state import StateTables
|
|
24
25
|
from devservices.utils.supervisor import SupervisorManager
|
|
@@ -39,7 +40,7 @@ def foreground(args: Namespace) -> None:
|
|
|
39
40
|
console = Console()
|
|
40
41
|
program_name = args.program_name
|
|
41
42
|
try:
|
|
42
|
-
service = find_matching_service()
|
|
43
|
+
service = find_matching_service(config_path=getattr(args, "config", None))
|
|
43
44
|
except ConfigNotFoundError as e:
|
|
44
45
|
capture_exception(e, level="info")
|
|
45
46
|
console.failure(
|
|
@@ -60,9 +61,7 @@ def foreground(args: Namespace) -> None:
|
|
|
60
61
|
)
|
|
61
62
|
return
|
|
62
63
|
state = State()
|
|
63
|
-
|
|
64
|
-
started_services = set(state.get_service_entries(StateTables.STARTED_SERVICES))
|
|
65
|
-
active_services = starting_services.union(started_services)
|
|
64
|
+
active_services = get_active_service_names()
|
|
66
65
|
if service.name not in active_services:
|
|
67
66
|
console.warning(f"{service.name} is not running")
|
|
68
67
|
return
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from argparse import _SubParsersAction
|
|
4
3
|
from argparse import ArgumentParser
|
|
5
4
|
from argparse import Namespace
|
|
5
|
+
from argparse import _SubParsersAction
|
|
6
6
|
|
|
7
7
|
from sentry_sdk import capture_exception
|
|
8
8
|
|
|
@@ -32,7 +32,9 @@ def list_dependencies(args: Namespace) -> None:
|
|
|
32
32
|
service_name = args.service_name
|
|
33
33
|
|
|
34
34
|
try:
|
|
35
|
-
service = find_matching_service(
|
|
35
|
+
service = find_matching_service(
|
|
36
|
+
service_name, config_path=getattr(args, "config", None)
|
|
37
|
+
)
|
|
36
38
|
except ConfigNotFoundError as e:
|
|
37
39
|
capture_exception(e, level="info")
|
|
38
40
|
console.failure(
|
|
@@ -3,19 +3,19 @@ from __future__ import annotations
|
|
|
3
3
|
import concurrent.futures
|
|
4
4
|
import os
|
|
5
5
|
import subprocess
|
|
6
|
-
from argparse import _SubParsersAction
|
|
7
6
|
from argparse import ArgumentParser
|
|
8
7
|
from argparse import Namespace
|
|
8
|
+
from argparse import _SubParsersAction
|
|
9
9
|
|
|
10
10
|
from sentry_sdk import capture_exception
|
|
11
11
|
|
|
12
12
|
from devservices.constants import CONFIG_FILE_NAME
|
|
13
13
|
from devservices.constants import DEPENDENCY_CONFIG_VERSION
|
|
14
|
-
from devservices.constants import DependencyType
|
|
15
14
|
from devservices.constants import DEVSERVICES_DEPENDENCIES_CACHE_DIR
|
|
16
15
|
from devservices.constants import DEVSERVICES_DEPENDENCIES_CACHE_DIR_KEY
|
|
17
16
|
from devservices.constants import DEVSERVICES_DIR_NAME
|
|
18
17
|
from devservices.constants import MAX_LOG_LINES
|
|
18
|
+
from devservices.constants import DependencyType
|
|
19
19
|
from devservices.exceptions import ConfigError
|
|
20
20
|
from devservices.exceptions import ConfigNotFoundError
|
|
21
21
|
from devservices.exceptions import DependencyError
|
|
@@ -24,12 +24,16 @@ from devservices.exceptions import ServiceNotFoundError
|
|
|
24
24
|
from devservices.exceptions import SupervisorConfigError
|
|
25
25
|
from devservices.exceptions import SupervisorError
|
|
26
26
|
from devservices.utils.console import Console
|
|
27
|
-
from devservices.utils.dependencies import install_and_verify_dependencies
|
|
28
27
|
from devservices.utils.dependencies import InstalledRemoteDependency
|
|
28
|
+
from devservices.utils.dependencies import install_and_verify_dependencies
|
|
29
|
+
from devservices.utils.docker import get_container_health
|
|
30
|
+
from devservices.utils.docker_compose import DockerComposeCommand
|
|
31
|
+
from devservices.utils.docker_compose import get_container_names_for_project
|
|
29
32
|
from devservices.utils.docker_compose import get_docker_compose_commands_to_run
|
|
30
33
|
from devservices.utils.docker_compose import run_cmd
|
|
31
|
-
from devservices.utils.services import find_matching_service
|
|
32
34
|
from devservices.utils.services import Service
|
|
35
|
+
from devservices.utils.services import find_matching_service
|
|
36
|
+
from devservices.utils.services import get_active_service_names
|
|
33
37
|
from devservices.utils.state import State
|
|
34
38
|
from devservices.utils.state import StateTables
|
|
35
39
|
from devservices.utils.supervisor import SupervisorManager
|
|
@@ -51,7 +55,9 @@ def logs(args: Namespace) -> None:
|
|
|
51
55
|
console = Console()
|
|
52
56
|
service_name = args.service_name
|
|
53
57
|
try:
|
|
54
|
-
service = find_matching_service(
|
|
58
|
+
service = find_matching_service(
|
|
59
|
+
service_name, config_path=getattr(args, "config", None)
|
|
60
|
+
)
|
|
55
61
|
except ConfigNotFoundError as e:
|
|
56
62
|
capture_exception(e, level="info")
|
|
57
63
|
console.failure(
|
|
@@ -84,9 +90,7 @@ def logs(args: Namespace) -> None:
|
|
|
84
90
|
if not mode_dependencies and "default" in modes:
|
|
85
91
|
mode_dependencies.update(modes["default"])
|
|
86
92
|
|
|
87
|
-
|
|
88
|
-
started_services = set(state.get_service_entries(StateTables.STARTED_SERVICES))
|
|
89
|
-
running_services = starting_services.union(started_services)
|
|
93
|
+
running_services = get_active_service_names()
|
|
90
94
|
if service.name not in running_services:
|
|
91
95
|
console.warning(f"Service {service.name} is not running")
|
|
92
96
|
return
|
|
@@ -102,7 +106,9 @@ def logs(args: Namespace) -> None:
|
|
|
102
106
|
)
|
|
103
107
|
exit(1)
|
|
104
108
|
try:
|
|
105
|
-
logs_output = _logs(
|
|
109
|
+
logs_output, docker_compose_commands = _logs(
|
|
110
|
+
service, list(remote_dependencies), list(mode_dependencies)
|
|
111
|
+
)
|
|
106
112
|
except DockerComposeError as dce:
|
|
107
113
|
capture_exception(dce, level="info")
|
|
108
114
|
console.failure(f"Failed to get logs for {service.name}: {dce.stderr}")
|
|
@@ -128,12 +134,14 @@ def logs(args: Namespace) -> None:
|
|
|
128
134
|
console.info(f"=== Logs for supervisor program: {program_name} ===")
|
|
129
135
|
console.info(log_content)
|
|
130
136
|
|
|
137
|
+
_print_health_logs(console, docker_compose_commands)
|
|
138
|
+
|
|
131
139
|
|
|
132
140
|
def _logs(
|
|
133
141
|
service: Service,
|
|
134
|
-
remote_dependencies:
|
|
142
|
+
remote_dependencies: list[InstalledRemoteDependency],
|
|
135
143
|
mode_dependencies: list[str],
|
|
136
|
-
) -> list[subprocess.CompletedProcess[str]]:
|
|
144
|
+
) -> tuple[list[subprocess.CompletedProcess[str]], list[DockerComposeCommand]]:
|
|
137
145
|
relative_local_dependency_directory = os.path.relpath(
|
|
138
146
|
os.path.join(DEVSERVICES_DEPENDENCIES_CACHE_DIR, DEPENDENCY_CONFIG_VERSION),
|
|
139
147
|
service.repo_path,
|
|
@@ -141,23 +149,20 @@ def _logs(
|
|
|
141
149
|
service_config_file_path = os.path.join(
|
|
142
150
|
service.repo_path, DEVSERVICES_DIR_NAME, CONFIG_FILE_NAME
|
|
143
151
|
)
|
|
144
|
-
# Set the environment variable for the local dependencies directory to be used by docker compose
|
|
145
152
|
current_env = os.environ.copy()
|
|
146
|
-
current_env[
|
|
147
|
-
|
|
148
|
-
|
|
153
|
+
current_env[DEVSERVICES_DEPENDENCIES_CACHE_DIR_KEY] = (
|
|
154
|
+
relative_local_dependency_directory
|
|
155
|
+
)
|
|
149
156
|
docker_compose_commands = get_docker_compose_commands_to_run(
|
|
150
157
|
service=service,
|
|
151
|
-
remote_dependencies=
|
|
158
|
+
remote_dependencies=remote_dependencies,
|
|
152
159
|
current_env=current_env,
|
|
153
160
|
command="logs",
|
|
154
|
-
options=["-n", MAX_LOG_LINES],
|
|
161
|
+
options=["--timestamps", "-n", MAX_LOG_LINES],
|
|
155
162
|
service_config_file_path=service_config_file_path,
|
|
156
163
|
mode_dependencies=mode_dependencies,
|
|
157
164
|
)
|
|
158
|
-
|
|
159
165
|
cmd_outputs = []
|
|
160
|
-
|
|
161
166
|
with concurrent.futures.ThreadPoolExecutor() as executor:
|
|
162
167
|
futures = [
|
|
163
168
|
executor.submit(run_cmd, cmd.full_command, current_env)
|
|
@@ -165,8 +170,46 @@ def _logs(
|
|
|
165
170
|
]
|
|
166
171
|
for future in concurrent.futures.as_completed(futures):
|
|
167
172
|
cmd_outputs.append(future.result())
|
|
173
|
+
return cmd_outputs, docker_compose_commands
|
|
174
|
+
|
|
175
|
+
|
|
176
|
+
def _print_health_logs(
|
|
177
|
+
console: Console, docker_compose_commands: list[DockerComposeCommand]
|
|
178
|
+
) -> None:
|
|
179
|
+
container_names: list[str] = []
|
|
180
|
+
for cmd in docker_compose_commands:
|
|
181
|
+
try:
|
|
182
|
+
container_names += [
|
|
183
|
+
c.name
|
|
184
|
+
for c in get_container_names_for_project(
|
|
185
|
+
cmd.project_name, cmd.config_path, cmd.services
|
|
186
|
+
)
|
|
187
|
+
]
|
|
188
|
+
except Exception:
|
|
189
|
+
continue
|
|
190
|
+
|
|
191
|
+
if not container_names:
|
|
192
|
+
return
|
|
193
|
+
|
|
194
|
+
health_results = get_container_health(container_names)
|
|
195
|
+
if not health_results:
|
|
196
|
+
return
|
|
168
197
|
|
|
169
|
-
|
|
198
|
+
console.info("=== Container health ===")
|
|
199
|
+
for h in health_results:
|
|
200
|
+
if h.status == "healthy":
|
|
201
|
+
console.success(f" {h.name}: {h.status}")
|
|
202
|
+
elif h.status in ("unhealthy", "starting"):
|
|
203
|
+
console.warning(f" {h.name}: {h.status}")
|
|
204
|
+
else:
|
|
205
|
+
console.info(f" {h.name}: no healthcheck")
|
|
206
|
+
|
|
207
|
+
for h in health_results:
|
|
208
|
+
if not h.log or h.status == "healthy":
|
|
209
|
+
continue
|
|
210
|
+
console.info(f"\n--- {h.name} health check log ---")
|
|
211
|
+
for entry in h.log:
|
|
212
|
+
console.info(f" exit={entry.exit_code} {entry.output}")
|
|
170
213
|
|
|
171
214
|
|
|
172
215
|
def _supervisor_logs(
|
|
@@ -1,25 +1,26 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from argparse import _SubParsersAction
|
|
4
3
|
from argparse import ArgumentParser
|
|
5
4
|
from argparse import Namespace
|
|
5
|
+
from argparse import _SubParsersAction
|
|
6
6
|
|
|
7
7
|
from sentry_sdk import capture_exception
|
|
8
8
|
|
|
9
9
|
from devservices.commands.down import down
|
|
10
|
-
from devservices.constants import DependencyType
|
|
11
10
|
from devservices.constants import DEVSERVICES_ORCHESTRATOR_LABEL
|
|
11
|
+
from devservices.constants import DependencyType
|
|
12
12
|
from devservices.exceptions import DockerDaemonNotRunningError
|
|
13
13
|
from devservices.exceptions import DockerError
|
|
14
14
|
from devservices.utils.console import Console
|
|
15
15
|
from devservices.utils.console import Status
|
|
16
|
-
from devservices.utils.dependencies import construct_dependency_graph
|
|
17
16
|
from devservices.utils.dependencies import DependencyNode
|
|
17
|
+
from devservices.utils.dependencies import construct_dependency_graph
|
|
18
18
|
from devservices.utils.docker import get_matching_containers
|
|
19
19
|
from devservices.utils.docker import get_volumes_for_containers
|
|
20
20
|
from devservices.utils.docker import remove_docker_resources
|
|
21
21
|
from devservices.utils.docker import stop_containers
|
|
22
22
|
from devservices.utils.services import find_matching_service
|
|
23
|
+
from devservices.utils.services import get_active_service_names
|
|
23
24
|
from devservices.utils.state import State
|
|
24
25
|
from devservices.utils.state import StateTables
|
|
25
26
|
|
|
@@ -69,9 +70,7 @@ def reset(args: Namespace) -> None:
|
|
|
69
70
|
exit(1)
|
|
70
71
|
|
|
71
72
|
state = State()
|
|
72
|
-
|
|
73
|
-
starting_services = set(state.get_service_entries(StateTables.STARTING_SERVICES))
|
|
74
|
-
active_service_names = starting_services.union(started_services)
|
|
73
|
+
active_service_names = get_active_service_names(clean_stale_entries=True)
|
|
75
74
|
|
|
76
75
|
# TODO: We should add threading here to speed up the process
|
|
77
76
|
for active_service_name in active_service_names:
|
|
@@ -3,9 +3,9 @@ from __future__ import annotations
|
|
|
3
3
|
import os
|
|
4
4
|
import pty
|
|
5
5
|
import shlex
|
|
6
|
-
from argparse import _SubParsersAction
|
|
7
6
|
from argparse import ArgumentParser
|
|
8
7
|
from argparse import Namespace
|
|
8
|
+
from argparse import _SubParsersAction
|
|
9
9
|
|
|
10
10
|
from sentry_sdk import capture_exception
|
|
11
11
|
|
|
@@ -35,7 +35,7 @@ def serve(args: Namespace) -> None:
|
|
|
35
35
|
console = Console()
|
|
36
36
|
|
|
37
37
|
try:
|
|
38
|
-
service = find_matching_service()
|
|
38
|
+
service = find_matching_service(config_path=getattr(args, "config", None))
|
|
39
39
|
except ConfigNotFoundError as e:
|
|
40
40
|
console.failure(
|
|
41
41
|
f"{str(e)}. Please run the command from a directory with a valid devservices configuration."
|
|
@@ -4,22 +4,22 @@ import concurrent.futures
|
|
|
4
4
|
import json
|
|
5
5
|
import os
|
|
6
6
|
import subprocess
|
|
7
|
-
from argparse import _SubParsersAction
|
|
8
7
|
from argparse import ArgumentParser
|
|
9
8
|
from argparse import Namespace
|
|
9
|
+
from argparse import _SubParsersAction
|
|
10
10
|
from collections import namedtuple
|
|
11
11
|
from datetime import timedelta
|
|
12
12
|
from typing import TypedDict
|
|
13
13
|
|
|
14
14
|
from sentry_sdk import capture_exception
|
|
15
15
|
|
|
16
|
-
from devservices.constants import Color
|
|
17
16
|
from devservices.constants import CONFIG_FILE_NAME
|
|
18
17
|
from devservices.constants import DEPENDENCY_CONFIG_VERSION
|
|
19
|
-
from devservices.constants import DependencyType
|
|
20
18
|
from devservices.constants import DEVSERVICES_DEPENDENCIES_CACHE_DIR
|
|
21
19
|
from devservices.constants import DEVSERVICES_DEPENDENCIES_CACHE_DIR_KEY
|
|
22
20
|
from devservices.constants import DEVSERVICES_DIR_NAME
|
|
21
|
+
from devservices.constants import Color
|
|
22
|
+
from devservices.constants import DependencyType
|
|
23
23
|
from devservices.exceptions import ConfigError
|
|
24
24
|
from devservices.exceptions import ConfigNotFoundError
|
|
25
25
|
from devservices.exceptions import DependencyError
|
|
@@ -27,15 +27,16 @@ from devservices.exceptions import DockerComposeError
|
|
|
27
27
|
from devservices.exceptions import ServiceNotFoundError
|
|
28
28
|
from devservices.exceptions import SupervisorConfigError
|
|
29
29
|
from devservices.utils.console import Console
|
|
30
|
-
from devservices.utils.dependencies import construct_dependency_graph
|
|
31
30
|
from devservices.utils.dependencies import DependencyGraph
|
|
32
31
|
from devservices.utils.dependencies import DependencyNode
|
|
33
|
-
from devservices.utils.dependencies import install_and_verify_dependencies
|
|
34
32
|
from devservices.utils.dependencies import InstalledRemoteDependency
|
|
33
|
+
from devservices.utils.dependencies import construct_dependency_graph
|
|
34
|
+
from devservices.utils.dependencies import install_and_verify_dependencies
|
|
35
35
|
from devservices.utils.docker_compose import get_docker_compose_commands_to_run
|
|
36
36
|
from devservices.utils.docker_compose import run_cmd
|
|
37
|
-
from devservices.utils.services import find_matching_service
|
|
38
37
|
from devservices.utils.services import Service
|
|
38
|
+
from devservices.utils.services import find_matching_service
|
|
39
|
+
from devservices.utils.services import get_active_service_names
|
|
39
40
|
from devservices.utils.state import ServiceRuntime
|
|
40
41
|
from devservices.utils.state import State
|
|
41
42
|
from devservices.utils.state import StateTables
|
|
@@ -80,7 +81,9 @@ def status(args: Namespace) -> None:
|
|
|
80
81
|
console = Console()
|
|
81
82
|
service_name = args.service_name
|
|
82
83
|
try:
|
|
83
|
-
service = find_matching_service(
|
|
84
|
+
service = find_matching_service(
|
|
85
|
+
service_name, config_path=getattr(args, "config", None)
|
|
86
|
+
)
|
|
84
87
|
except ConfigNotFoundError as e:
|
|
85
88
|
capture_exception(e)
|
|
86
89
|
console.failure(
|
|
@@ -95,10 +98,7 @@ def status(args: Namespace) -> None:
|
|
|
95
98
|
console.failure(str(e))
|
|
96
99
|
exit(1)
|
|
97
100
|
|
|
98
|
-
|
|
99
|
-
starting_services = set(state.get_service_entries(StateTables.STARTING_SERVICES))
|
|
100
|
-
started_services = set(state.get_service_entries(StateTables.STARTED_SERVICES))
|
|
101
|
-
active_services = starting_services.union(started_services)
|
|
101
|
+
active_services = get_active_service_names()
|
|
102
102
|
if service.name not in active_services:
|
|
103
103
|
console.warning(f"Status unavailable. {service.name} is not running standalone")
|
|
104
104
|
return # Since exit(0) is captured as an internal_error by sentry
|
|
@@ -182,9 +182,9 @@ def get_status_json_results(
|
|
|
182
182
|
)
|
|
183
183
|
# Set the environment variable for the local dependencies directory to be used by docker compose
|
|
184
184
|
current_env = os.environ.copy()
|
|
185
|
-
current_env[
|
|
186
|
-
|
|
187
|
-
|
|
185
|
+
current_env[DEVSERVICES_DEPENDENCIES_CACHE_DIR_KEY] = (
|
|
186
|
+
relative_local_dependency_directory
|
|
187
|
+
)
|
|
188
188
|
docker_compose_commands = get_docker_compose_commands_to_run(
|
|
189
189
|
service=service,
|
|
190
190
|
remote_dependencies=list(remote_dependencies),
|
|
@@ -318,9 +318,9 @@ def parse_docker_compose_status(
|
|
|
318
318
|
docker_compose_service_status
|
|
319
319
|
)
|
|
320
320
|
compose_service = docker_compose_service_status_json["Service"]
|
|
321
|
-
docker_compose_service_to_status[
|
|
322
|
-
|
|
323
|
-
|
|
321
|
+
docker_compose_service_to_status[compose_service] = (
|
|
322
|
+
docker_compose_service_status_json
|
|
323
|
+
)
|
|
324
324
|
|
|
325
325
|
return docker_compose_service_to_status
|
|
326
326
|
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
-
from argparse import _SubParsersAction
|
|
4
3
|
from argparse import ArgumentParser
|
|
5
4
|
from argparse import Namespace
|
|
5
|
+
from argparse import _SubParsersAction
|
|
6
6
|
|
|
7
7
|
from sentry_sdk import capture_exception
|
|
8
8
|
|
|
@@ -19,8 +19,9 @@ from devservices.utils.console import Status
|
|
|
19
19
|
from devservices.utils.dependencies import construct_dependency_graph
|
|
20
20
|
from devservices.utils.dependencies import get_non_shared_remote_dependencies
|
|
21
21
|
from devservices.utils.dependencies import install_and_verify_dependencies
|
|
22
|
-
from devservices.utils.services import find_matching_service
|
|
23
22
|
from devservices.utils.services import Service
|
|
23
|
+
from devservices.utils.services import find_matching_service
|
|
24
|
+
from devservices.utils.services import get_active_service_names
|
|
24
25
|
from devservices.utils.state import ServiceRuntime
|
|
25
26
|
from devservices.utils.state import State
|
|
26
27
|
from devservices.utils.state import StateTables
|
|
@@ -52,7 +53,9 @@ def toggle(args: Namespace) -> None:
|
|
|
52
53
|
console = Console()
|
|
53
54
|
service_name = args.service_name
|
|
54
55
|
try:
|
|
55
|
-
service = find_matching_service(
|
|
56
|
+
service = find_matching_service(
|
|
57
|
+
service_name, config_path=getattr(args, "config", None)
|
|
58
|
+
)
|
|
56
59
|
except ConfigNotFoundError as e:
|
|
57
60
|
capture_exception(e, level="info")
|
|
58
61
|
console.failure(
|
|
@@ -97,9 +100,7 @@ def handle_transition_to_local_runtime(service_to_transition: Service) -> None:
|
|
|
97
100
|
console = Console()
|
|
98
101
|
state = State()
|
|
99
102
|
|
|
100
|
-
|
|
101
|
-
started_services = set(state.get_service_entries(StateTables.STARTED_SERVICES))
|
|
102
|
-
active_services = starting_services.union(started_services)
|
|
103
|
+
active_services = get_active_service_names(clean_stale_entries=True)
|
|
103
104
|
|
|
104
105
|
# If the service is already running standalone, we can just update the runtime
|
|
105
106
|
if service_to_transition.name in active_services:
|
|
@@ -158,9 +159,7 @@ def handle_transition_to_containerized_runtime(service: Service) -> None:
|
|
|
158
159
|
"""Handle the transition to a containerized runtime for a service."""
|
|
159
160
|
console = Console()
|
|
160
161
|
state = State()
|
|
161
|
-
|
|
162
|
-
started_services = set(state.get_service_entries(StateTables.STARTED_SERVICES))
|
|
163
|
-
active_services = starting_services.union(started_services)
|
|
162
|
+
active_services = get_active_service_names(clean_stale_entries=True)
|
|
164
163
|
if service.name in active_services:
|
|
165
164
|
console.warning(f"{service.name} is running, please stop it first")
|
|
166
165
|
return
|
|
@@ -3,9 +3,9 @@ from __future__ import annotations
|
|
|
3
3
|
import concurrent.futures
|
|
4
4
|
import os
|
|
5
5
|
import subprocess
|
|
6
|
-
from argparse import _SubParsersAction
|
|
7
6
|
from argparse import ArgumentParser
|
|
8
7
|
from argparse import Namespace
|
|
8
|
+
from argparse import _SubParsersAction
|
|
9
9
|
|
|
10
10
|
from sentry_sdk import capture_exception
|
|
11
11
|
from sentry_sdk import logger as sentry_logger
|
|
@@ -14,10 +14,10 @@ from sentry_sdk import start_span
|
|
|
14
14
|
|
|
15
15
|
from devservices.constants import CONFIG_FILE_NAME
|
|
16
16
|
from devservices.constants import DEPENDENCY_CONFIG_VERSION
|
|
17
|
-
from devservices.constants import DependencyType
|
|
18
17
|
from devservices.constants import DEVSERVICES_DEPENDENCIES_CACHE_DIR
|
|
19
18
|
from devservices.constants import DEVSERVICES_DEPENDENCIES_CACHE_DIR_KEY
|
|
20
19
|
from devservices.constants import DEVSERVICES_DIR_NAME
|
|
20
|
+
from devservices.constants import DependencyType
|
|
21
21
|
from devservices.exceptions import ConfigError
|
|
22
22
|
from devservices.exceptions import ConfigNotFoundError
|
|
23
23
|
from devservices.exceptions import ContainerHealthcheckFailedError
|
|
@@ -29,17 +29,17 @@ from devservices.exceptions import SupervisorConfigError
|
|
|
29
29
|
from devservices.exceptions import SupervisorError
|
|
30
30
|
from devservices.utils.console import Console
|
|
31
31
|
from devservices.utils.console import Status
|
|
32
|
-
from devservices.utils.dependencies import construct_dependency_graph
|
|
33
32
|
from devservices.utils.dependencies import DependencyNode
|
|
34
|
-
from devservices.utils.dependencies import install_and_verify_dependencies
|
|
35
33
|
from devservices.utils.dependencies import InstalledRemoteDependency
|
|
34
|
+
from devservices.utils.dependencies import construct_dependency_graph
|
|
35
|
+
from devservices.utils.dependencies import install_and_verify_dependencies
|
|
36
36
|
from devservices.utils.docker import check_all_containers_healthy
|
|
37
37
|
from devservices.utils.docker_compose import DockerComposeCommand
|
|
38
38
|
from devservices.utils.docker_compose import get_container_names_for_project
|
|
39
39
|
from devservices.utils.docker_compose import get_docker_compose_commands_to_run
|
|
40
40
|
from devservices.utils.docker_compose import run_cmd
|
|
41
|
-
from devservices.utils.services import find_matching_service
|
|
42
41
|
from devservices.utils.services import Service
|
|
42
|
+
from devservices.utils.services import find_matching_service
|
|
43
43
|
from devservices.utils.state import ServiceRuntime
|
|
44
44
|
from devservices.utils.state import State
|
|
45
45
|
from devservices.utils.state import StateTables
|
|
@@ -76,7 +76,9 @@ def up(args: Namespace, existing_status: Status | None = None) -> None:
|
|
|
76
76
|
console = Console()
|
|
77
77
|
service_name = args.service_name
|
|
78
78
|
try:
|
|
79
|
-
service = find_matching_service(
|
|
79
|
+
service = find_matching_service(
|
|
80
|
+
service_name, config_path=getattr(args, "config", None)
|
|
81
|
+
)
|
|
80
82
|
except ConfigNotFoundError as e:
|
|
81
83
|
capture_exception(e, level="info")
|
|
82
84
|
console.failure(
|
|
@@ -318,9 +320,9 @@ def bring_up_docker_compose_services(
|
|
|
318
320
|
)
|
|
319
321
|
# Set the environment variable for the local dependencies directory to be used by docker compose
|
|
320
322
|
current_env = os.environ.copy()
|
|
321
|
-
current_env[
|
|
322
|
-
|
|
323
|
-
|
|
323
|
+
current_env[DEVSERVICES_DEPENDENCIES_CACHE_DIR_KEY] = (
|
|
324
|
+
relative_local_dependency_directory
|
|
325
|
+
)
|
|
324
326
|
dependency_graph = construct_dependency_graph(service, modes=modes)
|
|
325
327
|
starting_order = dependency_graph.get_starting_order()
|
|
326
328
|
sorted_remote_dependencies = sorted(
|
|
@@ -388,7 +390,9 @@ def bring_up_docker_compose_services(
|
|
|
388
390
|
)
|
|
389
391
|
exit(1)
|
|
390
392
|
try:
|
|
391
|
-
check_all_containers_healthy(
|
|
393
|
+
check_all_containers_healthy(
|
|
394
|
+
status, containers_to_check, timeout=service.config.healthcheck_timeout
|
|
395
|
+
)
|
|
392
396
|
except ContainerHealthcheckFailedError as e:
|
|
393
397
|
status.failure(str(e))
|
|
394
398
|
exit(1)
|