test(deployments): cover Docker-created bind dirs and sudo-less imports

This commit is contained in:
Andras Bacsai
2026-10-27 18:56:46 +01:00
parent 56d0cf0e27
commit d59899469c
2 changed files with 21 additions and 6 deletions
@@ -379,14 +379,14 @@ test('a deployment to an additional server writes missing content files on that
->and(ContentFilesUpRecorder::ssh()[$write])->not->toContain("@'{$this->server->ip}'");
});
test('a Dockerfile deployment to an additional server writes missing content files before the container starts', function () {
test('a Dockerfile deployment to an additional server writes missing content files before the container starts', function (string $state) {
$this->application->update(['build_pack' => 'dockerfile', 'docker_compose_location' => null]);
$volume = contentFilesVolume($this->application, 'config/app.conf');
$otherServer = Server::factory()->create([
'team_id' => $this->team->id,
'private_key_id' => $this->server->private_key_id,
])->fresh();
fakeContentFilesServer([$volume->fs_path => 'missing']);
fakeContentFilesServer([$volume->fs_path => $state]);
$job = new ContentFilesUpDeploymentJob;
$queue = Mockery::mock(ApplicationDeploymentQueue::class)->makePartial();
@@ -421,11 +421,20 @@ test('a Dockerfile deployment to an additional server writes missing content fil
->and($write)->toBeLessThan($up)
->and(ContentFilesUpRecorder::ssh()[$write])->toContain("@'{$otherServer->ip}'")
->and($this->logEntries)->toContain(['Writing 1 missing configuration file.', 'stdout']);
if ($state === 'empty-directory') {
$writeCommand = ContentFilesUpRecorder::ssh()[$write];
$escapedPath = escapeshellarg($volume->fs_path);
expect(strpos($writeCommand, "rmdir {$escapedPath}"))->toBeLessThan(strpos($writeCommand, "tee {$escapedPath}"));
}
$connect = ContentFilesUpRecorder::indexOf('deployment', "docker network connect '{$this->destination->network}' coolify-proxy");
expect($connect)->not->toBeNull()
->and($connect)->toBeLessThan($up);
});
})->with([
'missing file' => ['missing'],
// Docker creates a missing bind source as an empty directory (#10398).
'empty directory left by Docker' => ['empty-directory'],
]);
test('the content file check and write are safe for non-root servers', function () {
$this->server->update(['user' => 'ubuntu']);
@@ -440,12 +440,15 @@ test('a restore that exits like a lost SSH connection or timeout reports a norma
expect($restoreLine)->toContain($scriptPath);
// Fake sudo and docker: `docker exec <container> ...` runs the restore script on this machine.
// Database images have no sudo (#9949), so sudo fails inside the fake container.
$dir = sys_get_temp_dir().'/coolify-restore-exit-'.Str::random(8);
mkdir($dir);
mkdir("{$dir}/container", recursive: true);
file_put_contents("{$dir}/sudo", "#!/bin/sh\nexec \"\$@\"\n");
file_put_contents("{$dir}/docker", "#!/bin/sh\n[ \"\$1\" = exec ] || exit 1\nshift 2\nexec \"\$@\"\n");
file_put_contents("{$dir}/container/sudo", "#!/bin/sh\necho 'sh: sudo: not found' >&2\nexit 127\n");
file_put_contents("{$dir}/docker", "#!/bin/sh\n[ \"\$1\" = exec ] || exit 1\nshift 2\nPATH=\"{$dir}/container:\$PATH\" exec \"\$@\"\n");
file_put_contents($scriptPath, "echo restoring\nexit {$restoreExitCode}\n");
chmod("{$dir}/sudo", 0755);
chmod("{$dir}/container/sudo", 0755);
chmod("{$dir}/docker", 0755);
chmod($scriptPath, 0755);
@@ -454,9 +457,12 @@ test('a restore that exits like a lost SSH connection or timeout reports a norma
$restore->run();
expect($restore->getExitCode())->toBe($expectedExitCode)
->and($restore->getOutput())->toContain('restoring');
->and($restore->getOutput())->toContain('restoring')
->and($restore->getErrorOutput())->not->toContain('sudo: not found');
} finally {
@unlink($scriptPath);
@unlink("{$dir}/container/sudo");
rmdir("{$dir}/container");
array_map('unlink', glob("{$dir}/*"));
rmdir($dir);
}