mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-06 13:10:37 -04:00
fix(server): explain why the Docker Engine version check failed
When "docker version --format json" did not return clean JSON (for example, text from a shell startup file), validation stored 0.0.0 and said that Docker was older than version 24. Validation now stores no version and tells the user to run the command on the server. When the version is too old, the message shows the detected version. Closes #11089 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
8429b6e94b
commit
2ea1afb811
@@ -114,7 +114,7 @@ class ValidateServer
|
||||
if ($this->docker_version) {
|
||||
return 'OK';
|
||||
} else {
|
||||
$this->error = 'Docker Engine is not installed. Please install Docker manually before continuing: <a target="_blank" class="text-black underline dark:text-white" href="https://docs.docker.com/engine/install/#server">documentation</a>.';
|
||||
$this->error = $server->dockerEngineVersionError();
|
||||
$server->update([
|
||||
'validation_logs' => $this->error,
|
||||
]);
|
||||
|
||||
@@ -160,14 +160,14 @@ class ValidateAndInstallServerJob implements ShouldBeEncrypted, ShouldQueue
|
||||
// Validate Docker version
|
||||
$dockerVersion = $this->server->validateDockerEngineVersion();
|
||||
if (! $dockerVersion) {
|
||||
$requiredDockerVersion = str(config('constants.docker.minimum_required_version'))->before('.');
|
||||
$errorMessage = 'Minimum Docker Engine version '.$requiredDockerVersion.' is not installed. Please install Docker manually before continuing: <a target="_blank" class="underline" href="https://docs.docker.com/engine/install/#server">documentation</a>.';
|
||||
$errorMessage = $this->server->dockerEngineVersionError();
|
||||
$this->server->update([
|
||||
'validation_logs' => $errorMessage,
|
||||
'is_validating' => false,
|
||||
]);
|
||||
Log::error('ValidateAndInstallServer: Docker version not sufficient', [
|
||||
'server_id' => $this->server->id,
|
||||
'docker_version' => $this->server->dockerVersion(),
|
||||
]);
|
||||
|
||||
return;
|
||||
|
||||
@@ -320,8 +320,7 @@ class ValidateAndInstall extends Component
|
||||
instant_remote_process(ensureProxyNetworksExist($this->server)->toArray(), $this->server, false);
|
||||
StartProxy::dispatch($this->server);
|
||||
} else {
|
||||
$requiredDockerVersion = str(config('constants.docker.minimum_required_version'))->before('.');
|
||||
$this->error = 'Minimum Docker Engine version '.$requiredDockerVersion.' is not installed. Please install Docker manually before continuing: <a target="_blank" class="underline" href="https://docs.docker.com/engine/install/#server">documentation</a>.';
|
||||
$this->error = $this->server->dockerEngineVersionError();
|
||||
$this->server->update([
|
||||
'validation_logs' => $this->error,
|
||||
]);
|
||||
|
||||
+22
-5
@@ -2567,13 +2567,11 @@ $siteAddress {
|
||||
public function validateDockerEngineVersion()
|
||||
{
|
||||
$dockerVersionRaw = instant_remote_process(['docker version --format json'], $this, false);
|
||||
$dockerVersionJson = json_decode($dockerVersionRaw, true);
|
||||
$dockerVersion = data_get($dockerVersionJson, 'Server.Version', '0.0.0');
|
||||
$this->rememberDockerVersion(is_string($dockerVersion) ? $dockerVersion : null);
|
||||
$dockerVersion = dockerEngineVersionFromJson($dockerVersionRaw);
|
||||
$this->rememberDockerVersion($dockerVersion);
|
||||
$composeVersionRaw = instant_remote_process(['docker compose version --short'], $this, false);
|
||||
$this->rememberComposeVersion(is_string($composeVersionRaw) ? $composeVersionRaw : null);
|
||||
$dockerVersion = checkMinimumDockerEngineVersion($dockerVersion);
|
||||
if (is_null($dockerVersion)) {
|
||||
if (is_null(checkMinimumDockerEngineVersion($dockerVersion))) {
|
||||
$this->settings->is_usable = false;
|
||||
$this->settings->save();
|
||||
|
||||
@@ -2587,6 +2585,25 @@ $siteAddress {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Explains why validateDockerEngineVersion() failed: the version could not be read, or it is too old.
|
||||
*/
|
||||
public function dockerEngineVersionError(): string
|
||||
{
|
||||
$requiredVersion = str(config('constants.docker.minimum_required_version'))->before('.');
|
||||
$documentation = '<a target="_blank" class="underline" href="https://docs.docker.com/engine/install/#server">documentation</a>';
|
||||
$dockerVersion = $this->dockerVersion();
|
||||
|
||||
if (is_null($dockerVersion)) {
|
||||
$command = $this->isNonRoot() ? 'sudo docker version --format json' : 'docker version --format json';
|
||||
$user = e($this->user);
|
||||
|
||||
return "Coolify could not read the Docker Engine version. Run \"{$command}\" on the server as the {$user} user. It must exit without an error and print only JSON. Text from shell startup files (for example ~/.bashrc) breaks the output.";
|
||||
}
|
||||
|
||||
return "Docker Engine {$dockerVersion} is installed, but Coolify needs version {$requiredVersion} or newer. Please upgrade Docker: {$documentation}.";
|
||||
}
|
||||
|
||||
public function validateCoolifyNetwork($isSwarm = false, $isBuildServer = false)
|
||||
{
|
||||
if ($isBuildServer) {
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
<?php
|
||||
|
||||
use App\Events\ServerReachabilityChanged;
|
||||
use App\Livewire\Server\ValidateAndInstall;
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\PrivateKey;
|
||||
use App\Models\Server;
|
||||
use App\Models\Team;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Event;
|
||||
use Illuminate\Support\Facades\Process;
|
||||
use Livewire\Livewire;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
beforeEach(function () {
|
||||
InstanceSettings::forceCreate(['id' => 0]);
|
||||
Event::fake([ServerReachabilityChanged::class]);
|
||||
|
||||
$user = User::factory()->create();
|
||||
$this->team = Team::factory()->create();
|
||||
$user->teams()->attach($this->team, ['role' => 'owner']);
|
||||
$this->actingAs($user);
|
||||
session(['currentTeam' => $this->team]);
|
||||
|
||||
$this->server = Server::factory()->create([
|
||||
'team_id' => $this->team->id,
|
||||
'user' => 'root',
|
||||
'private_key_id' => PrivateKey::factory()->create(['team_id' => $this->team->id])->id,
|
||||
]);
|
||||
});
|
||||
|
||||
function fakeDockerVersionOutput(string $output, int $exitCode = 0): void
|
||||
{
|
||||
Process::fake([
|
||||
'*compose*' => Process::result(output: 'v2.40.3', exitCode: 0),
|
||||
'*' => Process::result(output: $output, exitCode: $exitCode),
|
||||
]);
|
||||
}
|
||||
|
||||
function validateDockerVersionError(Server $server): ?string
|
||||
{
|
||||
$component = Livewire::test(ValidateAndInstall::class, ['server' => $server])->instance();
|
||||
$component->uptime = '1 day';
|
||||
$component->validateDockerVersion();
|
||||
|
||||
return $component->error;
|
||||
}
|
||||
|
||||
it('accepts Docker Engine 29', function () {
|
||||
fakeDockerVersionOutput('{"Client":{"Version":"29.6.2"},"Server":{"Version":"29.6.2"}}');
|
||||
|
||||
expect($this->server->validateDockerEngineVersion())->toBeTrue()
|
||||
->and($this->server->fresh()->dockerVersion())->toBe('29.6.2');
|
||||
});
|
||||
|
||||
it('reports an unreadable docker version instead of a too old one', function (string $output, int $exitCode) {
|
||||
fakeDockerVersionOutput($output, $exitCode);
|
||||
|
||||
$error = validateDockerVersionError($this->server);
|
||||
|
||||
expect($error)->toContain('could not read the Docker Engine version')
|
||||
->toContain('Run "docker version --format json" on the server as the root user')
|
||||
->not->toContain('Minimum Docker Engine version')
|
||||
->and($this->server->fresh()->dockerVersion())->toBeNull()
|
||||
->and($this->server->fresh()->validation_logs)->toBe($error);
|
||||
})->with([
|
||||
'text before the json' => ['Welcome to the server'."\n".'{"Server":{"Version":"29.6.2"}}', 0],
|
||||
'command failed' => ['', 1],
|
||||
]);
|
||||
|
||||
it('reports the detected version when docker is too old', function () {
|
||||
fakeDockerVersionOutput('{"Server":{"Version":"23.0.6"}}');
|
||||
|
||||
expect(validateDockerVersionError($this->server))
|
||||
->toContain('Docker Engine 23.0.6 is installed')
|
||||
->toContain('version 24 or newer');
|
||||
});
|
||||
|
||||
it('shows the sudo command for non-root servers', function () {
|
||||
$this->server->update(['user' => 'deploy']);
|
||||
fakeDockerVersionOutput('not json');
|
||||
|
||||
expect(validateDockerVersionError($this->server->fresh()))
|
||||
->toContain('Run "sudo docker version --format json" on the server as the deploy user');
|
||||
});
|
||||
Reference in New Issue
Block a user