Skip to content

Commit 96f6edc

Browse files
LarsMichelsenJenkins
authored andcommitted
Group agent download feature in cmk.gui.agent_download
Collect the Setup download pages, the REST API download endpoints and the packed agent path helpers, which were scattered across cmk/gui/utils, cmk/gui/wato/pages and two openapi endpoint packages, in a single cmk/gui/agent_download package with a central registration module. Also move raw_linux_agent_wget_commands to cmk.gui.agent_commands (its only consumers) and cmk/gui/utils/agent_registration.py to cmk/gui/watolib/tls_registration_help.py. CMK-34704 Change-Id: I82c02f57b7697b374cc67db8855b47c3392c3d8a
1 parent 9066b99 commit 96f6edc

30 files changed

Lines changed: 271 additions & 193 deletions

File tree

cmk/gui/BUILD

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,6 @@ py_library(
293293
":auth_core",
294294
":config",
295295
":core_toolkit",
296-
"//cmk/gui/utils:agent",
297296
"//cmk/gui/utils:urls",
298297
"//cmk/gui/watolib",
299298
"//packages/cmk-ccc:hostaddress",
@@ -792,6 +791,7 @@ py_library(
792791
":page_toolkit",
793792
":view_toolkit",
794793
":werks",
794+
"//cmk/gui/agent_download",
795795
"//cmk/gui/agent_registration",
796796
"//cmk/gui/availability",
797797
"//cmk/gui/background_job/job",

cmk/gui/agent_commands.py

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414
from cmk.gui.i18n import _
1515
from cmk.gui.logged_in import LoggedInUser, user
1616
from cmk.gui.permissions import PermissionRegistry
17-
from cmk.gui.utils.agent import raw_linux_agent_wget_commands
1817
from cmk.gui.utils.urls import doc_reference_url, DocReference, DocReferenceUtm
1918
from cmk.shared_typing.agent_slideout import (
2019
AgentInstallCmds,
@@ -162,6 +161,24 @@ def build_agent_status_cmds() -> AgentStatusCmds:
162161
)
163162

164163

164+
_LINUX_RPM_DOWNLOAD_URL = (
165+
"{{SERVER}}/{{SITE}}/check_mk/agents/check-mk-agent-{version}-1.noarch.rpm"
166+
)
167+
_LINUX_DEB_DOWNLOAD_URL = "{{SERVER}}/{{SITE}}/check_mk/agents/check-mk-agent_{version}-1_all.deb"
168+
169+
170+
def raw_linux_agent_wget_commands(version: str) -> list[str]:
171+
"""`wget` commands for the basic, unbaked Linux agent packages.
172+
173+
The asymmetric naming (RPM uses `-`, DEB uses `_`) matches the on-disk
174+
filenames produced by the build (see `Makefile`, `tests/system/multisite/utils.py`).
175+
"""
176+
return [
177+
f"wget {_LINUX_RPM_DOWNLOAD_URL.format(version=version)}",
178+
f"wget {_LINUX_DEB_DOWNLOAD_URL.format(version=version)}",
179+
]
180+
181+
165182
def baked_agents_available(
166183
logged_in_user: LoggedInUser,
167184
permission_registry: PermissionRegistry,

cmk/gui/agent_download/BUILD

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
load("@aspect_rules_py//py:defs.bzl", "py_library")
2+
3+
py_library(
4+
name = "agent_download",
5+
srcs = glob(include = ["**/*.py"]),
6+
imports = ["../../.."],
7+
visibility = [
8+
"//cmk:__subpackages__",
9+
"//tests:__subpackages__",
10+
],
11+
deps = [
12+
"//cmk/fields",
13+
"//cmk/gui:auth_core",
14+
"//cmk/gui:config",
15+
"//cmk/gui:core_toolkit",
16+
"//cmk/gui:html_toolkit",
17+
"//cmk/gui:page_toolkit",
18+
"//cmk/gui/openapi:utils",
19+
"//cmk/gui/openapi/framework",
20+
"//cmk/gui/openapi/restful_objects",
21+
"//cmk/gui/openapi/shared_endpoint_families",
22+
"//cmk/gui/token_auth",
23+
"//cmk/gui/utils:urls",
24+
"//cmk/gui/watolib",
25+
"//cmk/gui/watolib:registries",
26+
"//cmk/utils",
27+
"//cmk/utils:paths",
28+
"//packages/cmk-ccc:version",
29+
"//packages/cmk-plugin-apis:discover_plugins",
30+
],
31+
)

cmk/gui/agent_download/__init__.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#!/usr/bin/env python3
2+
# Copyright (C) 2025 Checkmk GmbH - License: GNU General Public License v2
3+
# This file is part of Checkmk (https://checkmk.com). It is subject to the terms and
4+
# conditions defined in the file COPYING, which is part of this source code package.
5+
"""Download of the agent packages shipped with Checkmk (Setup pages and REST API)."""
6+
7+
from ._pages import ABCModeDownloadAgents
8+
9+
__all__ = [
10+
"ABCModeDownloadAgents",
11+
]

cmk/gui/openapi/api_endpoints/agent_download/download_by_token.py renamed to cmk/gui/agent_download/_endpoints.py

Lines changed: 64 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
#!/usr/bin/env python3
2-
# Copyright (C) 2025 Checkmk GmbH - License: GNU General Public License v2
2+
# Copyright (C) 2021 Checkmk GmbH - License: GNU General Public License v2
33
# This file is part of Checkmk (https://checkmk.com). It is subject to the terms and
44
# conditions defined in the file COPYING, which is part of this source code package.
55

6+
from collections.abc import Mapping
67
from typing import Annotated, Literal
78

9+
from cmk import fields
810
from cmk.gui.http import ContentDispositionType, Response
911
from cmk.gui.openapi.framework import (
1012
ApiContext,
@@ -16,11 +18,66 @@
1618
QueryParam,
1719
VersionedEndpoint,
1820
)
19-
from cmk.gui.openapi.restful_objects.constructors import domain_type_action_href
21+
from cmk.gui.openapi.restful_objects import constructors, Endpoint
2022
from cmk.gui.openapi.shared_endpoint_families.agent import AGENTS_FAMILY
2123
from cmk.gui.openapi.utils import ProblemException
2224
from cmk.gui.token_auth import AgentDownloadToken, get_token_store
23-
from cmk.gui.utils import agent
25+
26+
from ._utils import (
27+
packed_agent_path_linux_deb,
28+
packed_agent_path_linux_rpm,
29+
packed_agent_path_windows_msi,
30+
)
31+
32+
OS_TYPES_AVAILABLE_IN_RAW = ["linux_rpm", "linux_deb", "windows_msi"]
33+
34+
OS_TYPE_RAW = {
35+
"os_type": fields.String(
36+
description=(
37+
"The type of the operating system. May be one of "
38+
+ ", ".join(OS_TYPES_AVAILABLE_IN_RAW)
39+
),
40+
enum=sorted(OS_TYPES_AVAILABLE_IN_RAW),
41+
example="linux_deb",
42+
required=True,
43+
),
44+
}
45+
46+
47+
@Endpoint(
48+
constructors.domain_type_action_href("agent", "download"),
49+
"cmk/download",
50+
method="get",
51+
content_type="application/octet-stream",
52+
query_params=[OS_TYPE_RAW],
53+
family_name=AGENTS_FAMILY.name,
54+
)
55+
def download_agent(params: Mapping[str, object]) -> Response:
56+
"""Download agents shipped with Checkmk"""
57+
os_type = params.get("os_type")
58+
response = Response()
59+
60+
if os_type == "windows_msi":
61+
agent_path = packed_agent_path_windows_msi()
62+
response.set_content_type("application/x-msi")
63+
elif os_type == "linux_rpm":
64+
agent_path = packed_agent_path_linux_rpm()
65+
response.set_content_type("application/x-rpm")
66+
elif os_type == "linux_deb":
67+
agent_path = packed_agent_path_linux_deb()
68+
response.set_content_type("application/x-deb")
69+
else:
70+
# This should never happen. Due to validation `os_type` can only be one
71+
# of the three elements above.
72+
raise AssertionError(f"Agent: os_type '{os_type}' not known in raw edition.")
73+
74+
response.set_content_disposition(ContentDispositionType.ATTACHMENT, agent_path.name)
75+
76+
with open(agent_path, mode="rb") as f:
77+
response.data = f.read()
78+
response.status_code = 200
79+
return response
80+
2481

2582
_OS_TYPES_AVAILABLE = ["linux_deb", "linux_rpm", "windows_msi"]
2683

@@ -55,13 +112,13 @@ def download_agent_by_token(
55112

56113
response = Response()
57114
if os_type == "windows_msi":
58-
agent_path = agent.packed_agent_path_windows_msi()
115+
agent_path = packed_agent_path_windows_msi()
59116
response.set_content_type("application/x-msi")
60117
elif os_type == "linux_rpm":
61-
agent_path = agent.packed_agent_path_linux_rpm()
118+
agent_path = packed_agent_path_linux_rpm()
62119
response.set_content_type("application/x-rpm")
63120
elif os_type == "linux_deb":
64-
agent_path = agent.packed_agent_path_linux_deb()
121+
agent_path = packed_agent_path_linux_deb()
65122
response.set_content_type("application/x-deb")
66123
else:
67124
raise AssertionError(f"Agent: os_type '{os_type}' not known in this edition.")
@@ -75,7 +132,7 @@ def download_agent_by_token(
75132

76133
ENDPOINT_DOWNLOAD_BY_TOKEN = VersionedEndpoint(
77134
metadata=EndpointMetadata(
78-
path=domain_type_action_href("agent", "download_by_token"),
135+
path=constructors.domain_type_action_href("agent", "download_by_token"),
79136
link_relation="cmk/download_by_token",
80137
method="get",
81138
content_type="application/octet-stream",
Lines changed: 10 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -34,12 +34,17 @@
3434
PageMenuEntry,
3535
PageMenuTopic,
3636
)
37-
from cmk.gui.pages import Page, PageContext, PageEndpoint, PageRegistry
37+
from cmk.gui.pages import Page, PageContext
3838
from cmk.gui.type_defs import IconNames, PermissionName, StaticIcon
39-
from cmk.gui.utils import agent
4039
from cmk.gui.utils.urls import makeuri_contextless
4140
from cmk.gui.watolib.hosts_and_folders import folder_preserving_link
42-
from cmk.gui.watolib.mode import ModeRegistry, WatoMode
41+
from cmk.gui.watolib.mode import WatoMode
42+
43+
from ._utils import (
44+
packed_agent_path_linux_deb,
45+
packed_agent_path_linux_rpm,
46+
packed_agent_path_windows_msi,
47+
)
4348

4449
# Page names of the GUI handlers that stream agent plugin files which live outside
4550
# the statically served share/check_mk/agents tree (e.g. cmk/plugins/<family>/agents/).
@@ -49,34 +54,6 @@
4954
DOWNLOAD_LOCAL_AGENT_PLUGIN_PAGE = "download_local_agent_plugin"
5055

5156

52-
def register(page_registry: PageRegistry, mode_registry: ModeRegistry) -> None:
53-
mode_registry.register(ModeDownloadAgentsOther)
54-
mode_registry.register(ModeDownloadAgentsWindows)
55-
mode_registry.register(ModeDownloadAgentsLinux)
56-
57-
# The endpoints handing out the files need to filter for allowed ones themselves!
58-
# Bonus: fills the cache of _plugin_family_agent_dirs at apache load.
59-
available_dirs = [d.path for d in _plugin_family_agent_dirs()]
60-
page_registry.register(
61-
PageEndpoint(
62-
f"noauth:{DOWNLOAD_AGENT_PLUGIN_PAGE}",
63-
PageDownloadAgentPlugin(
64-
[p for p in available_dirs if not p.is_relative_to(cmk.utils.paths.local_root)],
65-
require_permission=False,
66-
),
67-
)
68-
)
69-
page_registry.register(
70-
PageEndpoint(
71-
DOWNLOAD_LOCAL_AGENT_PLUGIN_PAGE,
72-
PageDownloadAgentPlugin(
73-
available_dirs,
74-
require_permission=True,
75-
),
76-
)
77-
)
78-
79-
8057
@dataclass(frozen=True)
8158
class PluginFamilyAgentDir:
8259
"""An agent plugin directory of a single plugin family (cmk.bakery.v2).
@@ -386,7 +363,7 @@ def title(self) -> str:
386363

387364
@override
388365
def _packed_agents(self) -> list[str]:
389-
return [str(agent.packed_agent_path_windows_msi())]
366+
return [str(packed_agent_path_windows_msi())]
390367

391368
@override
392369
def _walk_base_dirs(self) -> list[str]:
@@ -408,7 +385,7 @@ def title(self) -> str:
408385

409386
@override
410387
def _packed_agents(self) -> list[str]:
411-
return [str(agent.packed_agent_path_linux_deb()), str(agent.packed_agent_path_linux_rpm())]
388+
return [str(packed_agent_path_linux_deb()), str(packed_agent_path_linux_rpm())]
412389

413390
@override
414391
def _walk_base_dirs(self) -> list[str]:
Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -26,21 +26,3 @@ def packed_agent_path_linux_rpm() -> Path:
2626
cmk.utils.paths.agents_dir
2727
/ f"check-mk-agent-{Version.from_str(cmk_version).version_without_rc}-1.noarch.rpm"
2828
)
29-
30-
31-
_LINUX_RPM_DOWNLOAD_URL = (
32-
"{{SERVER}}/{{SITE}}/check_mk/agents/check-mk-agent-{version}-1.noarch.rpm"
33-
)
34-
_LINUX_DEB_DOWNLOAD_URL = "{{SERVER}}/{{SITE}}/check_mk/agents/check-mk-agent_{version}-1_all.deb"
35-
36-
37-
def raw_linux_agent_wget_commands(version: str) -> list[str]:
38-
"""`wget` commands for the basic, unbaked Linux agent packages.
39-
40-
The asymmetric naming (RPM uses `-`, DEB uses `_`) matches the on-disk
41-
filenames produced by the build (see `Makefile`, `tests/system/multisite/utils.py`).
42-
"""
43-
return [
44-
f"wget {_LINUX_RPM_DOWNLOAD_URL.format(version=version)}",
45-
f"wget {_LINUX_DEB_DOWNLOAD_URL.format(version=version)}",
46-
]
Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
#!/usr/bin/env python3
2+
# Copyright (C) 2025 Checkmk GmbH - License: GNU General Public License v2
3+
# This file is part of Checkmk (https://checkmk.com). It is subject to the terms and
4+
# conditions defined in the file COPYING, which is part of this source code package.
5+
6+
import cmk.utils.paths
7+
from cmk.gui.openapi.framework.registry import VersionedEndpointRegistry
8+
from cmk.gui.openapi.restful_objects.registry import EndpointRegistry
9+
from cmk.gui.pages import PageEndpoint, PageRegistry
10+
from cmk.gui.watolib.mode import ModeRegistry
11+
12+
from ._endpoints import download_agent, ENDPOINT_DOWNLOAD_BY_TOKEN
13+
from ._pages import (
14+
_plugin_family_agent_dirs,
15+
DOWNLOAD_AGENT_PLUGIN_PAGE,
16+
DOWNLOAD_LOCAL_AGENT_PLUGIN_PAGE,
17+
ModeDownloadAgentsLinux,
18+
ModeDownloadAgentsOther,
19+
ModeDownloadAgentsWindows,
20+
PageDownloadAgentPlugin,
21+
)
22+
23+
24+
def register_endpoints(
25+
endpoint_registry: EndpointRegistry,
26+
versioned_endpoint_registry: VersionedEndpointRegistry,
27+
) -> None:
28+
endpoint_registry.register(download_agent)
29+
versioned_endpoint_registry.register(ENDPOINT_DOWNLOAD_BY_TOKEN)
30+
31+
32+
def register(
33+
page_registry: PageRegistry,
34+
mode_registry: ModeRegistry,
35+
endpoint_registry: EndpointRegistry,
36+
versioned_endpoint_registry: VersionedEndpointRegistry,
37+
) -> None:
38+
mode_registry.register(ModeDownloadAgentsOther)
39+
mode_registry.register(ModeDownloadAgentsWindows)
40+
mode_registry.register(ModeDownloadAgentsLinux)
41+
42+
# The endpoints handing out the files need to filter for allowed ones themselves!
43+
# Bonus: fills the cache of _plugin_family_agent_dirs at apache load.
44+
available_dirs = [d.path for d in _plugin_family_agent_dirs()]
45+
page_registry.register(
46+
PageEndpoint(
47+
f"noauth:{DOWNLOAD_AGENT_PLUGIN_PAGE}",
48+
PageDownloadAgentPlugin(
49+
[p for p in available_dirs if not p.is_relative_to(cmk.utils.paths.local_root)],
50+
require_permission=False,
51+
),
52+
)
53+
)
54+
page_registry.register(
55+
PageEndpoint(
56+
DOWNLOAD_LOCAL_AGENT_PLUGIN_PAGE,
57+
PageDownloadAgentPlugin(
58+
available_dirs,
59+
require_permission=True,
60+
),
61+
)
62+
)
63+
register_endpoints(endpoint_registry, versioned_endpoint_registry)

cmk/gui/common_registration.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
welcome,
3737
werks,
3838
)
39+
from cmk.gui.agent_download import registration as agent_download_registration
3940
from cmk.gui.autocompleters import AutocompleterRegistry
4041
from cmk.gui.availability import registration as availability_registration
4142
from cmk.gui.background_job.job import BackgroundJobRegistry
@@ -366,6 +367,12 @@ def register( # noqa: PLR0917
366367
versioned_endpoint_registry,
367368
endpoint_family_registry,
368369
)
370+
agent_download_registration.register(
371+
page_registry,
372+
mode_registry,
373+
endpoint_registry,
374+
versioned_endpoint_registry,
375+
)
369376
availability_registration.register(
370377
versioned_endpoint_registry=versioned_endpoint_registry,
371378
endpoint_family_registry=endpoint_family_registry,

cmk/gui/openapi/api_endpoints/BUILD

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,6 @@ py_library(
3333
"//cmk/gui/rest_api_types:notifications",
3434
"//cmk/gui/token_auth",
3535
"//cmk/gui/userdb",
36-
"//cmk/gui/utils:agent",
37-
"//cmk/gui/utils:agent_registration",
3836
"//cmk/gui/utils:urls",
3937
"//cmk/gui/watolib",
4038
"//cmk/gui/watolib:config_infra",

0 commit comments

Comments
 (0)