mirror of
https://github.com/tiennm99/ccs.git
synced 2026-10-15 20:15:32 +00:00
fix(cliproxy): finish concurrent update recovery
This commit is contained in:
1 parent
192b27fbb2
commit
fc56ecaac4
14 files changed
+428
-87
No files matched your search
@@ -1,5 +1,8 @@
|
||||
import { readFileSync } from 'fs';
|
||||
import { mkdtempSync, readFileSync, rmSync } from 'fs';
|
||||
import { spawnSync } from 'child_process';
|
||||
import { describe, expect, it } from 'bun:test';
|
||||
import { tmpdir } from 'os';
|
||||
import { join } from 'path';
|
||||
|
||||
const updateScript = readFileSync('docker/host/ccs-cliproxy-safe-update.sh', 'utf8');
|
||||
const reconcileScript = readFileSync('docker/host/ccs-cliproxy-reconcile.sh', 'utf8');
|
||||
@@ -26,6 +29,54 @@ describe('CLIProxy Docker host continuity assets', () => {
|
||||
expect(reconcileScript).toContain('flock -n 9');
|
||||
});
|
||||
|
||||
it('shares the in-container install lifecycle lock with CLI and dashboard installs', () => {
|
||||
expect(updateScript).toContain(
|
||||
"install_lock_target='/root/.ccs/cliproxy/bin/.install-lifecycle-plus'"
|
||||
);
|
||||
expect(updateScript).toContain('install_lock_dir="$install_lock_target.lock"');
|
||||
expect(updateScript).toContain('while ! mkdir "$install_lock_dir"');
|
||||
expect(updateScript).toContain('touch "$install_lock_dir"');
|
||||
expect(updateScript).toContain('install_lock_stale_seconds=600');
|
||||
expect(updateScript).toContain('remove_stale_install_lock');
|
||||
expect(updateScript).toContain('rmdir "$install_lock_dir"');
|
||||
expect(updateScript.indexOf('acquire_install_lock')).toBeLessThan(
|
||||
updateScript.indexOf('supervisorctl_cmd stop cliproxy')
|
||||
);
|
||||
});
|
||||
|
||||
it('reclaims an orphaned stale lifecycle lock', () => {
|
||||
const testRoot = mkdtempSync(join(tmpdir(), 'ccs-cliproxy-host-lock-'));
|
||||
const helpersStart = updateScript.indexOf('remove_stale_install_lock() {');
|
||||
const helpersEnd = updateScript.indexOf('\nwait_for_proxy() {');
|
||||
const helpers = updateScript.slice(helpersStart, helpersEnd);
|
||||
try {
|
||||
const result = spawnSync(
|
||||
'bash',
|
||||
[
|
||||
'-c',
|
||||
`set -Eeuo pipefail
|
||||
install_lock_target="$1/target"
|
||||
install_lock_dir="$install_lock_target.lock"
|
||||
install_lock_stale_seconds=600
|
||||
install_lock_owned=0
|
||||
mkdir -p "$install_lock_target" "$install_lock_dir"
|
||||
touch -t 200001010000 "$install_lock_dir"
|
||||
${helpers}
|
||||
acquire_install_lock
|
||||
test "$install_lock_owned" -eq 1
|
||||
release_install_lock`,
|
||||
'test-shell',
|
||||
testRoot,
|
||||
],
|
||||
{ encoding: 'utf8' }
|
||||
);
|
||||
|
||||
expect(result.status).toBe(0);
|
||||
} finally {
|
||||
rmSync(testRoot, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it('rolls back failed swaps and health-checks both services', () => {
|
||||
expect(updateScript).toContain('rollback()');
|
||||
expect(updateScript).toContain('previous-binary');
|
||||
@@ -41,6 +92,7 @@ describe('CLIProxy Docker host continuity assets', () => {
|
||||
expect(reconcileScript).toContain('-f "$compose_file" up -d --no-build');
|
||||
expect(reconcileScript).toContain('restart ccs-dashboard cliproxy');
|
||||
expect(reconcileScript).toContain('docker restart "$container_name"');
|
||||
expect(reconcileScript).toContain('up -d --force-recreate --no-build');
|
||||
expect(reconcileScript.indexOf('docker restart "$container_name"')).toBeGreaterThan(
|
||||
reconcileScript.indexOf('restart ccs-dashboard cliproxy')
|
||||
);
|
||||
@@ -53,4 +105,36 @@ describe('CLIProxy Docker host continuity assets', () => {
|
||||
expect(reconcileService).toContain('ExecStart=/opt/cliproxy/ccs-cliproxy-reconcile.sh');
|
||||
expect(reconcileTimer).toContain('OnUnitActiveSec=30s');
|
||||
});
|
||||
|
||||
it('forces nonzero signal exits so the EXIT trap rolls back maintenance', () => {
|
||||
expect(updateScript).toContain("trap 'exit 130' INT");
|
||||
expect(updateScript).toContain("trap 'exit 143' TERM");
|
||||
expect(updateScript).toContain("trap 'exit 129' HUP");
|
||||
|
||||
const handlerStart = updateScript.indexOf('on_exit() {');
|
||||
const handlerEnd = updateScript.indexOf('\ntrap on_exit EXIT');
|
||||
const handlers = updateScript.slice(handlerStart, handlerEnd);
|
||||
const result = spawnSync(
|
||||
'bash',
|
||||
[
|
||||
'-c',
|
||||
`set -Eeuo pipefail
|
||||
maintenance_started=1
|
||||
stage_root=/tmp/unused
|
||||
rollback() { printf 'rollback\\n'; }
|
||||
cleanup() { printf 'cleanup\\n'; }
|
||||
release_install_lock() { :; }
|
||||
${handlers}
|
||||
trap on_exit EXIT
|
||||
trap 'exit 143' TERM
|
||||
true
|
||||
kill -TERM $$`,
|
||||
],
|
||||
{ encoding: 'utf8' }
|
||||
);
|
||||
|
||||
expect(result.status).toBe(143);
|
||||
expect(result.stdout).toContain('rollback');
|
||||
expect(result.stdout).toContain('cleanup');
|
||||
});
|
||||
});
|
||||
Reference in new issue
Block a user