aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDennis Fink2026-09-27 21:37:45 +0200
committerDennis Fink2026-09-27 21:37:45 +0200
commitc7403b5870aed4baaf5253d21761619d8607efb7 (patch)
tree98da4ca9ec935431c9ca533dc395cb1451a9d37a
parent731a25e096cf9af3c0c5b3303796df7b3e91df36 (diff)
downloadprometheus-pacman-exporter-c7403b5870aed4baaf5253d21761619d8607efb7.tar.gz
prometheus-pacman-exporter-c7403b5870aed4baaf5253d21761619d8607efb7.zip
fix(commands): distinguish empty query results
Track exit codes that indicate an empty package query separately from accepted success codes. Treat pacman exit status 1 as an empty result only when both stdout and stderr are empty, while preserving genuine command failures as errors. Apply the empty-result handling to pacman queries that may legitimately return no packages, including update checks.
Diffstat (limited to '')
-rw-r--r--prometheus_pacman_exporter/__init__.py45
1 files changed, 34 insertions, 11 deletions
diff --git a/prometheus_pacman_exporter/__init__.py b/prometheus_pacman_exporter/__init__.py
index 0125187..80198d7 100644
--- a/prometheus_pacman_exporter/__init__.py
+++ b/prometheus_pacman_exporter/__init__.py
@@ -30,38 +30,48 @@ class PackageQuery(NamedTuple):
description: str
metric: Gauge
accepted_exit_codes: tuple[int, ...] = (0,)
+ empty_exit_codes: tuple[int, ...] = ()
PACMAN_BASE_COMMAND = ("pacman", "--query")
+PACMAN_EMPTY_EXIT_CODES = (1,)
BASE_QUERIES: tuple[PackageQuery, ...] = (
PackageQuery(PACMAN_BASE_COMMAND, "installed packages", metrics.INSTALLED_PACKAGES),
PackageQuery(
(*PACMAN_BASE_COMMAND, "--explicit"),
"explicitly installed packages",
metrics.EXPLICIT_PACKAGES,
+ empty_exit_codes=PACMAN_EMPTY_EXIT_CODES,
),
PackageQuery(
(*PACMAN_BASE_COMMAND, "--deps"),
"packages installed as dependencies",
metrics.DEPENDS_PACKAGES,
+ empty_exit_codes=PACMAN_EMPTY_EXIT_CODES,
),
PackageQuery(
(*PACMAN_BASE_COMMAND, "--unrequired"),
"unrequired packages",
metrics.UNREQUIRED_PACKAGES,
+ empty_exit_codes=PACMAN_EMPTY_EXIT_CODES,
),
PackageQuery(
(*PACMAN_BASE_COMMAND, "--foreign"),
"foreign packages",
metrics.FOREIGN_PACKAGES,
+ empty_exit_codes=PACMAN_EMPTY_EXIT_CODES,
),
PackageQuery(
- (*PACMAN_BASE_COMMAND, "--native"), "native packages", metrics.NATIVE_PACKAGES
+ (*PACMAN_BASE_COMMAND, "--native"),
+ "native packages",
+ metrics.NATIVE_PACKAGES,
+ empty_exit_codes=PACMAN_EMPTY_EXIT_CODES,
),
PackageQuery(
(*PACMAN_BASE_COMMAND, "--deps", "--unrequired"),
"orphan packages",
metrics.ORPHAN_PACKAGES,
+ (0, 1),
),
)
@@ -124,6 +134,7 @@ def run_command(
ctx: click.Context,
command: tuple[str, ...],
accepted_exit_codes: tuple[int, ...] = (0,),
+ empty_exit_codes: tuple[int, ...] = (),
) -> subprocess.CompletedProcess[str]:
"""Run a command and return the result.
@@ -133,19 +144,29 @@ def run_command(
:param ctx: Click context injected by :func:`click.pass_context`.
:param command: Command and its arguments.
:param accepted_exit_codes: Exit codes considered successful.
+ :param empty_exit_codes: Exit codes considered an empty result when both
+ stdout and stderr are empty.
:return: Completed process with stdout and stderr captured.
"""
command_string = shlex.join(command)
- debug("Running command", command_string, err=True)
+ debug("Running command:", command_string, err=True)
result = subprocess.run(command, capture_output=True, encoding="utf-8", check=False)
- if result.returncode not in accepted_exit_codes:
- error(
- f"Command ({command_string}) failed with exit code:", str(result.returncode)
- )
- if result.stderr:
- error("stderr:", result.stderr.strip())
- ctx.exit(1)
+ if result.returncode in accepted_exit_codes:
+ return result
+
+ if (
+ result.returncode in empty_exit_codes
+ and not result.stdout.strip()
+ and not result.stderr.strip()
+ ):
+ debug("Command returned an empty result:", command_string, err=True)
+ return result
+
+ error(f"Command ({command_string}) failed with exit code:", str(result.returncode))
+ if result.stderr:
+ error("stderr:", result.stderr.strip())
+ ctx.exit(1)
return result
@@ -262,7 +283,7 @@ def prometheus_pacman_exporter(
(*PACMAN_BASE_COMMAND, "--upgrades"),
"updateable packages",
metrics.UPDATEABLE_PACKAGES,
- (0, 1),
+ empty_exit_codes=PACMAN_EMPTY_EXIT_CODES,
)
)
@@ -276,7 +297,9 @@ def prometheus_pacman_exporter(
)
for query in queries:
- output = run_command(query.command, query.accepted_exit_codes)
+ output = run_command(
+ query.command, query.accepted_exit_codes, query.empty_exit_codes
+ )
value = len(output.stdout.splitlines())
verbose(f"Setting {query.description}:", str(value), err=True)
query.metric.set(value)