Skip to content

feat(slm): the frontend bundle keeps a rollback target that nothing can trigger — the flip is a documented shell command only #17270

Open

Description

@mrveiss

Problem

The SLM frontend publish layout maintains a rollback target that nothing in the product can trigger.

autobot-slm-backend/services/slm_frontend_build.py publishes dist-<build-id>/ per build, with current as the served symlink and previous as the rollback target. Retention is paid for deliberately: _RELEASE_KEEP (:86, default 3, clamped 1–50) keeps the newest bundles, and _prune_old_builds (:132-144) excludes the targets of current and previous by name, so the rollback target survives pruning regardless of its age.

The flip itself is documented as an operator shell command, in a comment in ansible/roles/_shared/tasks/build_publish_slm_frontend.yml:80-82:

Rollback, by hand, is the same single syscall:
cd <publish dir> && ln -s "$(readlink previous)" .current.next && mv -T .current.next current

No API route and no UI control performs it. nginx follows the symlink per request with no reload needed, so the operation is a single atomic syscall away from being a button — the capability is complete except for its trigger.

Scope — what this is not

Rollback is not generally missing. POST /api/blue-green/{id}/rollback (autobot-slm-backend/api/blue_green.py:174) and POST /api/deployments/{id}/rollback (autobot-slm-backend/api/deployments.py:297) both exist and both have frontend controls (autobot-slm-frontend/src/views/DeploymentsView.vue, FleetOverview.vue, composables/useAutobotApi.ts).

This issue is only about the frontend bundle symlink path, which has retention and a preserved target but no trigger.

Why it matters

A bad frontend bundle is exactly the failure that locks an operator out of the UI they would use to fix it. The recovery path currently requires shell access to the node — and the retention machinery that makes recovery possible is already running and already costing disk.

Acceptance criteria

  • An API route flips the served bundle to the retained previous target
  • The route refuses, with a named reason, when no valid previous target exists
  • A UI control exposes it alongside the existing deployment rollback controls
  • The flip remains atomic — no window where current resolves to nothing
  • A test covers flip, flip-back, and the no-previous case

Refs #10016, #15610, #16310.

Metadata

Metadata

Assignees

No one assigned

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions