发布切换容器前等待生产上游同步完成 #284
@ -65,6 +65,14 @@ if ! flock -n 9; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 容器替换会终止其中的同步进程;与生产刷新任务共享同一把锁。
|
||||
exec 8>/run/caiwu-upstream-sync.lock
|
||||
echo "Waiting for active upstream synchronization before replacing the application container"
|
||||
if ! flock -w 1800 8; then
|
||||
echo "Upstream synchronization is still running; application container was not replaced" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PREVIOUS_IMAGE="$(docker inspect caiwu_app --format '{{.Config.Image}}' 2>/dev/null || true)"
|
||||
PREVIOUS_SHA="$(cat "$REMOTE_DIR/.deployed-sha" 2>/dev/null || true)"
|
||||
|
||||
|
||||
@ -69,6 +69,14 @@ if ! flock -n 9; then
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# 容器替换会终止其中的同步进程;与生产刷新任务共享同一把锁。
|
||||
exec 8>/run/caiwu-upstream-sync.lock
|
||||
echo "Waiting for active upstream synchronization before replacing the application container"
|
||||
if ! flock -w 1800 8; then
|
||||
echo "Upstream synchronization is still running; application container was not replaced" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
PREVIOUS_IMAGE="$(docker inspect caiwu_app --format '{{.Config.Image}}' 2>/dev/null || true)"
|
||||
PREVIOUS_SHA="$(cat "$REMOTE_DIR/.deployed-sha" 2>/dev/null || true)"
|
||||
|
||||
|
||||
57
tests/Unit/Deployment/DeploymentSyncLockTest.php
Normal file
57
tests/Unit/Deployment/DeploymentSyncLockTest.php
Normal file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
namespace Tests\Unit\Deployment;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Process\Process;
|
||||
|
||||
class DeploymentSyncLockTest extends TestCase
|
||||
{
|
||||
public function test_container_replacement_waits_for_sync_and_fails_safely_on_lock_timeout(): void
|
||||
{
|
||||
foreach (['activate-release.sh', 'activate-incremental-release.sh'] as $script) {
|
||||
$contents = file_get_contents(dirname(__DIR__, 3) . '/deploy/' . $script);
|
||||
$start = strpos($contents, 'exec 8>/run/caiwu-upstream-sync.lock');
|
||||
$end = strpos($contents, 'PREVIOUS_IMAGE=', $start ?: 0);
|
||||
$this->assertNotFalse($start, $script);
|
||||
$this->assertNotFalse($end, $script);
|
||||
$this->assertLessThan(strpos($contents, 'docker compose -f compose.jiangling.yml up'), $end);
|
||||
$guard = substr($contents, $start, $end - $start);
|
||||
|
||||
$directory = sys_get_temp_dir() . '/caiwu-deploy-lock-' . bin2hex(random_bytes(8));
|
||||
mkdir($directory, 0700);
|
||||
$holder = new Process(['sh', '-c', 'exec 8>"$1"; flock 8; touch "$2"; sleep 2', 'holder', $directory . '/sync.lock', $directory . '/held']);
|
||||
try {
|
||||
$holder->start();
|
||||
$deadline = microtime(true) + 3;
|
||||
while (! file_exists($directory . '/held') && microtime(true) < $deadline) {
|
||||
usleep(10000);
|
||||
}
|
||||
$this->assertFileExists($directory . '/held');
|
||||
$guard = str_replace('/run/caiwu-upstream-sync.lock', $directory . '/sync.lock', $guard);
|
||||
$guard .= "touch '$directory/activated'\n";
|
||||
|
||||
$timeout = new Process(['sh', '-c', str_replace('-w 1800', '-w 0', $guard)]);
|
||||
$timeout->run();
|
||||
$this->assertSame(1, $timeout->getExitCode());
|
||||
$this->assertStringContainsString('application container was not replaced', $timeout->getErrorOutput());
|
||||
$this->assertFileDoesNotExist($directory . '/activated');
|
||||
|
||||
$release = new Process(['sh', '-c', $guard]);
|
||||
$release->start();
|
||||
usleep(100000);
|
||||
$this->assertTrue($release->isRunning());
|
||||
$this->assertFileDoesNotExist($directory . '/activated');
|
||||
$release->wait();
|
||||
$this->assertTrue($release->isSuccessful(), $release->getErrorOutput());
|
||||
$this->assertFileExists($directory . '/activated');
|
||||
} finally {
|
||||
$holder->stop();
|
||||
foreach (glob($directory . '/*') as $file) {
|
||||
unlink($file);
|
||||
}
|
||||
rmdir($directory);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user