remote-server-mcp 0.2.0__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.
- remote_server_mcp-0.2.0/PKG-INFO +68 -0
- remote_server_mcp-0.2.0/README.md +40 -0
- remote_server_mcp-0.2.0/pyproject.toml +75 -0
- remote_server_mcp-0.2.0/setup.cfg +4 -0
- remote_server_mcp-0.2.0/src/remote_server_mcp/__init__.py +12 -0
- remote_server_mcp-0.2.0/src/remote_server_mcp/operations.py +241 -0
- remote_server_mcp-0.2.0/src/remote_server_mcp/py.typed +0 -0
- remote_server_mcp-0.2.0/src/remote_server_mcp/resolution.py +265 -0
- remote_server_mcp-0.2.0/src/remote_server_mcp/server.py +1519 -0
- remote_server_mcp-0.2.0/src/remote_server_mcp.egg-info/PKG-INFO +68 -0
- remote_server_mcp-0.2.0/src/remote_server_mcp.egg-info/SOURCES.txt +20 -0
- remote_server_mcp-0.2.0/src/remote_server_mcp.egg-info/dependency_links.txt +1 -0
- remote_server_mcp-0.2.0/src/remote_server_mcp.egg-info/entry_points.txt +2 -0
- remote_server_mcp-0.2.0/src/remote_server_mcp.egg-info/requires.txt +2 -0
- remote_server_mcp-0.2.0/src/remote_server_mcp.egg-info/top_level.txt +1 -0
- remote_server_mcp-0.2.0/tests/test_database_tools.py +718 -0
- remote_server_mcp-0.2.0/tests/test_modernization.py +426 -0
- remote_server_mcp-0.2.0/tests/test_resolution.py +252 -0
- remote_server_mcp-0.2.0/tests/test_security.py +559 -0
- remote_server_mcp-0.2.0/tests/test_security_bypass.py +521 -0
- remote_server_mcp-0.2.0/tests/test_ssh_connection.py +456 -0
- remote_server_mcp-0.2.0/tests/test_tool_commands.py +808 -0
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: remote-server-mcp
|
|
3
|
+
Version: 0.2.0
|
|
4
|
+
Summary: Security-first MCP server for managing Docker services on a remote server or on the local host.
|
|
5
|
+
Author-email: Alessio Elmi <alessio.elmi@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/elmuz/server-mcp
|
|
8
|
+
Project-URL: Repository, https://github.com/elmuz/server-mcp
|
|
9
|
+
Project-URL: Documentation, https://github.com/elmuz/server-mcp/tree/main/docs
|
|
10
|
+
Project-URL: Issues, https://github.com/elmuz/server-mcp/issues
|
|
11
|
+
Project-URL: Changelog, https://github.com/elmuz/server-mcp/blob/main/CHANGELOG.md
|
|
12
|
+
Keywords: mcp,model-context-protocol,linux,server-management,docker,homelab,self-hosting
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Environment :: Console
|
|
15
|
+
Classifier: Intended Audience :: System Administrators
|
|
16
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
|
+
Classifier: Topic :: System :: Systems Administration
|
|
23
|
+
Classifier: Typing :: Typed
|
|
24
|
+
Requires-Python: >=3.11
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
Requires-Dist: mcp<3,>=2.3
|
|
27
|
+
Requires-Dist: server-management-lib<0.3,>=0.2
|
|
28
|
+
|
|
29
|
+
# remote-server-mcp
|
|
30
|
+
|
|
31
|
+
Security-first MCP server for Docker service management over SSH or on the local host.
|
|
32
|
+
|
|
33
|
+
Part of the [Server MCP suite](../../README.md). It exposes a fixed catalog of 21 tools
|
|
34
|
+
(directory and container discovery, container and host-service logs, status and health,
|
|
35
|
+
start/stop/restart, scoped Compose operations, confined file reads, server health and
|
|
36
|
+
read-only InfluxDB/Prometheus queries). No generic command execution.
|
|
37
|
+
|
|
38
|
+
The container CLI is configurable (`docker`, `podman` or `nerdctl`). `restart_service`,
|
|
39
|
+
`stop_service`, `compose_pull`, `compose_up` and `compose_down` require `confirm=true` and
|
|
40
|
+
are bounded by a per-process rate limit. Streamable HTTP is available with
|
|
41
|
+
`--transport streamable-http`: it binds to loopback by default and requires a bearer token
|
|
42
|
+
for any other address.
|
|
43
|
+
|
|
44
|
+
Host infrastructure services such as `telegraf`, `smartd` and `docker` are systemd units,
|
|
45
|
+
not containers. `get_host_service_status` and `get_host_service_logs` read them with
|
|
46
|
+
`systemctl`/`journalctl` and only accept units listed in `security.host_units`.
|
|
47
|
+
|
|
48
|
+
Container logs always include timestamps. A log request can add a `since`/`until` window
|
|
49
|
+
and target one container in a compose project; search matches the exact literal pattern.
|
|
50
|
+
Every tool publishes a structured `ToolResponse` next to the readable text. Composite and
|
|
51
|
+
listing responses receive a final `output.max_bytes` cap. When the audit log
|
|
52
|
+
is enabled, InfluxDB and Prometheus queries are recorded by length/SHA-256 fingerprint
|
|
53
|
+
rather than by query text.
|
|
54
|
+
|
|
55
|
+
Prometheus is optional. Its metric endpoints can be reached through an HTTPS reverse proxy
|
|
56
|
+
or a loopback-only `ssh_tunnel` created by the MCP; see
|
|
57
|
+
[configuration](../../docs/configuration.md#database-endpoints).
|
|
58
|
+
|
|
59
|
+
- [Tools](../../docs/tools.md)
|
|
60
|
+
- [Configuration](../../docs/configuration.md)
|
|
61
|
+
- [Security model](../../docs/security-model.md)
|
|
62
|
+
- [Getting started](../../docs/getting-started.md)
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
uv run --directory apps/remote-server-mcp remote-server-mcp
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Requires a config file; see `config.example.yaml` in this directory.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# remote-server-mcp
|
|
2
|
+
|
|
3
|
+
Security-first MCP server for Docker service management over SSH or on the local host.
|
|
4
|
+
|
|
5
|
+
Part of the [Server MCP suite](../../README.md). It exposes a fixed catalog of 21 tools
|
|
6
|
+
(directory and container discovery, container and host-service logs, status and health,
|
|
7
|
+
start/stop/restart, scoped Compose operations, confined file reads, server health and
|
|
8
|
+
read-only InfluxDB/Prometheus queries). No generic command execution.
|
|
9
|
+
|
|
10
|
+
The container CLI is configurable (`docker`, `podman` or `nerdctl`). `restart_service`,
|
|
11
|
+
`stop_service`, `compose_pull`, `compose_up` and `compose_down` require `confirm=true` and
|
|
12
|
+
are bounded by a per-process rate limit. Streamable HTTP is available with
|
|
13
|
+
`--transport streamable-http`: it binds to loopback by default and requires a bearer token
|
|
14
|
+
for any other address.
|
|
15
|
+
|
|
16
|
+
Host infrastructure services such as `telegraf`, `smartd` and `docker` are systemd units,
|
|
17
|
+
not containers. `get_host_service_status` and `get_host_service_logs` read them with
|
|
18
|
+
`systemctl`/`journalctl` and only accept units listed in `security.host_units`.
|
|
19
|
+
|
|
20
|
+
Container logs always include timestamps. A log request can add a `since`/`until` window
|
|
21
|
+
and target one container in a compose project; search matches the exact literal pattern.
|
|
22
|
+
Every tool publishes a structured `ToolResponse` next to the readable text. Composite and
|
|
23
|
+
listing responses receive a final `output.max_bytes` cap. When the audit log
|
|
24
|
+
is enabled, InfluxDB and Prometheus queries are recorded by length/SHA-256 fingerprint
|
|
25
|
+
rather than by query text.
|
|
26
|
+
|
|
27
|
+
Prometheus is optional. Its metric endpoints can be reached through an HTTPS reverse proxy
|
|
28
|
+
or a loopback-only `ssh_tunnel` created by the MCP; see
|
|
29
|
+
[configuration](../../docs/configuration.md#database-endpoints).
|
|
30
|
+
|
|
31
|
+
- [Tools](../../docs/tools.md)
|
|
32
|
+
- [Configuration](../../docs/configuration.md)
|
|
33
|
+
- [Security model](../../docs/security-model.md)
|
|
34
|
+
- [Getting started](../../docs/getting-started.md)
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
uv run --directory apps/remote-server-mcp remote-server-mcp
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
Requires a config file; see `config.example.yaml` in this directory.
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "remote-server-mcp"
|
|
3
|
+
version = "0.2.0"
|
|
4
|
+
description = "Security-first MCP server for managing Docker services on a remote server or on the local host."
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = "MIT"
|
|
7
|
+
requires-python = ">=3.11"
|
|
8
|
+
dependencies = [
|
|
9
|
+
"mcp>=2.3,<3",
|
|
10
|
+
"server-management-lib>=0.2,<0.3",
|
|
11
|
+
]
|
|
12
|
+
|
|
13
|
+
authors = [
|
|
14
|
+
{ name = "Alessio Elmi", email = "alessio.elmi@gmail.com" },
|
|
15
|
+
]
|
|
16
|
+
keywords = [
|
|
17
|
+
"mcp",
|
|
18
|
+
"model-context-protocol",
|
|
19
|
+
"linux",
|
|
20
|
+
"server-management",
|
|
21
|
+
"docker",
|
|
22
|
+
"homelab",
|
|
23
|
+
"self-hosting",
|
|
24
|
+
]
|
|
25
|
+
classifiers = [
|
|
26
|
+
"Development Status :: 4 - Beta",
|
|
27
|
+
"Environment :: Console",
|
|
28
|
+
"Intended Audience :: System Administrators",
|
|
29
|
+
"Operating System :: POSIX :: Linux",
|
|
30
|
+
"Programming Language :: Python :: 3",
|
|
31
|
+
"Programming Language :: Python :: 3.11",
|
|
32
|
+
"Programming Language :: Python :: 3.12",
|
|
33
|
+
"Programming Language :: Python :: 3.13",
|
|
34
|
+
"Programming Language :: Python :: 3.14",
|
|
35
|
+
"Topic :: System :: Systems Administration",
|
|
36
|
+
"Typing :: Typed",
|
|
37
|
+
]
|
|
38
|
+
|
|
39
|
+
[project.urls]
|
|
40
|
+
Homepage = "https://github.com/elmuz/server-mcp"
|
|
41
|
+
Repository = "https://github.com/elmuz/server-mcp"
|
|
42
|
+
Documentation = "https://github.com/elmuz/server-mcp/tree/main/docs"
|
|
43
|
+
Issues = "https://github.com/elmuz/server-mcp/issues"
|
|
44
|
+
Changelog = "https://github.com/elmuz/server-mcp/blob/main/CHANGELOG.md"
|
|
45
|
+
|
|
46
|
+
[project.scripts]
|
|
47
|
+
remote-server-mcp = "remote_server_mcp:cli"
|
|
48
|
+
|
|
49
|
+
[build-system]
|
|
50
|
+
requires = ["setuptools>=61.0"]
|
|
51
|
+
build-backend = "setuptools.build_meta"
|
|
52
|
+
|
|
53
|
+
[tool.setuptools.package-data]
|
|
54
|
+
remote_server_mcp = ["py.typed"]
|
|
55
|
+
|
|
56
|
+
[tool.pytest.ini_options]
|
|
57
|
+
testpaths = ["tests"]
|
|
58
|
+
python_files = ["test_*.py"]
|
|
59
|
+
python_classes = ["Test*"]
|
|
60
|
+
python_functions = ["test_*"]
|
|
61
|
+
markers = [
|
|
62
|
+
"integration: marks integration tests that require real SSH/DB access",
|
|
63
|
+
]
|
|
64
|
+
# Keep the default suite deterministic. Run integration tests explicitly with:
|
|
65
|
+
# uv run pytest -m integration
|
|
66
|
+
addopts = "-m 'not integration' --cov=src/remote_server_mcp --cov-report=term-missing"
|
|
67
|
+
|
|
68
|
+
[tool.coverage.run]
|
|
69
|
+
source = ["remote_server_mcp"]
|
|
70
|
+
branch = true
|
|
71
|
+
|
|
72
|
+
[tool.coverage.report]
|
|
73
|
+
fail_under = 60
|
|
74
|
+
show_missing = true
|
|
75
|
+
skip_covered = true
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Operation catalog for the remote-server MCP.
|
|
3
|
+
|
|
4
|
+
Commands are built as argument vectors and rendered with :func:`shlex.join`, so
|
|
5
|
+
no user-controlled value is ever concatenated into a shell string. Operations
|
|
6
|
+
also declare their safety level, which is used for MCP tool annotations and
|
|
7
|
+
audit records.
|
|
8
|
+
|
|
9
|
+
The container CLI is configurable (``container_runtime``): Docker, Podman and
|
|
10
|
+
nerdctl share the same command surface for the operations below. The module
|
|
11
|
+
level constants remain the Docker catalog for backwards compatibility and for
|
|
12
|
+
the command-policy tests.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from dataclasses import dataclass
|
|
16
|
+
|
|
17
|
+
from server_management_lib import Operation
|
|
18
|
+
|
|
19
|
+
# ``docker ps``/``docker stats`` expand ``\\n`` and ``\\t`` inside their format
|
|
20
|
+
# strings, but ``docker inspect`` does not: it passes the text to the Go
|
|
21
|
+
# template verbatim, so a literal ``\\n`` ends up in the output and every
|
|
22
|
+
# field stays on one line. Emitting the newline from inside a template action
|
|
23
|
+
# (``{{"\\n"}}``) keeps the argv free of control characters (the command
|
|
24
|
+
# policy rejects those) and still produces real line breaks.
|
|
25
|
+
NEWLINE = '{{"\\n"}}'
|
|
26
|
+
|
|
27
|
+
INSPECT_FORMAT = (
|
|
28
|
+
f"Name: {{{{.Name}}}}{NEWLINE}"
|
|
29
|
+
f"State: {{{{.State.Status}}}}{NEWLINE}"
|
|
30
|
+
f"Running: {{{{.State.Running}}}}{NEWLINE}"
|
|
31
|
+
f"Health: {{{{if .State.Health}}}}{{{{.State.Health.Status}}}}{{{{else}}}}none"
|
|
32
|
+
f"{{{{end}}}}{NEWLINE}"
|
|
33
|
+
f"FailingStreak: {{{{if .State.Health}}}}{{{{.State.Health.FailingStreak}}}}"
|
|
34
|
+
f"{{{{else}}}}0{{{{end}}}}{NEWLINE}"
|
|
35
|
+
f"RestartCount: {{{{.RestartCount}}}}{NEWLINE}"
|
|
36
|
+
f"ExitCode: {{{{.State.ExitCode}}}}{NEWLINE}"
|
|
37
|
+
f"StartedAt: {{{{.State.StartedAt}}}}"
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
# ``docker stats`` *does* expand ``\\n`` before it parses the template, so it
|
|
41
|
+
# must keep the plain escape. Using the ``NEWLINE`` action here would turn the
|
|
42
|
+
# string literal into a real newline and fail with "unterminated quoted
|
|
43
|
+
# string".
|
|
44
|
+
STATS_FORMAT = "CPU: {{.CPUPerc}}\\nMemory: {{.MemUsage}}\\nNet I/O: {{.NetIO}}"
|
|
45
|
+
HEALTH_FORMAT = "{{json .State.Health}}"
|
|
46
|
+
|
|
47
|
+
# One line per container, including stopped and crashed ones. The compose
|
|
48
|
+
# project label is how a service directory resolves to its containers.
|
|
49
|
+
CONTAINER_LIST_FORMAT = (
|
|
50
|
+
'{{.Names}}\\t{{.Label "com.docker.compose.project"}}\\t{{.Status}}'
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def container_logs_operation(
|
|
55
|
+
*, runtime: str = "docker", since: str | None = None, until: str | None = None
|
|
56
|
+
) -> Operation:
|
|
57
|
+
"""Build a ``<runtime> logs`` operation with optional time filters.
|
|
58
|
+
|
|
59
|
+
``--timestamps`` is always enabled so log lines can be correlated with
|
|
60
|
+
host events. Time values are validated by the security layer before they
|
|
61
|
+
reach this function.
|
|
62
|
+
"""
|
|
63
|
+
argv = [runtime, "logs", "--tail", "$lines", "--timestamps"]
|
|
64
|
+
if since:
|
|
65
|
+
argv.extend(("--since", "$since"))
|
|
66
|
+
if until:
|
|
67
|
+
argv.extend(("--until", "$until"))
|
|
68
|
+
argv.extend(("--", "$service"))
|
|
69
|
+
return Operation(tuple(argv))
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
@dataclass(frozen=True)
|
|
73
|
+
class ContainerCatalog:
|
|
74
|
+
"""Every container operation for one runtime."""
|
|
75
|
+
|
|
76
|
+
runtime: str
|
|
77
|
+
ps: Operation
|
|
78
|
+
ps_all: Operation
|
|
79
|
+
ps_unhealthy: Operation
|
|
80
|
+
inspect: Operation
|
|
81
|
+
stats: Operation
|
|
82
|
+
ports: Operation
|
|
83
|
+
health: Operation
|
|
84
|
+
restart: Operation
|
|
85
|
+
start: Operation
|
|
86
|
+
stop: Operation
|
|
87
|
+
compose_ps: Operation
|
|
88
|
+
compose_pull: Operation
|
|
89
|
+
compose_up: Operation
|
|
90
|
+
compose_down: Operation
|
|
91
|
+
|
|
92
|
+
def logs(self, *, since: str | None = None, until: str | None = None) -> Operation:
|
|
93
|
+
return container_logs_operation(runtime=self.runtime, since=since, until=until)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def build_container_catalog(runtime: str = "docker") -> ContainerCatalog:
|
|
97
|
+
"""Build the container operation catalog for ``runtime``.
|
|
98
|
+
|
|
99
|
+
``runtime`` is validated by :class:`SecurityValidator` before it reaches
|
|
100
|
+
this function; the command policy also rejects unknown runtimes.
|
|
101
|
+
"""
|
|
102
|
+
return ContainerCatalog(
|
|
103
|
+
runtime=runtime,
|
|
104
|
+
ps=Operation((runtime, "ps", "--format", "{{.Names}}\\t{{.Status}}")),
|
|
105
|
+
ps_all=Operation((runtime, "ps", "-a", "--format", CONTAINER_LIST_FORMAT)),
|
|
106
|
+
ps_unhealthy=Operation(
|
|
107
|
+
(
|
|
108
|
+
runtime,
|
|
109
|
+
"ps",
|
|
110
|
+
"-a",
|
|
111
|
+
"--filter",
|
|
112
|
+
"health=unhealthy",
|
|
113
|
+
"--format",
|
|
114
|
+
"{{.Names}}\\t{{.Status}}",
|
|
115
|
+
)
|
|
116
|
+
),
|
|
117
|
+
inspect=Operation(
|
|
118
|
+
(runtime, "inspect", "--format", INSPECT_FORMAT, "--", "$service")
|
|
119
|
+
),
|
|
120
|
+
stats=Operation(
|
|
121
|
+
(
|
|
122
|
+
runtime,
|
|
123
|
+
"stats",
|
|
124
|
+
"--no-stream",
|
|
125
|
+
"--format",
|
|
126
|
+
STATS_FORMAT,
|
|
127
|
+
"--",
|
|
128
|
+
"$service",
|
|
129
|
+
)
|
|
130
|
+
),
|
|
131
|
+
ports=Operation((runtime, "port", "--", "$service")),
|
|
132
|
+
health=Operation(
|
|
133
|
+
(runtime, "inspect", "--format", HEALTH_FORMAT, "--", "$service")
|
|
134
|
+
),
|
|
135
|
+
restart=Operation(
|
|
136
|
+
(runtime, "restart", "--", "$service"),
|
|
137
|
+
read_only=False,
|
|
138
|
+
destructive=True,
|
|
139
|
+
),
|
|
140
|
+
start=Operation((runtime, "start", "--", "$service"), read_only=False),
|
|
141
|
+
stop=Operation(
|
|
142
|
+
(runtime, "stop", "--", "$service"),
|
|
143
|
+
read_only=False,
|
|
144
|
+
destructive=True,
|
|
145
|
+
),
|
|
146
|
+
# Compose commands are scoped to a service directory below
|
|
147
|
+
# services_path. The security layer validates the directory and the
|
|
148
|
+
# exact subcommand arguments.
|
|
149
|
+
compose_ps=Operation(
|
|
150
|
+
(
|
|
151
|
+
runtime,
|
|
152
|
+
"compose",
|
|
153
|
+
"--project-directory",
|
|
154
|
+
"$directory",
|
|
155
|
+
"ps",
|
|
156
|
+
"--format",
|
|
157
|
+
"{{.Name}} {{.Status}}",
|
|
158
|
+
)
|
|
159
|
+
),
|
|
160
|
+
compose_pull=Operation(
|
|
161
|
+
(runtime, "compose", "--project-directory", "$directory", "pull"),
|
|
162
|
+
read_only=False,
|
|
163
|
+
),
|
|
164
|
+
compose_up=Operation(
|
|
165
|
+
(
|
|
166
|
+
runtime,
|
|
167
|
+
"compose",
|
|
168
|
+
"--project-directory",
|
|
169
|
+
"$directory",
|
|
170
|
+
"up",
|
|
171
|
+
"-d",
|
|
172
|
+
"--remove-orphans",
|
|
173
|
+
),
|
|
174
|
+
read_only=False,
|
|
175
|
+
),
|
|
176
|
+
compose_down=Operation(
|
|
177
|
+
(
|
|
178
|
+
runtime,
|
|
179
|
+
"compose",
|
|
180
|
+
"--project-directory",
|
|
181
|
+
"$directory",
|
|
182
|
+
"down",
|
|
183
|
+
"--remove-orphans",
|
|
184
|
+
),
|
|
185
|
+
read_only=False,
|
|
186
|
+
destructive=True,
|
|
187
|
+
),
|
|
188
|
+
)
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
DEFAULT_CATALOG = build_container_catalog("docker")
|
|
192
|
+
|
|
193
|
+
ECHO_CONNECTION = Operation(("echo", "Connection successful"))
|
|
194
|
+
LS_SERVICES = Operation(("ls", "-1", "$path"))
|
|
195
|
+
# The trailing slash from -p distinguishes directories from plain files.
|
|
196
|
+
LS_SERVICE_DIRS = Operation(("ls", "-1", "-p", "$path"))
|
|
197
|
+
DOCKER_PS = DEFAULT_CATALOG.ps
|
|
198
|
+
DOCKER_PS_ALL = DEFAULT_CATALOG.ps_all
|
|
199
|
+
# Health status is only reported for containers with a healthcheck. Including
|
|
200
|
+
# stopped containers keeps a crashed or exited unhealthy service visible.
|
|
201
|
+
DOCKER_PS_UNHEALTHY = DEFAULT_CATALOG.ps_unhealthy
|
|
202
|
+
DOCKER_LOGS = DEFAULT_CATALOG.logs()
|
|
203
|
+
DOCKER_INSPECT = DEFAULT_CATALOG.inspect
|
|
204
|
+
DOCKER_STATS = DEFAULT_CATALOG.stats
|
|
205
|
+
DOCKER_PORTS = DEFAULT_CATALOG.ports
|
|
206
|
+
DOCKER_HEALTH = DEFAULT_CATALOG.health
|
|
207
|
+
DOCKER_RESTART = DEFAULT_CATALOG.restart
|
|
208
|
+
DOCKER_START = DEFAULT_CATALOG.start
|
|
209
|
+
DOCKER_STOP = DEFAULT_CATALOG.stop
|
|
210
|
+
COMPOSE_PS = DEFAULT_CATALOG.compose_ps
|
|
211
|
+
COMPOSE_PULL = DEFAULT_CATALOG.compose_pull
|
|
212
|
+
COMPOSE_UP = DEFAULT_CATALOG.compose_up
|
|
213
|
+
COMPOSE_DOWN = DEFAULT_CATALOG.compose_down
|
|
214
|
+
|
|
215
|
+
UPTIME = Operation(("uptime",))
|
|
216
|
+
FREE_MEMORY = Operation(("free", "-h"))
|
|
217
|
+
DISK_USAGE = Operation(("df", "-h", "$path"))
|
|
218
|
+
|
|
219
|
+
# Host infrastructure services are systemd units, not Docker containers. The
|
|
220
|
+
# unit name is validated against security.host_units before rendering.
|
|
221
|
+
SYSTEMCTL_STATUS = Operation(("systemctl", "status", "--no-pager", "--full", "$unit"))
|
|
222
|
+
JOURNALCTL_UNIT = Operation(
|
|
223
|
+
(
|
|
224
|
+
"journalctl",
|
|
225
|
+
"-u",
|
|
226
|
+
"$unit",
|
|
227
|
+
"-n",
|
|
228
|
+
"$lines",
|
|
229
|
+
"--no-pager",
|
|
230
|
+
"--output",
|
|
231
|
+
"short-iso",
|
|
232
|
+
)
|
|
233
|
+
)
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
# Backwards-compatible alias used by the server and its tests.
|
|
237
|
+
def docker_logs_operation(
|
|
238
|
+
*, since: str | None = None, until: str | None = None
|
|
239
|
+
) -> Operation:
|
|
240
|
+
"""Docker ``logs`` operation (kept for existing callers)."""
|
|
241
|
+
return DEFAULT_CATALOG.logs(since=since, until=until)
|
|
File without changes
|