Files
sencho/backend/src/helpers/excludeSelfContainers.ts
T
Anso 10fb93dcb1 feat(scheduler): schedule container restart, stop, and start (#1526)
* feat(scheduler): schedule container restart, stop, and start

Add container as a scheduled-task target type so operators can automate lifecycle actions against standalone containers by node and name, with matching UI pickers, validation, execution on local and remote nodes, and tests.

* fix(scheduler): stack service matching and container picker hygiene

Backfill Service on smartFallback containers so per-service stack restarts work when container_name is set. Match services by compose label and container name in stack routes and scheduled restarts. Exclude Sencho from GET /api/containers lists. Hide the Restart Stack service picker when a stack has only one service.

* test(scheduler): scope service checkbox assertion to Services block

The create dialog also has a Delete after run checkbox. Count checkboxes only inside the Services section so CI does not include unrelated form controls.

* fix(scheduler): narrow closest() result to HTMLElement in schedule test

The service-checkbox assertion passed an Element from closest() into
within(), which requires an HTMLElement, failing tsc -b in the frontend
build and Docker build stages. Use the closest<HTMLElement>() type
argument so the value type-checks without an unsafe cast.

* fix(scheduler): hide Sencho container on remote node picker lists

Remote container lists are proxied from peer Sencho instances, so id-only self filtering missed peers on older builds. Await SelfIdentity init, match ImageID, and drop official saelix/sencho images. Apply the same heuristic in the scheduled-operations UI and when the hub fetches remote containers for scheduled runs.

* test(monitor): add missing DatabaseService mocks for scan history cleanup

* test(scheduler): add missing markStaleScansAsFailed mock

SchedulerService.tick() calls db.markStaleScansAsFailed() to sweep stale
vulnerability scans. The scheduler-service test was missing this method in
its DatabaseService mock, causing TypeError failures during test initialization.

Added mockMarkStaleScansAsFailed to hoisted mocks and DatabaseService mock
object, returning safe default of 0 scans marked as failed.

* test(compose): add missing FileSystemService mocks for getStackContent/getEnvContent

* test(containers-route): mock SelfIdentityService to prevent initialize() crash

The excludeSelfContainers() helper calls SelfIdentityService.initialize(), which tries to access DockerController. Without a proper SelfIdentityService mock, the initialize() call fails silently, causing a 500 error on GET /api/containers.

Added SelfIdentityService mock with initialize(), isOwnContainer(), and isOwnImage() methods to prevent the crash.
2026-07-02 22:31:29 -04:00

39 lines
1.4 KiB
TypeScript

import SelfIdentityService from '../services/SelfIdentityService';
export interface DockerContainerListRow {
Id: string;
Names?: string[];
Image?: string;
ImageID?: string;
Labels?: Record<string, string>;
}
/** Official Sencho release images (Docker Hub + GHCR). Used when SelfIdentity is unavailable on a peer. */
export function isPublishedSenchoImage(image: string): boolean {
const lower = image.toLowerCase();
return /(?:^|\/)saelix\/sencho(?:-dev)?(?:[:@]|$)/.test(lower)
|| /studio-saelix\/sencho(?:-dev)?(?:[:@]|$)/.test(lower);
}
function isLikelySenchoManagementContainer(c: DockerContainerListRow): boolean {
const name = c.Names?.[0]?.replace(/^\//, '').toLowerCase() ?? '';
if (name === 'sencho' || name === 'sencho-agent') return true;
if (c.Image && isPublishedSenchoImage(c.Image)) return true;
return false;
}
/** Drop the running Sencho instance from container picker lists. */
export async function excludeSelfContainers<T extends DockerContainerListRow>(containers: T[]): Promise<T[]> {
const self = SelfIdentityService.getInstance();
await self.initialize();
return containers.filter(c => {
const name = c.Names?.[0]?.replace(/^\//, '') ?? '';
if (self.isOwnContainer(c.Id)) return false;
if (name && self.isOwnContainer(name)) return false;
if (c.ImageID && self.isOwnImage(c.ImageID)) return false;
if (isLikelySenchoManagementContainer(c)) return false;
return true;
});
}