mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-04 10:42:23 +00:00
fix(runners): preserve unsaved settings during Sysbox checks
Move Sysbox checks and installation into a lazy Livewire component so its requests do not reset the runner form's unsaved state. Update tests to cover the separate component.
This commit is contained in:
1 parent
23eebbf2ad
commit
faf0b6fac4
6 files changed
+108
-67
No files matched your search
@@ -55,11 +55,6 @@ class GithubRunners extends Component
|
||||
|
||||
public bool $allowPullRequests = false;
|
||||
|
||||
/** Null until checked over SSH. */
|
||||
public ?bool $isSysboxInstalled = null;
|
||||
|
||||
protected $listeners = ['sysboxInstalled' => 'checkSysbox'];
|
||||
|
||||
public function mount(string $server_uuid)
|
||||
{
|
||||
try {
|
||||
@@ -178,7 +173,6 @@ class GithubRunners extends Component
|
||||
$githubApp = $this->selectedApp;
|
||||
$this->authorize('update', $githubApp);
|
||||
if ($this->dockerMode === GithubRunnerDockerMode::Sysbox->value && ! InstallSysbox::isInstalled($this->server)) {
|
||||
$this->isSysboxInstalled = false;
|
||||
throw ValidationException::withMessages(['dockerMode' => 'Sysbox is not installed on this server. Install it, then save again.']);
|
||||
}
|
||||
|
||||
@@ -258,25 +252,6 @@ class GithubRunners extends Component
|
||||
}
|
||||
}
|
||||
|
||||
public function checkSysbox(): void
|
||||
{
|
||||
$this->authorize('view', $this->server);
|
||||
$this->isSysboxInstalled = InstallSysbox::isInstalled($this->server);
|
||||
}
|
||||
|
||||
public function installSysbox()
|
||||
{
|
||||
$this->authorize('update', $this->server);
|
||||
|
||||
try {
|
||||
$activity = InstallSysbox::run($this->server);
|
||||
$this->dispatch('sysbox-install-started');
|
||||
$this->dispatch('activityMonitor', $activity->id, 'sysboxInstalled');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the runner group when the App can run workflow jobs. An App that is not ready is saved anyway:
|
||||
* the page lists what it misses, and the runner group is created when the first job is provisioned.
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
namespace App\Livewire\Server;
|
||||
|
||||
use App\Actions\Server\InstallSysbox;
|
||||
use App\Models\Server;
|
||||
use Illuminate\Contracts\View\View;
|
||||
use Illuminate\Foundation\Auth\Access\AuthorizesRequests;
|
||||
use Livewire\Attributes\Lazy;
|
||||
use Livewire\Component;
|
||||
|
||||
/**
|
||||
* A separate component, so that its requests do not send the unsaved fields of the runner form.
|
||||
* Otherwise the check would store a new Docker mode in the form snapshot and hide the unsaved bar.
|
||||
*/
|
||||
#[Lazy]
|
||||
class SysboxStatus extends Component
|
||||
{
|
||||
use AuthorizesRequests;
|
||||
|
||||
public Server $server;
|
||||
|
||||
public bool $isInstalled = false;
|
||||
|
||||
protected $listeners = ['sysboxInstalled' => 'checkSysbox'];
|
||||
|
||||
public function mount(Server $server): void
|
||||
{
|
||||
$this->authorize('view', $server);
|
||||
$this->server = $server;
|
||||
$this->checkSysbox();
|
||||
}
|
||||
|
||||
public function checkSysbox(): void
|
||||
{
|
||||
$this->authorize('view', $this->server);
|
||||
$this->isInstalled = InstallSysbox::isInstalled($this->server);
|
||||
}
|
||||
|
||||
public function installSysbox()
|
||||
{
|
||||
$this->authorize('update', $this->server);
|
||||
|
||||
try {
|
||||
$activity = InstallSysbox::run($this->server);
|
||||
$this->dispatch('sysbox-install-started');
|
||||
$this->dispatch('activityMonitor', $activity->id, 'sysboxInstalled');
|
||||
} catch (\Throwable $e) {
|
||||
return handleError($e, $this);
|
||||
}
|
||||
}
|
||||
|
||||
public function placeholder(): View
|
||||
{
|
||||
return view('livewire.server.sysbox-status-placeholder');
|
||||
}
|
||||
|
||||
public function render(): View
|
||||
{
|
||||
return view('livewire.server.sysbox-status');
|
||||
}
|
||||
}
|
||||
@@ -171,32 +171,8 @@
|
||||
:placeholder="config('constants.github_runner.image')"
|
||||
helper="Empty uses the latest official runner image, which Coolify pulls for every runner. Set a tag, for example ghcr.io/actions/actions-runner:2.337.0, to pin a version, or use a custom image based on the official one to add tools. GitHub stops sending jobs to runners that are more than 30 days old." />
|
||||
</div>
|
||||
<div x-cloak x-show="$wire.dockerMode === 'sysbox'" class="mt-4"
|
||||
x-effect="if ($wire.dockerMode === 'sysbox' && $wire.isSysboxInstalled === null) $wire.checkSysbox()">
|
||||
@if ($isSysboxInstalled === true)
|
||||
<x-callout type="success" title="Sysbox is installed">
|
||||
Jobs get a private Docker daemon in an unprivileged container.
|
||||
</x-callout>
|
||||
@elseif ($isSysboxInstalled === false)
|
||||
<x-callout type="warning" title="Sysbox is not installed">
|
||||
Isolated Docker needs the Sysbox runtime. Coolify installs Sysbox
|
||||
{{ config('constants.github_runner.sysbox.version') }} from the official package
|
||||
(Debian and Ubuntu, kernel 5.12 or newer). Docker reloads its configuration, and
|
||||
running containers are not restarted.
|
||||
@can('update', $server)
|
||||
<div class="mt-3">
|
||||
<x-forms.button type="button" wire:click="installSysbox"
|
||||
wire:loading.attr="disabled" wire:target="installSysbox">
|
||||
Install Sysbox
|
||||
</x-forms.button>
|
||||
</div>
|
||||
@endcan
|
||||
</x-callout>
|
||||
@else
|
||||
<x-callout type="info" title="Checking Sysbox">
|
||||
Coolify checks if Sysbox is installed on this server.
|
||||
</x-callout>
|
||||
@endif
|
||||
<div x-cloak x-show="$wire.dockerMode === 'sysbox'" class="mt-4">
|
||||
<livewire:server.sysbox-status :server="$server" />
|
||||
</div>
|
||||
</x-application.settings-section>
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
<div>
|
||||
<x-callout type="info" title="Checking Sysbox">
|
||||
Coolify checks if Sysbox is installed on this server.
|
||||
</x-callout>
|
||||
</div>
|
||||
@@ -0,0 +1,22 @@
|
||||
<div>
|
||||
@if ($isInstalled)
|
||||
<x-callout type="success" title="Sysbox is installed">
|
||||
Jobs get a private Docker daemon in an unprivileged container.
|
||||
</x-callout>
|
||||
@else
|
||||
<x-callout type="warning" title="Sysbox is not installed">
|
||||
Isolated Docker needs the Sysbox runtime. Coolify installs Sysbox
|
||||
{{ config('constants.github_runner.sysbox.version') }} from the official package
|
||||
(Debian and Ubuntu, kernel 5.12 or newer). Docker reloads its configuration, and
|
||||
running containers are not restarted.
|
||||
@can('update', $server)
|
||||
<div class="mt-3">
|
||||
<x-forms.button type="button" wire:click="installSysbox"
|
||||
wire:loading.attr="disabled" wire:target="installSysbox">
|
||||
Install Sysbox
|
||||
</x-forms.button>
|
||||
</div>
|
||||
@endcan
|
||||
</x-callout>
|
||||
@endif
|
||||
</div>
|
||||
@@ -8,6 +8,7 @@ use App\Jobs\ProvisionGithubRunnerJob;
|
||||
use App\Livewire\Server\GithubRunnerExecutions;
|
||||
use App\Livewire\Server\GithubRunners;
|
||||
use App\Livewire\Server\Show;
|
||||
use App\Livewire\Server\SysboxStatus;
|
||||
use App\Livewire\Source\Github\Change;
|
||||
use App\Livewire\Source\Github\Create;
|
||||
use App\Models\GithubApp;
|
||||
@@ -446,8 +447,7 @@ describe('isolated Docker with Sysbox', function () {
|
||||
->set('githubAppId', $this->githubApp->id)
|
||||
->set('dockerMode', 'sysbox')
|
||||
->call('submit')
|
||||
->assertHasErrors('dockerMode')
|
||||
->assertSet('isSysboxInstalled', false);
|
||||
->assertHasErrors('dockerMode');
|
||||
|
||||
expect(GithubRunnerConfig::count())->toBe(0);
|
||||
});
|
||||
@@ -469,29 +469,29 @@ describe('isolated Docker with Sysbox', function () {
|
||||
it('checks that Sysbox can mount its FUSE file system', function () {
|
||||
Process::fake(['*' => Process::result(output: '')]);
|
||||
|
||||
Livewire::actingAs($this->owner)
|
||||
->test(GithubRunners::class, ['server_uuid' => $this->server->uuid])
|
||||
->call('checkSysbox');
|
||||
Livewire::withoutLazyLoading()
|
||||
->actingAs($this->owner)
|
||||
->test(SysboxStatus::class, ['server' => $this->server]);
|
||||
|
||||
Process::assertRan(fn ($process) => str_contains(remoteProcessScript($process), 'test -x /usr/bin/fusermount3'));
|
||||
});
|
||||
|
||||
it('shows the install button when Sysbox is missing', function () {
|
||||
Process::fake(['*' => Process::result(output: '')]);
|
||||
GithubRunnerConfig::create(['server_id' => $this->server->id, 'github_app_id' => $this->githubApp->id, 'labels' => ['coolify']]);
|
||||
|
||||
Livewire::actingAs($this->owner)
|
||||
->test(GithubRunners::class, ['server_uuid' => $this->server->uuid])
|
||||
->call('checkSysbox')
|
||||
->assertSet('isSysboxInstalled', false)
|
||||
Livewire::withoutLazyLoading()
|
||||
->actingAs($this->owner)
|
||||
->test(SysboxStatus::class, ['server' => $this->server])
|
||||
->assertSet('isInstalled', false)
|
||||
->assertSee('Install Sysbox');
|
||||
});
|
||||
|
||||
it('stops before the installation on servers without apt', function () {
|
||||
Process::fake(['*' => Process::result(output: '')]);
|
||||
|
||||
Livewire::actingAs($this->owner)
|
||||
->test(GithubRunners::class, ['server_uuid' => $this->server->uuid])
|
||||
Livewire::withoutLazyLoading()
|
||||
->actingAs($this->owner)
|
||||
->test(SysboxStatus::class, ['server' => $this->server])
|
||||
->call('installSysbox')
|
||||
->assertNotDispatched('activityMonitor');
|
||||
|
||||
@@ -499,14 +499,15 @@ describe('isolated Docker with Sysbox', function () {
|
||||
});
|
||||
|
||||
it('does not let members install Sysbox', function () {
|
||||
Process::fake();
|
||||
Process::fake(['*' => Process::result(output: '')]);
|
||||
|
||||
Livewire::actingAs($this->member)
|
||||
->test(GithubRunners::class, ['server_uuid' => $this->server->uuid])
|
||||
Livewire::withoutLazyLoading()
|
||||
->actingAs($this->member)
|
||||
->test(SysboxStatus::class, ['server' => $this->server])
|
||||
->call('installSysbox')
|
||||
->assertForbidden();
|
||||
|
||||
Process::assertNothingRan();
|
||||
Process::assertDidntRun(fn ($process) => str_contains(remoteProcessScript($process), 'apt-get'));
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
Reference in new issue
Block a user