Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -49,10 +49,28 @@ log "Checking Redis architecture compliance..."
if pgrep redis-server >/dev/null || docker ps | grep redis >/dev/null 2>&1; then
warning "Redis is running locally on main instance - this violates architecture!"
echo "Stopping local Redis instances..."
sudo systemctl stop redis-server 2>/dev/null || true
sudo systemctl stop redis-stack-server 2>/dev/null || true
# #16071 AC4: distinguish "unit not installed on this host" (nothing to do,
# e.g. redis-server on a node that only ever had redis-stack-server) from
# "unit exists but the stop failed" (a real problem) -- a blanket `|| true`
# discarded both alike. list-unit-files exits non-zero for an absent unit,
# the same existence check already used for autobot-redis/redis-stack-server
# elsewhere in this tree.
stop_failed=0
for unit in redis-server redis-stack-server; do
if ! systemctl list-unit-files "${unit}.service" &>/dev/null; then
continue
fi
if ! sudo systemctl stop "${unit}"; then
error "Failed to stop ${unit}"
stop_failed=1
fi
done
docker stop $(docker ps -q -f name=redis) 2>/dev/null || true
success "Local Redis instances stopped"
if [ "${stop_failed}" -eq 0 ]; then
success "Local Redis instances stopped"
else
error "One or more local Redis units failed to stop -- see above"
fi
else
success "✅ No local Redis instances found (correct)"
fi
Expand Down
4 changes: 4 additions & 0 deletions autobot-slm-backend/api/deployments.py
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,10 @@
"redis": {
"description": "Redis Stack server for data persistence",
"category": "data",
# Binary names, not systemd units (#16060/#16071): Redis Stack ships
# redis-server/redis-cli at /opt/redis-stack/bin/. The systemd unit it
# runs under is redis-stack-server, tracked separately by
# services/role_units.py.
"tools": ["redis-server", "redis-cli", "redis-sentinel"],
},
"ai-stack": {
Expand Down
4 changes: 4 additions & 0 deletions autobot-slm-frontend/src/constants/node-roles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,6 +92,10 @@ export const NODE_ROLE_METADATA: Record<NodeRole, RoleMetadata> = {
displayName: 'Redis',
description: 'Redis Stack server for data persistence',
category: 'data',
// Binary names, not systemd units (#16060/#16071): Redis Stack ships
// redis-server/redis-cli at /opt/redis-stack/bin/, same as api/deployments.py.
// The systemd unit it runs under is redis-stack-server, tracked separately
// by services/role_units.py.
tools: ['redis-server', 'redis-cli', 'redis-sentinel'],
},
'postgres': {
Expand Down
9 changes: 9 additions & 0 deletions changelog/unreleased/16060-16071-redis-naming-completion.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
type: fix
scope: infra
issue: 16060
pr: 0
---
Recorded why `node-roles.ts` and `api/deployments.py` list `redis-server`/`redis-cli` for the redis role — binary names Redis Stack genuinely ships, not the systemd unit (`redis-stack-server`) the fleet manages via `services/role_units.py` — completing #16060's remaining "correct or record as display-only" criterion.

`fix-architecture-issues.sh`'s Redis-stop step no longer discards its result with a blanket `|| true`: it now checks whether each unit (`redis-server`, `redis-stack-server`) is actually installed before attempting to stop it, and reports a real stop failure instead of swallowing it identically to an absent unit — completing #16071's AC4.
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
# Copyright 2025-2026 mrveiss
# SPDX-License-Identifier: Apache-2.0
# AutoBot - AI-Powered Automation Platform
# Author: mrveiss
"""fix-architecture-issues.sh reports a failed Redis stop, not just an absent
unit (#16071 AC4).

The script stops BOTH `redis-server` and `redis-stack-server` deliberately
(a node provisioned before #16060/#16071 may still carry the legacy apt
package) -- that dual-stop is a declared, tested exemption in
`redis_unit_is_the_stack_unit_16071_test.py::_LEGACY_CLEANUP`, not the
defect. The defect was `2>/dev/null || true` on each stop: it discarded
"unit not installed on this host" (expected, nothing to do) and "unit
exists but the stop failed" (a real problem) identically.

Not executed here -- a real `systemctl stop` needs a live systemd host,
and this repo never runs its own scripts locally (evidence comes from CI
or a sanctioned hook). These are static/structural checks on the script
text, the same evidence shape as the ansible YAML guards in this session.
"""

from __future__ import annotations

import re

from repo_tests._paths import repo_root

_SCRIPT = "autobot-infrastructure/shared/scripts/vm-management/fix-architecture-issues.sh"


def _text() -> str:
return (repo_root() / _SCRIPT).read_text(encoding="utf-8")


def test_script_exists():
assert (repo_root() / _SCRIPT).is_file(), f"{_SCRIPT} missing or moved"


def test_no_blanket_swallow_on_a_redis_unit_stop():
"""The exact defect: a `stop <unit> ... || true` line can't tell an
absent unit from a real failure. None should remain for either name."""
offenders = [
line.strip()
for line in _text().splitlines()
if re.search(r"systemctl\s+stop\s+redis(-stack)?-server", line) and "|| true" in line
]
assert not offenders, f"still discards the stop result: {offenders}"


def test_checks_unit_existence_before_stopping():
"""AC4's fix: skip a unit that was never installed, rather than
attempting (and then swallowing the failure of) stopping it. The script
loops a single variable-driven check over both unit names rather than
writing the check out twice, so this asserts the loop covers both names
AND that the check itself is present, rather than checking each name
against a check that is actually parameterised by shell variable."""
text = _text()
assert re.search(
r'list-unit-files\s+"\$\{unit\}\.service"', text
), "no existence check gates the per-unit stop loop"
assert re.search(
r"for\s+unit\s+in\s+redis-server\s+redis-stack-server\b", text
), "the existence-gated loop no longer covers both redis unit names"


def test_a_real_stop_failure_is_reported_not_swallowed():
"""The other half of AC4: once a unit is confirmed present, a failed
stop must surface (via error()), not disappear into `|| true`."""
text = _text()
assert re.search(
r"if\s+!\s+sudo\s+systemctl\s+stop\s+\"?\$\{?unit\}?\"?", text
), 'expected an `if ! sudo systemctl stop "$unit"` guard so a real failure is caught'
assert "Failed to stop" in text, "a failed stop is no longer reported"
Loading