mirror of
https://github.com/Studio-Saelix/sencho.git
synced 2026-08-31 04:38:11 +00:00
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.
This commit is contained in:
@@ -71,6 +71,12 @@ describe('scheduledActions registry', () => {
|
||||
expect(def?.id).toBe('update');
|
||||
});
|
||||
|
||||
it('maps container target types to container UI entries', () => {
|
||||
expect(resolveTaskAction({ action: 'restart', target_type: 'container' })?.id).toBe('container-restart');
|
||||
expect(resolveTaskAction({ action: 'auto_stop', target_type: 'container' })?.id).toBe('container-stop');
|
||||
expect(resolveTaskAction({ action: 'auto_start', target_type: 'container' })?.id).toBe('container-start');
|
||||
});
|
||||
|
||||
it('maps a non-aliased action to its direct entry', () => {
|
||||
expect(resolveTaskAction({ action: 'restart', target_type: 'stack' })?.id).toBe('restart');
|
||||
expect(resolveTaskAction({ action: 'snapshot', target_type: 'fleet' })?.id).toBe('snapshot');
|
||||
@@ -90,6 +96,7 @@ describe('scheduledActions registry', () => {
|
||||
// Verify the exact order: lifecycle first, then updates, security, maintenance, backups.
|
||||
expect(ids).toEqual([
|
||||
'auto_backup', 'auto_start', 'restart', 'auto_stop', 'auto_down',
|
||||
'container-restart', 'container-stop', 'container-start',
|
||||
'update', 'update-fleet',
|
||||
'scan',
|
||||
'prune',
|
||||
@@ -111,6 +118,9 @@ describe('scheduledActions registry', () => {
|
||||
'restart': 'Restarts containers in place. Running services are stopped and started again on the same configuration.',
|
||||
'auto_stop': 'Stops containers but keeps them in place for a faster start later.',
|
||||
'auto_down': 'Runs compose down. Containers are removed, but compose files remain on disk.',
|
||||
'container-restart': 'Restarts a single container by name on the selected node. Targets the container directly, not through compose.',
|
||||
'container-stop': 'Stops a single container by name. The container remains on disk for a faster start later.',
|
||||
'container-start': 'Starts a stopped container by name on the selected node.',
|
||||
'update': "Checks this stack's images and recreates the stack only when newer images are available.",
|
||||
'update-fleet': 'Checks every stack on the selected node and updates stacks with newer images.',
|
||||
'scan': 'Runs Trivy against images on the selected local node and records the findings.',
|
||||
@@ -132,6 +142,9 @@ describe('scheduledActions registry', () => {
|
||||
'restart': 'interruptive',
|
||||
'auto_stop': 'interruptive',
|
||||
'auto_down': 'removes-containers',
|
||||
'container-restart': 'interruptive',
|
||||
'container-stop': 'interruptive',
|
||||
'container-start': 'runtime-change',
|
||||
'update': 'runtime-change',
|
||||
'update-fleet': 'runtime-change',
|
||||
'scan': 'read-only',
|
||||
@@ -212,6 +225,16 @@ describe('scheduledActions registry', () => {
|
||||
expect(scheduleTargetDescriptor(task)).toBe('Entire fleet');
|
||||
});
|
||||
|
||||
it('shows the container name for container actions', () => {
|
||||
const task: TargetTask = {
|
||||
action: 'restart',
|
||||
target_type: 'container',
|
||||
target_id: 'watchtower',
|
||||
name: 'Daily watchtower restart',
|
||||
};
|
||||
expect(scheduleTargetDescriptor(task, 'hub')).toBe('watchtower');
|
||||
});
|
||||
|
||||
it('shows the node for system actions (prune / scan), with a fallback', () => {
|
||||
const scan: TargetTask = { action: 'scan', target_type: 'system', target_id: null, name: 'Vul Scan' };
|
||||
const prune: TargetTask = { action: 'prune', target_type: 'system', target_id: null, name: 'Nightly Prune' };
|
||||
|
||||
Reference in New Issue
Block a user