CluPilotCloud/tests/Feature/Host/BootstrapArchiveTest.php

89 lines
3.1 KiB
PHP

<?php
/**
* Das Archiv ist der einzige Weg, auf dem das Skript auf eine nackte Maschine
* kommt. Was hier schiefgeht, merkt man erst im Rettungssystem eines Servers,
* der schon bestellt ist.
*/
beforeEach(function () {
fakeServices();
$this->code = App\Support\HostEnrolment::issue(App\Models\Host::factory()->create());
});
function fetchArchive(): \Illuminate\Testing\TestResponse
{
return test()->get('http://files.clupilot.test/bootstrap.tar.gz?code='.test()->code);
}
it('serves the bootstrap script as an archive', function () {
$response = fetchArchive();
$response->assertOk();
expect($response->headers->get('Content-Type'))->toBe('application/gzip');
});
/**
* Der oberste Eintrag MUSS `bootstrap/` heißen. Die kopierte Zeile packt mit
* `tar xz -C /opt/clupilot` aus, und das Skript sucht sich selbst unter
* `/opt/clupilot/bootstrap` — ein anderer Name landet daneben, und der Fehler
* zeigt sich als „lib/report.sh nicht gefunden" auf einer Maschine, an die man
* nur über die Anbieterkonsole kommt.
*/
it('unpacks to bootstrap/ with the script and its library', function () {
fetchArchive()->assertOk();
$listing = shell_exec('tar tzf '.escapeshellarg(storage_path('app/bootstrap.tar.gz')));
expect($listing)
->toContain('bootstrap/clupilot-bootstrap.sh')
->toContain('bootstrap/lib/report.sh')
->toContain('bootstrap/lib/proxmox.sh')
->toContain('bootstrap/lib/network.sh')
->toContain('bootstrap/lib/traefik.sh')
->toContain('bootstrap/lib/template.sh')
->toContain('bootstrap/lib/register.sh')
->toContain('bootstrap/assets/docker-compose.yml');
});
/**
* Es darf nichts Vertrauliches darin stehen. Alle Geheimnisse reisen in der
* Befehlszeile mit, die der Adminbereich genau einmal zeigt.
*/
it('carries no secrets', function () {
fetchArchive()->assertOk();
$dir = sys_get_temp_dir().'/bootstrap-pruefung-'.bin2hex(random_bytes(4));
mkdir($dir);
shell_exec('tar xzf '.escapeshellarg(storage_path('app/bootstrap.tar.gz')).' -C '.escapeshellarg($dir));
$content = '';
foreach (glob($dir.'/bootstrap/{,lib/,assets/}*', GLOB_BRACE) as $file) {
if (is_file($file)) {
$content .= file_get_contents($file);
}
}
expect($content)->not->toBeEmpty()
->and($content)->not->toContain(config('app.key'))
->and($content)->not->toContain((string) env('SECRETS_KEY'));
shell_exec('rm -rf '.escapeshellarg($dir));
});
/**
* Ein Archiv, das einmal gebaut wurde und danach liegen bleibt, ist die Fassung
* von dem Tag, an dem jemand zuletzt daran gedacht hat.
*/
it('rebuilds itself when the script changes', function () {
fetchArchive()->assertOk();
$first = filemtime(storage_path('app/bootstrap.tar.gz'));
// Eine Sekunde zurück, damit die Änderung sichtbar später liegt.
touch(storage_path('app/bootstrap.tar.gz'), $first - 10);
touch(base_path('deploy/bootstrap/clupilot-bootstrap.sh'));
fetchArchive()->assertOk();
expect(filemtime(storage_path('app/bootstrap.tar.gz')))->toBeGreaterThan($first - 10);
});