mirror of
https://github.com/coollabsio/coolify.git
synced 2026-09-26 01:10:30 -04:00
feat(dev): reuse prepared QEMU images for faster VM startup
Provision Docker once in a prepared image and use a seed ISO for new VMs. Switch development examples to the main branch and refresh service template timestamps.
This commit is contained in:
@@ -54,3 +54,10 @@
|
||||
|
||||
## Pass identities to Livewire actions
|
||||
- Pass record IDs to Livewire actions instead of display values, and resolve team-scoped records on the server. When JavaScript needs text, use `@js()` or `Js::from()`.
|
||||
- Mark Livewire properties that select records or feed server-side lookups as `#[Locked]`; clients can change every other public property.
|
||||
|
||||
## Keep host test runs away from the dev app cache
|
||||
- The repository is bind-mounted into the dev `coolify` container. Tests that call `app:init` run `optimize` and write a testing config/route cache into `bootstrap/cache`, so the dev app returns 500. For broad host test runs, set `APP_CONFIG_CACHE`, `APP_ROUTES_CACHE`, `APP_EVENTS_CACHE`, `APP_SERVICES_CACHE`, and `APP_PACKAGES_CACHE` to a temporary directory.
|
||||
|
||||
## Test the real runtime image
|
||||
- Deployment shell commands run in the Alpine/BusyBox helper image and pass through the non-root sudo parser. Verify new flags and shell syntax in that image and with `parseCommandsByLineForSudo()`; faked command output hides both failures.
|
||||
|
||||
@@ -24,7 +24,7 @@ class ConfigureDevelopmentQemuHost
|
||||
|
||||
private function installDependencies(): void
|
||||
{
|
||||
$binaries = ['curl', 'docker', 'iptables', 'qemu-img', 'virsh', 'virt-install'];
|
||||
$binaries = ['curl', 'docker', 'iptables', 'qemu-img', 'virsh', 'virt-install', 'xorriso'];
|
||||
$check = collect($binaries)->map(fn (string $binary) => 'command -v '.escapeshellarg($binary))->implode(' && ');
|
||||
|
||||
if (Process::run($check)->successful()) {
|
||||
@@ -36,7 +36,7 @@ class ConfigureDevelopmentQemuHost
|
||||
}
|
||||
|
||||
$this->runOrFail('apt-get update');
|
||||
$this->runOrFail('DEBIAN_FRONTEND=noninteractive apt-get install -y curl iptables libvirt-clients libvirt-daemon-system qemu-utils qemu-system-x86 virtinst');
|
||||
$this->runOrFail('DEBIAN_FRONTEND=noninteractive apt-get install -y curl iptables libvirt-clients libvirt-daemon-system qemu-utils qemu-system-x86 virtinst xorriso');
|
||||
}
|
||||
|
||||
private function configureLibvirtNetwork(): void
|
||||
|
||||
@@ -33,25 +33,39 @@ class StartDevelopmentQemuVm
|
||||
}
|
||||
}
|
||||
|
||||
$this->createVm($profile);
|
||||
$preparedImage = $this->preparedImage($profile['domain']);
|
||||
|
||||
if (! File::exists($preparedImage)) {
|
||||
$this->createVm($profile, false);
|
||||
$this->waitForPreparation($profile['domain']);
|
||||
$this->runOrFail('virsh undefine '.escapeshellarg($profile['domain']));
|
||||
$this->runOrFail('mv '.escapeshellarg($this->vmDisk($profile['domain'])).' '.escapeshellarg($preparedImage));
|
||||
if (File::exists($preparedImage)) {
|
||||
File::chmod($preparedImage, 0644);
|
||||
}
|
||||
}
|
||||
|
||||
$this->createVm($profile, true);
|
||||
|
||||
ConfigureDevelopmentQemuHost::run();
|
||||
$this->waitForSsh($profile['ip']);
|
||||
}
|
||||
|
||||
/** @param array{domain: string, ip: string, user: string, mac: string, image: string, image_url: string, os_variant: string, provisioner: string} $profile */
|
||||
private function createVm(array $profile): void
|
||||
private function createVm(array $profile, bool $prepared): void
|
||||
{
|
||||
$directory = config('development-qemu.storage_path');
|
||||
File::ensureDirectoryExists($directory);
|
||||
File::chmod($directory, 0777);
|
||||
$this->moveLegacyFiles($directory);
|
||||
$baseImage = "{$directory}/{$profile['image']}";
|
||||
$disk = "{$directory}/{$profile['domain']}.qcow2";
|
||||
$baseImage = $prepared ? $this->preparedImage($profile['domain']) : "{$directory}/{$profile['image']}";
|
||||
$disk = $this->vmDisk($profile['domain']);
|
||||
$userData = "{$directory}/{$profile['domain']}-user-data.yaml";
|
||||
$metaData = "{$directory}/{$profile['domain']}-meta-data.yaml";
|
||||
$networkConfig = "{$directory}/{$profile['domain']}-network.yaml";
|
||||
$seedImage = "{$directory}/{$profile['domain']}-seed.iso";
|
||||
|
||||
if (! File::exists($baseImage)) {
|
||||
if (! $prepared && ! File::exists($baseImage)) {
|
||||
$this->runOrFail(sprintf(
|
||||
'curl --fail --location --output %s %s',
|
||||
escapeshellarg($baseImage),
|
||||
@@ -76,23 +90,50 @@ class StartDevelopmentQemuVm
|
||||
File::chmod($disk, 0666);
|
||||
}
|
||||
|
||||
File::put($userData, $this->userData($profile));
|
||||
File::put($networkConfig, $this->networkConfig($profile));
|
||||
if (! $prepared) {
|
||||
File::put($userData, $this->userData($profile));
|
||||
File::put($metaData, "instance-id: {$profile['domain']}\nlocal-hostname: {$profile['domain']}\n");
|
||||
File::put($networkConfig, $this->networkConfig($profile));
|
||||
$this->runOrFail(sprintf(
|
||||
'xorriso -as mkisofs -V cidata -graft-points -o %s %s %s %s',
|
||||
escapeshellarg($seedImage),
|
||||
escapeshellarg('user-data='.$userData),
|
||||
escapeshellarg('meta-data='.$metaData),
|
||||
escapeshellarg('network-config='.$networkConfig),
|
||||
));
|
||||
}
|
||||
|
||||
$seedDisk = $prepared ? '' : ' --disk path='.escapeshellarg($seedImage).',format=raw,bus=virtio,readonly=on';
|
||||
|
||||
$this->runOrFail(sprintf(
|
||||
'virt-install --connect qemu:///system --name %s --memory %d --vcpus %d --import --os-variant %s --disk path=%s,format=qcow2,bus=virtio --network network=%s,model=virtio,mac=%s --cloud-init user-data=%s,network-config=%s,disable=on --noautoconsole',
|
||||
'virt-install --connect qemu:///system --name %s --memory %d --vcpus %d --import --os-variant %s --disk path=%s,format=qcow2,bus=virtio%s --network network=%s,model=virtio,mac=%s --noautoconsole',
|
||||
escapeshellarg($profile['domain']),
|
||||
config('development-qemu.memory'),
|
||||
config('development-qemu.vcpus'),
|
||||
escapeshellarg($profile['os_variant']),
|
||||
escapeshellarg($disk),
|
||||
$seedDisk,
|
||||
escapeshellarg(config('development-qemu.libvirt_network')),
|
||||
escapeshellarg($profile['mac']),
|
||||
escapeshellarg($userData),
|
||||
escapeshellarg($networkConfig),
|
||||
));
|
||||
}
|
||||
|
||||
private function vmDisk(string $domain): string
|
||||
{
|
||||
return config('development-qemu.storage_path')."/{$domain}.qcow2";
|
||||
}
|
||||
|
||||
private function preparedImage(string $domain): string
|
||||
{
|
||||
return config('development-qemu.storage_path')."/{$domain}-prepared.qcow2";
|
||||
}
|
||||
|
||||
private function waitForPreparation(string $domain): void
|
||||
{
|
||||
$check = 'while [ "$(virsh domstate '.escapeshellarg($domain).')" != "shut off" ]; do sleep 2; done';
|
||||
$this->runOrFail('timeout 900 bash -c '.escapeshellarg($check));
|
||||
}
|
||||
|
||||
private function moveLegacyFiles(string $directory): void
|
||||
{
|
||||
$legacyDirectory = storage_path('app/development-qemu');
|
||||
@@ -116,7 +157,9 @@ class StartDevelopmentQemuVm
|
||||
File::delete([
|
||||
"{$directory}/{$domain}.qcow2",
|
||||
"{$directory}/{$domain}-user-data.yaml",
|
||||
"{$directory}/{$domain}-meta-data.yaml",
|
||||
"{$directory}/{$domain}-network.yaml",
|
||||
"{$directory}/{$domain}-seed.iso",
|
||||
]);
|
||||
}
|
||||
|
||||
@@ -126,28 +169,48 @@ class StartDevelopmentQemuVm
|
||||
$publicKey = config('development-qemu.public_key');
|
||||
$adminGroup = $profile['provisioner'] === 'apt' ? 'sudo' : 'wheel';
|
||||
$sudo = $profile['user'] === 'root' ? '' : " groups: [{$adminGroup}]\n sudo: ALL=(ALL) NOPASSWD:ALL\n";
|
||||
$shell = $profile['provisioner'] === 'apk' ? '/bin/ash' : '/bin/bash';
|
||||
$password = $profile['provisioner'] === 'apk'
|
||||
? ' passwd: $6$dd2d71373a57c9ac$8.GUqZYlL/QqUmpUuupWfTuKQjNKT7kO31K5cp7OIY5SbBamlAVkJnBDYsIVimMaBrUtYfFjX3u6hzts3nKaD.'."\n lock_passwd: false"
|
||||
: ' lock_passwd: true';
|
||||
|
||||
[$packages, $startDocker] = match ($profile['provisioner']) {
|
||||
'apk' => [" - docker\n - sudo", 'rc-update add docker default && service docker start'],
|
||||
'rpm' => [" - curl\n - sudo", 'curl -fsSL https://get.docker.com | sh && systemctl enable --now docker'],
|
||||
default => [" - docker.io\n - sudo", 'systemctl enable --now docker'],
|
||||
[$packages, $installDocker, $startDocker] = match ($profile['provisioner']) {
|
||||
'apk' => [" - docker\n - docker-cli-buildx\n - docker-cli-compose\n - sudo", '', 'rc-update add cgroups boot && service cgroups start && rc-update add docker default && service docker start'],
|
||||
default => [" - curl\n - sudo", "curl -fsSL https://get.docker.com -o /tmp/get-docker.sh\n sh /tmp/get-docker.sh\n ", 'systemctl enable --now docker'],
|
||||
};
|
||||
$addUserToDockerGroup = $profile['user'] === 'root' ? '' : "\n - usermod -aG docker {$profile['user']}";
|
||||
$addUserToDockerGroup = $profile['user'] === 'root' ? '' : ($profile['provisioner'] === 'apk'
|
||||
? "addgroup {$profile['user']} docker"
|
||||
: "usermod -aG docker {$profile['user']}");
|
||||
|
||||
return <<<YAML
|
||||
#cloud-config
|
||||
disable_root: false
|
||||
users:
|
||||
- name: {$profile['user']}
|
||||
{$sudo} shell: /bin/bash
|
||||
lock_passwd: true
|
||||
{$sudo} shell: {$shell}
|
||||
{$password}
|
||||
ssh_authorized_keys:
|
||||
- {$publicKey}
|
||||
package_update: true
|
||||
packages:
|
||||
{$packages}
|
||||
runcmd:
|
||||
- {$startDocker}{$addUserToDockerGroup}
|
||||
- |
|
||||
set -eu
|
||||
{$installDocker}{$startDocker}
|
||||
{$addUserToDockerGroup}
|
||||
attempt=0
|
||||
until docker info >/dev/null 2>&1; do
|
||||
attempt=\$((attempt + 1))
|
||||
[ "\$attempt" -lt 60 ] || exit 1
|
||||
sleep 2
|
||||
done
|
||||
docker version
|
||||
docker info
|
||||
docker compose version
|
||||
docker buildx version
|
||||
touch /etc/cloud/cloud-init.disabled
|
||||
poweroff
|
||||
YAML;
|
||||
}
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ class ApplicationSeeder extends Seeder
|
||||
'name' => 'Docker Compose Example',
|
||||
'repository_project_id' => 603035348,
|
||||
'git_repository' => 'coollabsio/coolify-examples',
|
||||
'git_branch' => 'v4.x',
|
||||
'git_branch' => 'main',
|
||||
'base_directory' => '/docker-compose',
|
||||
'docker_compose_location' => '/docker-compose-test.yaml',
|
||||
'build_pack' => 'dockercompose',
|
||||
@@ -37,7 +37,7 @@ class ApplicationSeeder extends Seeder
|
||||
'fqdn' => 'http://nodejs.127.0.0.1.sslip.io',
|
||||
'repository_project_id' => 603035348,
|
||||
'git_repository' => 'coollabsio/coolify-examples',
|
||||
'git_branch' => 'v4.x',
|
||||
'git_branch' => 'main',
|
||||
'base_directory' => '/nodejs',
|
||||
'build_pack' => 'nixpacks',
|
||||
'ports_exposes' => '3000',
|
||||
@@ -53,7 +53,7 @@ class ApplicationSeeder extends Seeder
|
||||
'fqdn' => 'http://dockerfile.127.0.0.1.sslip.io',
|
||||
'repository_project_id' => 603035348,
|
||||
'git_repository' => 'coollabsio/coolify-examples',
|
||||
'git_branch' => 'v4.x',
|
||||
'git_branch' => 'main',
|
||||
'base_directory' => '/dockerfile',
|
||||
'build_pack' => 'dockerfile',
|
||||
'ports_exposes' => '80',
|
||||
@@ -68,7 +68,7 @@ class ApplicationSeeder extends Seeder
|
||||
'name' => 'Pure Dockerfile Example',
|
||||
'fqdn' => 'http://pure-dockerfile.127.0.0.1.sslip.io',
|
||||
'git_repository' => 'coollabsio/coolify',
|
||||
'git_branch' => 'v4.x',
|
||||
'git_branch' => 'main',
|
||||
'git_commit_sha' => 'HEAD',
|
||||
'build_pack' => 'dockerfile',
|
||||
'ports_exposes' => '80',
|
||||
@@ -86,7 +86,7 @@ CMD ["nginx", "-g", "daemon off;"]
|
||||
'uuid' => 'crashloop',
|
||||
'name' => 'Crash Loop Example',
|
||||
'git_repository' => 'coollabsio/coolify',
|
||||
'git_branch' => 'v4.x',
|
||||
'git_branch' => 'main',
|
||||
'git_commit_sha' => 'HEAD',
|
||||
'build_pack' => 'dockerfile',
|
||||
'ports_exposes' => '80',
|
||||
|
||||
@@ -285,13 +285,13 @@ class DevelopmentRailpackExamplesSeeder extends Seeder
|
||||
'is_static' => true,
|
||||
'is_spa' => true,
|
||||
],
|
||||
// Multi-language examples (only available on v4.x branch).
|
||||
// Multi-language examples on the main branch.
|
||||
[
|
||||
'uuid' => 'railpack-python-flask',
|
||||
'name' => 'Railpack Python Flask Example',
|
||||
'base_directory' => '/flask',
|
||||
'ports_exposes' => '5000',
|
||||
'git_branch' => 'v4.x',
|
||||
'git_branch' => 'main',
|
||||
'start_command' => 'flask run --host=0.0.0.0 --port=5000',
|
||||
],
|
||||
[
|
||||
@@ -299,63 +299,63 @@ class DevelopmentRailpackExamplesSeeder extends Seeder
|
||||
'name' => 'Railpack Go Gin Example',
|
||||
'base_directory' => '/go/gin',
|
||||
'ports_exposes' => '3000',
|
||||
'git_branch' => 'v4.x',
|
||||
'git_branch' => 'main',
|
||||
],
|
||||
[
|
||||
'uuid' => 'railpack-rust',
|
||||
'name' => 'Railpack Rust Example',
|
||||
'base_directory' => '/rust',
|
||||
'ports_exposes' => '8000',
|
||||
'git_branch' => 'v4.x',
|
||||
'git_branch' => 'main',
|
||||
],
|
||||
[
|
||||
'uuid' => 'railpack-laravel',
|
||||
'name' => 'Railpack Laravel Example',
|
||||
'base_directory' => '/laravel',
|
||||
'ports_exposes' => '80',
|
||||
'git_branch' => 'v4.x',
|
||||
'git_branch' => 'main',
|
||||
],
|
||||
[
|
||||
'uuid' => 'railpack-laravel-pure',
|
||||
'name' => 'Railpack Laravel Pure Example',
|
||||
'base_directory' => '/laravel-pure',
|
||||
'ports_exposes' => '80',
|
||||
'git_branch' => 'v4.x',
|
||||
'git_branch' => 'main',
|
||||
],
|
||||
[
|
||||
'uuid' => 'railpack-laravel-inertia',
|
||||
'name' => 'Railpack Laravel Inertia Example',
|
||||
'base_directory' => '/laravel-inertia',
|
||||
'ports_exposes' => '80',
|
||||
'git_branch' => 'v4.x',
|
||||
'git_branch' => 'main',
|
||||
],
|
||||
[
|
||||
'uuid' => 'railpack-symfony',
|
||||
'name' => 'Railpack Symfony Example',
|
||||
'base_directory' => '/symfony',
|
||||
'ports_exposes' => '80',
|
||||
'git_branch' => 'v4.x',
|
||||
'git_branch' => 'main',
|
||||
],
|
||||
[
|
||||
'uuid' => 'railpack-rails',
|
||||
'name' => 'Railpack Ruby on Rails Example',
|
||||
'base_directory' => '/rails-example',
|
||||
'ports_exposes' => '3000',
|
||||
'git_branch' => 'v4.x',
|
||||
'git_branch' => 'main',
|
||||
],
|
||||
[
|
||||
'uuid' => 'railpack-elixir-phoenix',
|
||||
'name' => 'Railpack Elixir Phoenix Example',
|
||||
'base_directory' => '/elixir-phoenix',
|
||||
'ports_exposes' => '4000',
|
||||
'git_branch' => 'v4.x',
|
||||
'git_branch' => 'main',
|
||||
],
|
||||
[
|
||||
'uuid' => 'railpack-bun',
|
||||
'name' => 'Railpack Bun Example',
|
||||
'base_directory' => '/bun',
|
||||
'ports_exposes' => '3000',
|
||||
'git_branch' => 'v4.x',
|
||||
'git_branch' => 'main',
|
||||
],
|
||||
[
|
||||
'uuid' => 'railpack-github-deploy-key',
|
||||
|
||||
@@ -64,7 +64,7 @@
|
||||
"category": "productivity",
|
||||
"logo": "svgs/alexandrie.svg",
|
||||
"minversion": "0.0.0",
|
||||
"template_last_updated_at": "2026-04-05T13:36:24+02:00",
|
||||
"template_last_updated_at": "2026-07-07T13:24:35+02:00",
|
||||
"port": "8200"
|
||||
},
|
||||
"anythingllm": {
|
||||
@@ -1370,7 +1370,7 @@
|
||||
"category": "productivity",
|
||||
"logo": "svgs/espocrm.svg",
|
||||
"minversion": "0.0.0",
|
||||
"template_last_updated_at": "2026-04-06T11:35:16-05:00",
|
||||
"template_last_updated_at": "2026-07-03T15:15:43+03:00",
|
||||
"port": "80"
|
||||
},
|
||||
"evolution-api": {
|
||||
|
||||
@@ -36,4 +36,8 @@ it('seeds the default applications without railpack examples', function () {
|
||||
|
||||
expect(Application::query()->where('build_pack', 'railpack')->exists())->toBeFalse();
|
||||
expect(Application::query()->whereIn('uuid', ['railpack-nodejs', 'railpack-static'])->exists())->toBeFalse();
|
||||
|
||||
expect(Application::query()
|
||||
->whereIn('git_repository', ['coollabsio/coolify-examples', 'coollabsio/coolify'])
|
||||
->pluck('git_branch')->unique()->all())->toBe(['main']);
|
||||
});
|
||||
|
||||
@@ -20,6 +20,7 @@ use Illuminate\Support\Facades\DB;
|
||||
use Illuminate\Support\Facades\File;
|
||||
use Illuminate\Support\Facades\Process;
|
||||
use Illuminate\Support\Facades\Validator;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
@@ -103,6 +104,7 @@ it('automatically configures the qemu host', function () {
|
||||
|
||||
ConfigureDevelopmentQemuHost::run();
|
||||
|
||||
Process::assertRan(fn ($process) => str_contains($process->command, 'command -v') && str_contains($process->command, 'xorriso'));
|
||||
Process::assertRan(fn ($process) => str_contains($process->command, 'systemctl enable --now libvirtd'));
|
||||
Process::assertRan(fn ($process) => str_contains($process->command, 'virsh net-define'));
|
||||
Process::assertRan(fn ($process) => str_contains($process->command, 'virsh net-start'));
|
||||
@@ -255,6 +257,74 @@ it('can create a vm without a host database connection', function () {
|
||||
expect(DB::getQueryLog())->toBeEmpty();
|
||||
Process::assertRan(fn ($process) => str_contains($process->command, 'virt-install'));
|
||||
Process::assertRan(fn ($process) => str_contains($process->command, 'iptables -I LIBVIRT_FWI'));
|
||||
Process::assertRan(fn ($process) => str_contains($process->command, 'virsh domstate') && str_contains($process->command, 'shut off'));
|
||||
Process::assertRan(fn ($process) => str_contains($process->command, 'mv ') && str_contains($process->command, 'prepared.qcow2'));
|
||||
Process::assertRan(fn ($process) => str_contains($process->command, 'qemu-img create') && str_contains($process->command, 'prepared.qcow2'));
|
||||
Process::assertRan(fn ($process) => str_contains($process->command, 'xorriso -as mkisofs -V cidata -graft-points'));
|
||||
Process::assertRan(fn ($process) => str_contains($process->command, 'virt-install') && str_contains($process->command, 'bus=virtio,readonly=on'));
|
||||
$userData = File::get(config('development-qemu.storage_path').'/coolify-dev-ubuntu-root-user-data.yaml');
|
||||
expect($userData)->toContain('docker compose version', 'docker buildx version', 'docker info', 'cloud-init.disabled');
|
||||
$cloudInit = Yaml::parse($userData);
|
||||
expect($cloudInit['runcmd'][0])->toContain('https://get.docker.com', 'systemctl enable --now docker', 'poweroff');
|
||||
});
|
||||
|
||||
it('reuses a prepared vm image without reinstalling docker', function () {
|
||||
$storagePath = sys_get_temp_dir().'/coolify-qemu-prepared-test-'.uniqid();
|
||||
config(['development-qemu.storage_path' => $storagePath]);
|
||||
File::ensureDirectoryExists($storagePath);
|
||||
File::put("{$storagePath}/coolify-dev-ubuntu-root-prepared.qcow2", 'prepared image');
|
||||
Process::fake([
|
||||
'* net-dumpxml *' => Process::result(output: '<network></network>'),
|
||||
'* network inspect *' => Process::result(output: "172.18.0.0/16\n"),
|
||||
'*' => Process::result(),
|
||||
]);
|
||||
|
||||
StartDevelopmentQemuVm::run('ubuntu-root');
|
||||
|
||||
Process::assertNotRan(fn ($process) => str_contains($process->command, 'curl --fail --location'));
|
||||
Process::assertNotRan(fn ($process) => str_contains($process->command, 'virsh domstate'));
|
||||
Process::assertRan(fn ($process) => str_contains($process->command, 'qemu-img create') && str_contains($process->command, 'coolify-dev-ubuntu-root-prepared.qcow2'));
|
||||
Process::assertRan(fn ($process) => str_contains($process->command, 'virt-install') && ! str_contains($process->command, '--cloud-init'));
|
||||
expect(File::exists("{$storagePath}/coolify-dev-ubuntu-root-prepared.qcow2"))->toBeTrue();
|
||||
});
|
||||
|
||||
it('prepares alpine with the compose and buildx packages for a non-root user', function () {
|
||||
$storagePath = sys_get_temp_dir().'/coolify-qemu-alpine-test-'.uniqid();
|
||||
config(['development-qemu.storage_path' => $storagePath]);
|
||||
Process::fake([
|
||||
'* net-dumpxml *' => Process::result(output: '<network></network>'),
|
||||
'* network inspect *' => Process::result(output: "172.18.0.0/16\n"),
|
||||
'*' => Process::result(),
|
||||
]);
|
||||
|
||||
StartDevelopmentQemuVm::run('alpine-non-root', false);
|
||||
|
||||
$cloudInit = Yaml::parse(File::get("{$storagePath}/coolify-dev-alpine-non-root-user-data.yaml"));
|
||||
expect($cloudInit['packages'])->toContain('docker', 'docker-cli-compose', 'docker-cli-buildx')
|
||||
->and($cloudInit['users'][0]['shell'])->toBe('/bin/ash')
|
||||
->and($cloudInit['users'][0]['lock_passwd'])->toBeFalse()
|
||||
->and($cloudInit['runcmd'][0])->toContain('service cgroups start', 'addgroup coolify docker', 'until docker info', 'docker compose version', 'docker buildx version');
|
||||
});
|
||||
|
||||
it('does not cache a vm when preparation does not finish', function () {
|
||||
config(['development-qemu.storage_path' => sys_get_temp_dir().'/coolify-qemu-failed-preparation-'.uniqid()]);
|
||||
Process::fake(function ($process) {
|
||||
if (str_contains($process->command, 'net-dumpxml')) {
|
||||
return Process::result(output: '<network></network>');
|
||||
}
|
||||
|
||||
if (str_contains($process->command, 'network inspect')) {
|
||||
return Process::result(output: "172.18.0.0/16\n");
|
||||
}
|
||||
|
||||
return str_contains($process->command, 'timeout 900 bash')
|
||||
? Process::result(exitCode: 124)
|
||||
: Process::result();
|
||||
});
|
||||
|
||||
expect(fn () => StartDevelopmentQemuVm::run('ubuntu-root'))->toThrow(RuntimeException::class);
|
||||
Process::assertNotRan(fn ($process) => str_contains($process->command, 'mv ') && str_contains($process->command, 'prepared.qcow2'));
|
||||
Process::assertNotRan(fn ($process) => str_starts_with($process->command, 'virt-install ') && ! str_contains($process->command, 'readonly=on'));
|
||||
});
|
||||
|
||||
it('seeds through the coolify container when the host database is unavailable', function () {
|
||||
|
||||
@@ -77,6 +77,11 @@ it('seeds every railpack example in the production environment on testing-host',
|
||||
$githubDeployKey = $applications->firstWhere('uuid', 'railpack-github-deploy-key');
|
||||
$gitlabDeployKey = $applications->firstWhere('uuid', 'railpack-gitlab-deploy-key');
|
||||
|
||||
expect($applications->where('git_repository', DevelopmentRailpackExamplesSeeder::GIT_REPOSITORY)
|
||||
->pluck('git_branch')->unique()->sort()->values()->all())->toBe(['main', 'next'])
|
||||
->and($applications->firstWhere('uuid', 'railpack-python-flask')->git_branch)->toBe('main')
|
||||
->and($nestjs->git_branch)->toBe('next');
|
||||
|
||||
expect($nestjs->base_directory)->toBe('/node/nestjs')
|
||||
->and($nestjs->build_command)->toBe('npm run build')
|
||||
->and($nestjs->start_command)->toBe('npm run start:prod')
|
||||
|
||||
Reference in New Issue
Block a user