Skip to content

Commit 9874efc

Browse files
committed
fix: update Podman plugin to use requests instead of requests_unixsocket and improve socket handling
This removes the requirement to have the package 'requests_unixsocket' installed CMK-28873 Change-Id: I658186c14e7c0cb00e461118677fe27c00866388
1 parent a7eb76a commit 9874efc

File tree

3 files changed

+112
-41
lines changed

3 files changed

+112
-41
lines changed

.werks/18724.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,4 +43,3 @@ The monitoring supports:<br>
4343

4444
### Prerequisites
4545
- Python 3.9 or newer must be available on the monitored host
46-
- Python package *requests_unixsocket* must be installed (required for communicating with Podman’s UNIX socket)

agents/plugins/BUILD

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
load("@aspect_rules_py//py:defs.bzl", "py_binary")
22
load("@cmk_requirements//:requirements.bzl", "requirement")
33

4-
exports_files([
5-
"dev-requirements.in",
6-
])
4+
exports_files(
5+
[
6+
"dev-requirements.in",
7+
],
8+
)
79

810
filegroup(
911
name = "srcs",
@@ -80,6 +82,7 @@ py_library(
8082
deps = [
8183
requirement("docker"),
8284
requirement("pymongo"),
85+
requirement("requests"),
8386
requirement("urllib3"),
8487
],
8588
)

agents/plugins/mk_podman.py

Lines changed: 106 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,24 @@
55
import configparser
66
import json
77
import os
8+
import socket
89
import sys
9-
from collections.abc import Mapping, Sequence
10+
from collections.abc import Callable, Mapping, Sequence
1011
from dataclasses import dataclass
1112
from enum import Enum
1213
from pathlib import Path
1314
from shutil import which
14-
from typing import Literal, TypedDict, Union
15+
from typing import Literal, TypedDict, TypeVar, Union
16+
17+
# override decorator is only available in Python 3.12+
18+
try:
19+
from typing import override
20+
except ImportError:
21+
_F = TypeVar("_F", bound=Callable[..., object])
22+
23+
def override(func: _F, /) -> _F:
24+
return func
25+
1526

1627
__version__ = "2.6.0b1"
1728

@@ -24,6 +35,8 @@
2435

2536
DEFAULT_SOCKET_PATH = "/run/podman/podman.sock"
2637

38+
DEFAULT_SCHEME = "http+unix://"
39+
2740

2841
# The checks below result in agent sections being created. This
2942
# is a way to end the plugin in case it is being executed on a non-podman host
@@ -139,27 +152,88 @@ def write_serialized_section(name: str, json_content: str) -> None:
139152
sys.stdout.flush()
140153

141154

142-
try:
143-
import requests_unixsocket # type: ignore[import-not-found]
155+
def write_piggyback_section(target_host: str, section: Union[JSONSection, Error]) -> None:
156+
sys.stdout.write(f"<<<<{target_host}>>>>\n")
157+
write_section(section)
158+
sys.stdout.write("<<<<>>>>\n")
159+
sys.stdout.flush()
160+
144161

145-
write_serialized_section("errors", json.dumps({}))
162+
try:
163+
from requests import Session
164+
from requests.adapters import HTTPAdapter
165+
from urllib3.connection import HTTPConnection
166+
from urllib3.connectionpool import HTTPConnectionPool
146167
except ImportError:
147168
write_section(
148169
Error(
149-
label="Missing Python dependency: requests-unixsocket.",
150-
message="Import error: No module named 'requests_unixsocket'. "
151-
"Install the OS package (for example: python3-requests-unixsocket on RHEL/Rocky via EPEL) "
152-
"or the pip package 'requests-unixsocket'.",
170+
label="Missing Python dependency: requests.",
171+
message="Import error: No module named 'requests'. "
172+
"Install the OS package (for example: python3-requests on RHEL/Rocky via EPEL) "
173+
"or the pip package 'requests'. ",
153174
)
154175
)
155176
sys.exit(0)
156177

157178

158-
def write_piggyback_section(target_host: str, section: Union[JSONSection, Error]) -> None:
159-
sys.stdout.write(f"<<<<{target_host}>>>>\n")
160-
write_section(section)
161-
sys.stdout.write("<<<<>>>>\n")
162-
sys.stdout.flush()
179+
# This was taken from cmk.utils.unixsocket_http
180+
# But, since we don't have access to that module here, we reimplement it.
181+
def make_unixsocket_session(
182+
socket_path: Path,
183+
target_base_url: str,
184+
) -> Session:
185+
session = Session()
186+
session.trust_env = False
187+
session.mount(
188+
target_base_url,
189+
_LocalAdapter(socket_path),
190+
)
191+
return session
192+
193+
194+
class _LocalConnection(HTTPConnection):
195+
def __init__(self, socket_path: Path) -> None:
196+
super().__init__("localhost")
197+
self._socket_path = socket_path
198+
199+
@override
200+
def connect(self) -> None:
201+
self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
202+
self.sock.connect(str(self._socket_path))
203+
204+
205+
class _LocalConnectionPool(HTTPConnectionPool):
206+
def __init__(self, socket_path: Path) -> None:
207+
super().__init__("localhost")
208+
self._connection = _LocalConnection(socket_path)
209+
210+
# TODO: Why does `@override` not work here?
211+
def _new_conn(self) -> _LocalConnection:
212+
return self._connection
213+
214+
215+
class _LocalAdapter(HTTPAdapter):
216+
def __init__(self, socket_path: Path) -> None:
217+
super().__init__()
218+
self._connection_pool = _LocalConnectionPool(socket_path)
219+
220+
@override
221+
def get_connection(
222+
self,
223+
url: Union[str, bytes],
224+
proxies: object = None,
225+
) -> _LocalConnectionPool:
226+
return self._connection_pool
227+
228+
@override
229+
def get_connection_with_tls_context(
230+
self,
231+
request: object,
232+
verify: object,
233+
proxies: object = None,
234+
cert: object = None,
235+
) -> _LocalConnectionPool:
236+
return self._connection_pool
163237

164238

165239
def build_url_human_readable(socket_path: str, endpoint_uri: str) -> str:
@@ -170,9 +244,7 @@ def build_url_callable(socket_path: str, endpoint_uri: str) -> str:
170244
return f"http+unix://{socket_path.replace('/', '%2F')}{endpoint_uri}"
171245

172246

173-
def query_containers(
174-
session: requests_unixsocket.Session, socket_path: str
175-
) -> Union[JSONSection, Error]:
247+
def query_containers(session: Session, socket_path: str) -> Union[JSONSection, Error]:
176248
endpoint = "/v4.0.0/libpod/containers/json"
177249
try:
178250
response = session.get(build_url_callable(socket_path, endpoint), params={"all": "true"})
@@ -183,9 +255,7 @@ def query_containers(
183255
return JSONSection("containers", json.dumps(output))
184256

185257

186-
def query_disk_usage(
187-
session: requests_unixsocket.Session, socket_path: str
188-
) -> Union[JSONSection, Error]:
258+
def query_disk_usage(session: Session, socket_path: str) -> Union[JSONSection, Error]:
189259
endpoint = "/v4.0.0/libpod/system/df"
190260
try:
191261
response = session.get(build_url_callable(socket_path, endpoint))
@@ -195,9 +265,7 @@ def query_disk_usage(
195265
return JSONSection("disk_usage", json.dumps(response.json()))
196266

197267

198-
def query_engine(
199-
session: requests_unixsocket.Session, socket_path: str
200-
) -> Union[JSONSection, Error]:
268+
def query_engine(session: Session, socket_path: str) -> Union[JSONSection, Error]:
201269
endpoint = "/v4.0.0/libpod/info"
202270
try:
203271
response = session.get(build_url_callable(socket_path, endpoint))
@@ -207,7 +275,7 @@ def query_engine(
207275
return JSONSection("engine", json.dumps(response.json()))
208276

209277

210-
def query_pods(session: requests_unixsocket.Session, socket_path: str) -> Union[JSONSection, Error]:
278+
def query_pods(session: Session, socket_path: str) -> Union[JSONSection, Error]:
211279
endpoint = "/v4.0.0/libpod/pods/json"
212280
try:
213281
response = session.get(build_url_callable(socket_path, endpoint), params={"all": "true"})
@@ -218,7 +286,7 @@ def query_pods(session: requests_unixsocket.Session, socket_path: str) -> Union[
218286

219287

220288
def query_container_inspect(
221-
session: requests_unixsocket.Session,
289+
session: Session,
222290
socket_path: str,
223291
container_id: str,
224292
) -> Union[JSONSection, Error]:
@@ -234,9 +302,7 @@ def query_container_inspect(
234302
return section
235303

236304

237-
def query_raw_stats(
238-
session: requests_unixsocket.Session, socket_path: str
239-
) -> Union[Mapping[str, object], Error]:
305+
def query_raw_stats(session: Session, socket_path: str) -> Union[Mapping[str, object], Error]:
240306
endpoint = "/v4.0.0/libpod/containers/stats"
241307
try:
242308
response = session.get(
@@ -265,7 +331,7 @@ def handle_containers_stats(
265331
containers: Sequence[Mapping[str, object]],
266332
container_stats: Mapping[str, object],
267333
socket_path: str,
268-
session: requests_unixsocket.Session,
334+
session: Session,
269335
) -> None:
270336
for container in containers:
271337
if not (container_id := str(container.get("Id", ""))):
@@ -289,20 +355,23 @@ def handle_containers_stats(
289355
def main() -> None:
290356
socket_paths = get_socket_paths(load_cfg())
291357

292-
for socket_path in socket_paths:
293-
with requests_unixsocket.Session() as session:
294-
containers_section = query_containers(session, socket_path)
358+
for socket_path_str in socket_paths:
359+
with make_unixsocket_session(
360+
socket_path=Path(socket_path_str),
361+
target_base_url=DEFAULT_SCHEME,
362+
) as session:
363+
containers_section = query_containers(session, socket_path_str)
295364

296365
write_sections(
297366
[
298367
containers_section,
299-
query_disk_usage(session, socket_path),
300-
query_engine(session, socket_path),
301-
query_pods(session, socket_path),
368+
query_disk_usage(session, socket_path_str),
369+
query_engine(session, socket_path_str),
370+
query_pods(session, socket_path_str),
302371
]
303372
)
304373

305-
raw_container_stats = query_raw_stats(session, socket_path)
374+
raw_container_stats = query_raw_stats(session, socket_path_str)
306375
container_stats = (
307376
extract_container_stats(raw_container_stats)
308377
if not isinstance(raw_container_stats, Error)
@@ -313,7 +382,7 @@ def main() -> None:
313382
handle_containers_stats(
314383
containers=json.loads(containers_section.content),
315384
container_stats=container_stats,
316-
socket_path=socket_path,
385+
socket_path=socket_path_str,
317386
session=session,
318387
)
319388
else:

0 commit comments

Comments
 (0)