87 lines
3.8 KiB
PHP
87 lines
3.8 KiB
PHP
<?php
|
|
|
|
use App\Models\Host;
|
|
use App\Provisioning\Jobs\CollectHostLoad;
|
|
use App\Services\Proxmox\FakeProxmoxClient;
|
|
use App\Services\Proxmox\HostLoadSeries;
|
|
use App\Services\Proxmox\ProxmoxClient;
|
|
use Illuminate\Contracts\Queue\ShouldBeUnique;
|
|
|
|
/**
|
|
* Der Sammler holt die Stundenkurve dort, wo der Tunnel ist.
|
|
*
|
|
* Er existiert genau deshalb: nur der `provisioning`-Container hängt im
|
|
* WireGuard-Netz. Der `app`-Container, der die Konsole rendert, erreicht die
|
|
* Management-Adresse eines Hosts gar nicht — auf echter Hardware blieben
|
|
* deshalb alle Kacheln leer, während der Host sichtbar online war.
|
|
*/
|
|
it('runs on the queue that can actually reach a host', function () {
|
|
// Kein Detail: dieselbe Warteschlange wie PingHosts, aus demselben Grund.
|
|
expect((new CollectHostLoad)->queue)->toBe('provisioning');
|
|
});
|
|
|
|
it('leaves a series behind for every host worth asking', function () {
|
|
$s = fakeServices();
|
|
$s['pve']->rrd = [['time' => 1754035200, 'cpu' => 0.5, 'memused' => 1, 'memtotal' => 2]];
|
|
$host = Host::factory()->active()->create(['api_token_ref' => 'ref']);
|
|
|
|
(new CollectHostLoad)->handle(app(HostLoadSeries::class));
|
|
|
|
expect(app(HostLoadSeries::class)->forHost($host)['available'])->toBeTrue();
|
|
});
|
|
|
|
it('skips a host that has no token to ask with', function () {
|
|
// Ein Host mitten in der Übernahme hat noch keinen — ihn zu fragen wäre ein
|
|
// sicherer Fehlschlag je Minute, und eine Zeile im Protokoll dazu.
|
|
$s = fakeServices();
|
|
$s['pve']->rrd = [['time' => 1754035200, 'cpu' => 0.5, 'memused' => 1, 'memtotal' => 2]];
|
|
$host = Host::factory()->create(['status' => 'onboarding', 'api_token_ref' => null]);
|
|
|
|
(new CollectHostLoad)->handle(app(HostLoadSeries::class));
|
|
|
|
expect(app(HostLoadSeries::class)->forHost($host)['available'])->toBeFalse();
|
|
});
|
|
|
|
it('keeps collecting after one host refuses to answer', function () {
|
|
// Eine Flotte darf nicht daran hängen, dass der erste Host schweigt.
|
|
$s = fakeServices();
|
|
$s['pve']->rrd = [['time' => 1754035200, 'cpu' => 0.5, 'memused' => 1, 'memtotal' => 2]];
|
|
$silent = Host::factory()->active()->create(['api_token_ref' => 'ref-1', 'name' => 'aaa-still']);
|
|
$healthy = Host::factory()->active()->create(['api_token_ref' => 'ref-2', 'name' => 'zzz-antwortet']);
|
|
|
|
// Der Fake wirft für den ersten Host und antwortet für den zweiten.
|
|
app()->instance(ProxmoxClient::class, new class($silent->id) extends FakeProxmoxClient
|
|
{
|
|
public function __construct(private int $silentId)
|
|
{
|
|
$this->rrd = [['time' => 1754035200, 'cpu' => 0.5, 'memused' => 1, 'memtotal' => 2]];
|
|
}
|
|
|
|
public function nodeRrdData(string $node, string $timeframe = 'hour'): array
|
|
{
|
|
if ($this->host?->id === $this->silentId) {
|
|
throw new RuntimeException('proxmox unreachable');
|
|
}
|
|
|
|
return $this->rrd;
|
|
}
|
|
});
|
|
|
|
(new CollectHostLoad)->handle(app(HostLoadSeries::class));
|
|
|
|
expect(app(HostLoadSeries::class)->forHost($silent)['available'])->toBeFalse()
|
|
->and(app(HostLoadSeries::class)->forHost($healthy)['available'])->toBeTrue();
|
|
});
|
|
|
|
it('never lets a second collector queue up behind a slow one', function () {
|
|
// Die provisioning-Warteschlange ist DIESELBE, auf der Kunden-Bereitstellung
|
|
// läuft. Ein Host, der in die Zeitüberschreitung läuft, kostet 15 Sekunden;
|
|
// bei genug Hosts ist der minütliche Lauf noch nicht fertig, wenn der
|
|
// nächste kommt — und der Rückstau verzögert dann bezahlte Arbeit.
|
|
// Dasselbe Mittel wie bei CollectInstanceTraffic nebenan.
|
|
expect(new CollectHostLoad)->toBeInstanceOf(ShouldBeUnique::class)
|
|
// Und nur ein Versuch: eine verpasste Minute wird von der nächsten
|
|
// Minute geholt, nicht von einer Wiederholung derselben.
|
|
->and((new CollectHostLoad)->tries)->toBe(1);
|
|
});
|