147 lines
5.3 KiB
PHP
147 lines
5.3 KiB
PHP
<?php
|
|
|
|
use App\Livewire\Admin\HostCreate;
|
|
use App\Models\Datacenter;
|
|
use App\Models\Host;
|
|
use App\Models\Operator;
|
|
use App\Support\HostEnrolment;
|
|
use Livewire\Livewire;
|
|
|
|
beforeEach(function () {
|
|
fakeServices();
|
|
|
|
config()->set('provisioning.wireguard.hub_public_key', 'HUBKEYHUBKEYHUBKEYHUBKEYHUBKEYHUBKEYHUBKEY0=');
|
|
config()->set('provisioning.wireguard.endpoint', 'hub.clupilot.cloud:51820');
|
|
config()->set('provisioning.wireguard.hub_address', '10.66.0.1');
|
|
config()->set('provisioning.wireguard.subnet', '10.66.0.0/24');
|
|
config()->set('admin_access.app_host', 'clupilot.cloud');
|
|
|
|
// firstOrCreate: die Migrationen legen Rechenzentren schon an, und ein
|
|
// zweites `fsn` verletzt den eindeutigen Index.
|
|
Datacenter::query()->firstOrCreate(
|
|
['code' => 'fsn'],
|
|
['name' => 'Falkenstein', 'location' => 'DE', 'active' => true],
|
|
);
|
|
});
|
|
|
|
function createHostAs(): \Livewire\Features\SupportTesting\Testable
|
|
{
|
|
return Livewire::actingAs(Operator::factory()->role('Owner')->create(), 'operator')
|
|
->test(HostCreate::class)
|
|
->set('name', 'pve-fsn-2')
|
|
->set('datacenter', 'fsn')
|
|
->set('public_ip', '198.51.45.9')
|
|
->set('root_password', 'ein-langes-kennwort')
|
|
->call('save');
|
|
}
|
|
|
|
/**
|
|
* Die eine Frage, die diese Seite beantworten muss: was gebe ich auf dem Server
|
|
* ein? Steht die Zeile nicht da, ist die ganze Übernahme nicht benutzbar.
|
|
*/
|
|
it('shows the command exactly once, right after creating the host', function () {
|
|
$page = createHostAs();
|
|
|
|
$page->assertSee('curl')
|
|
->assertSee('clupilot-bootstrap.sh')
|
|
->assertSee('--code');
|
|
|
|
// Ein frisch geladenes Bauteil — also jemand, der die Seite neu lädt — hat
|
|
// nichts mehr. Der Code steht nur als Hash in der Datenbank.
|
|
Livewire::actingAs(Operator::factory()->role('Owner')->create(), 'operator')
|
|
->test(HostCreate::class)
|
|
->assertDontSee('curl');
|
|
});
|
|
|
|
/**
|
|
* Die Zeile muss ALLES tragen, was das Skript vor dem Tunnel braucht (Spec §5).
|
|
* Fehlt ein Wert, läuft sie an und endet in einem Tunnel ohne Handshake — und
|
|
* das merkt man erst auf einer Maschine, die schon bestellt ist.
|
|
*/
|
|
it('carries every value the script needs before the tunnel', function () {
|
|
$command = createHostAs()->get('command');
|
|
|
|
foreach (['--code', '--wg-private', '--wg-ip', '--hub-pubkey', '--hub-endpoint', '--api'] as $argument) {
|
|
expect($command)->toContain($argument);
|
|
}
|
|
|
|
expect($command)
|
|
->toContain('hub.clupilot.cloud:51820')
|
|
->toContain('--api http://10.66.0.1')
|
|
->toContain('/opt/clupilot');
|
|
});
|
|
|
|
/**
|
|
* Über den ÖFFENTLICHEN Hostnamen. Die Konsole läuft unter admin.…, aber die
|
|
* Zeile wird auf einer Maschine ausgeführt, die den Adminbereich nicht erreichen
|
|
* darf — sie ist genau dafür abgeriegelt. Ein Link auf den Konsolen-Hostnamen
|
|
* liefe in eine 404 auf einem Server, an den man dann nur noch über die
|
|
* Anbieterkonsole kommt.
|
|
*/
|
|
it('fetches the installer over the public hostname', function () {
|
|
expect(createHostAs()->get('command'))
|
|
->toContain('https://clupilot.cloud/bootstrap.tar.gz')
|
|
->not->toContain('admin.');
|
|
});
|
|
|
|
/**
|
|
* Der Schlüssel ist base64 und enthält +, / und =. Ohne Anführungszeichen
|
|
* zerlegt die Shell die Zeile an einer Stelle, die niemand sieht.
|
|
*/
|
|
it('quotes the keys so a shell cannot split them', function () {
|
|
$command = createHostAs()->get('command');
|
|
|
|
expect($command)->toMatch("/--wg-private '[^']+'/")
|
|
->and($command)->toMatch("/--hub-pubkey '[^']+'/");
|
|
});
|
|
|
|
/**
|
|
* Der Code aus der Zeile muss der sein, mit dem der Host sich meldet. Sonst ist
|
|
* die Anleitung richtig und der Ablauf trotzdem kaputt.
|
|
*/
|
|
it('shows a code that actually resolves to the new host', function () {
|
|
$command = createHostAs()->get('command');
|
|
|
|
preg_match('/--code ([A-Za-z0-9]+)/', $command, $matches);
|
|
|
|
expect(HostEnrolment::claim($matches[1])?->name)->toBe('pve-fsn-2');
|
|
});
|
|
|
|
/**
|
|
* Ein leerer Hub-Schlüssel ergibt eine Zeile, die läuft und nie handshaked.
|
|
* Das gehört gesagt, BEVOR jemand einen Server bestellt.
|
|
*/
|
|
it('warns before creating a host when the tunnel settings are missing', function () {
|
|
config()->set('provisioning.wireguard.hub_public_key', '');
|
|
config()->set('provisioning.wireguard.endpoint', '');
|
|
|
|
Livewire::actingAs(Operator::factory()->role('Owner')->create(), 'operator')
|
|
->test(HostCreate::class)
|
|
->assertSee('CLUPILOT_WG_HUB_PUBKEY')
|
|
->assertSee('CLUPILOT_WG_ENDPOINT');
|
|
});
|
|
|
|
/**
|
|
* Die drei Schritte sind die Anleitung. Fehlt einer, fehlt genau der, den
|
|
* jemand nicht von selbst weiß — meistens das Rettungssystem.
|
|
*/
|
|
it('spells out the three steps, rescue system first', function () {
|
|
createHostAs()
|
|
->assertSee(__('hosts.takeover.step1_title'))
|
|
->assertSee(__('hosts.takeover.step2_title'))
|
|
->assertSee(__('hosts.takeover.step3_title'));
|
|
});
|
|
|
|
it('refuses to create a host without the permission', function () {
|
|
Livewire::actingAs(Operator::factory()->create(), 'operator')
|
|
->test(HostCreate::class)
|
|
->set('name', 'pve-fsn-3')
|
|
->set('datacenter', 'fsn')
|
|
->set('public_ip', '198.51.45.10')
|
|
->set('root_password', 'ein-langes-kennwort')
|
|
->call('save')
|
|
->assertForbidden();
|
|
|
|
expect(Host::query()->where('name', 'pve-fsn-3')->exists())->toBeFalse();
|
|
});
|