fix(engine): drop wg peer on host removal; clear reboot state on deadline fail
- Removing a host now removes its WireGuard hub peer so a freed wg_ip can't route to the removed server via a stale peer. - RebootIntoPveKernel clears reboot_issued/deadline when it times out, so the manual retry action actually re-issues a reboot instead of failing instantly. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>feat/portal-design
parent
91e308efb0
commit
b65fe69088
|
|
@ -3,6 +3,7 @@
|
||||||
namespace App\Livewire\Admin;
|
namespace App\Livewire\Admin;
|
||||||
|
|
||||||
use App\Models\Host;
|
use App\Models\Host;
|
||||||
|
use App\Services\Wireguard\WireguardHub;
|
||||||
use LivewireUI\Modal\ModalComponent;
|
use LivewireUI\Modal\ModalComponent;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -21,11 +22,16 @@ class ConfirmRemoveHost extends ModalComponent
|
||||||
$this->name = Host::query()->where('uuid', $uuid)->value('name') ?? '';
|
$this->name = Host::query()->where('uuid', $uuid)->value('name') ?? '';
|
||||||
}
|
}
|
||||||
|
|
||||||
public function remove()
|
public function remove(WireguardHub $hub)
|
||||||
{
|
{
|
||||||
$host = Host::query()->where('uuid', $this->uuid)->first();
|
$host = Host::query()->where('uuid', $this->uuid)->first();
|
||||||
|
|
||||||
if ($host !== null) {
|
if ($host !== null) {
|
||||||
|
// Drop the hub peer first so its wg_ip can't later be reused while a
|
||||||
|
// stale peer still routes to the removed server.
|
||||||
|
if (filled($host->wg_pubkey)) {
|
||||||
|
$hub->removePeer($host->wg_pubkey);
|
||||||
|
}
|
||||||
$host->runs()->delete(); // cascades run_resources + step_events
|
$host->runs()->delete(); // cascades run_resources + step_events
|
||||||
$host->delete();
|
$host->delete();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,11 @@ class RebootIntoPveKernel extends HostStep
|
||||||
|
|
||||||
$deadline = $run->context('reboot_deadline');
|
$deadline = $run->context('reboot_deadline');
|
||||||
if ($deadline !== null && now()->greaterThan(Carbon::parse($deadline))) {
|
if ($deadline !== null && now()->greaterThan(Carbon::parse($deadline))) {
|
||||||
|
// Clear the reboot markers so a manual retry re-issues a fresh reboot
|
||||||
|
// instead of immediately re-hitting the expired deadline.
|
||||||
|
$run->forgetContext('reboot_issued');
|
||||||
|
$run->forgetContext('reboot_deadline');
|
||||||
|
|
||||||
return StepResult::fail('Host did not return on the Proxmox kernel before the deadline.');
|
return StepResult::fail('Host did not return on the Proxmox kernel before the deadline.');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -99,8 +99,10 @@ it('retries a failed run from the detail page', function () {
|
||||||
Queue::assertPushed(AdvanceRunJob::class);
|
Queue::assertPushed(AdvanceRunJob::class);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('removes the host record without wiping the server', function () {
|
it('removes the host record and its wireguard peer without wiping the server', function () {
|
||||||
$host = Host::factory()->create();
|
$services = fakeServices();
|
||||||
|
$host = Host::factory()->active()->create(['wg_pubkey' => 'HOSTPUB=']);
|
||||||
|
$services['hub']->addPeer('HOSTPUB=', $host->wg_ip);
|
||||||
ProvisioningRun::factory()->forHost($host)->create();
|
ProvisioningRun::factory()->forHost($host)->create();
|
||||||
|
|
||||||
Livewire::actingAs(admin())
|
Livewire::actingAs(admin())
|
||||||
|
|
@ -108,7 +110,8 @@ it('removes the host record without wiping the server', function () {
|
||||||
->call('remove')
|
->call('remove')
|
||||||
->assertRedirect(route('admin.hosts'));
|
->assertRedirect(route('admin.hosts'));
|
||||||
|
|
||||||
expect(Host::query()->find($host->id))->toBeNull();
|
expect(Host::query()->find($host->id))->toBeNull()
|
||||||
|
->and($services['hub']->peers())->not->toHaveKey('HOSTPUB=');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('renders the live stepper for a running host', function () {
|
it('renders the live stepper for a running host', function () {
|
||||||
|
|
|
||||||
|
|
@ -175,7 +175,8 @@ it('fails when the host does not return before the reboot deadline', function ()
|
||||||
'reboot_deadline' => now()->subMinute()->toIso8601String(),
|
'reboot_deadline' => now()->subMinute()->toIso8601String(),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
expect(app(RebootIntoPveKernel::class)->execute($run)->type)->toBe('fail');
|
expect(app(RebootIntoPveKernel::class)->execute($run)->type)->toBe('fail')
|
||||||
|
->and($run->fresh()->context('reboot_issued'))->toBeNull(); // cleared so a retry re-issues
|
||||||
});
|
});
|
||||||
|
|
||||||
// --- CreateAutomationToken ---
|
// --- CreateAutomationToken ---
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue