mirror of
https://github.com/tiennm99/coolify.git
synced 2026-10-15 18:15:49 +00:00
Merge remote-tracking branch 'origin/next' into 8826-investigate-postgresql-restart
This commit is contained in:
commit
0a1782175a
18 files changed
+732
-200
No files matched your search
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
it('copies the realtime terminal utilities into the container image', function () {
|
||||
$dockerfile = file_get_contents(base_path('docker/coolify-realtime/Dockerfile'));
|
||||
|
||||
expect($dockerfile)->toContain('COPY docker/coolify-realtime/terminal-utils.js /terminal/terminal-utils.js');
|
||||
});
|
||||
|
||||
it('mounts the realtime terminal utilities in local development compose files', function (string $composeFile) {
|
||||
$composeContents = file_get_contents(base_path($composeFile));
|
||||
|
||||
expect($composeContents)->toContain('./docker/coolify-realtime/terminal-utils.js:/terminal/terminal-utils.js');
|
||||
})->with([
|
||||
'default dev compose' => 'docker-compose.dev.yml',
|
||||
'maxio dev compose' => 'docker-compose-maxio.dev.yml',
|
||||
]);
|
||||
|
||||
it('keeps terminal browser logging restricted to Vite development mode', function () {
|
||||
$terminalClient = file_get_contents(base_path('resources/js/terminal.js'));
|
||||
|
||||
expect($terminalClient)
|
||||
->toContain('const terminalDebugEnabled = import.meta.env.DEV;')
|
||||
->toContain("logTerminal('log', '[Terminal] WebSocket connection established.');")
|
||||
->not->toContain("console.log('[Terminal] WebSocket connection established. Cool cool cool cool cool cool.');");
|
||||
});
|
||||
|
||||
it('keeps realtime terminal server logging restricted to development environments', function () {
|
||||
$terminalServer = file_get_contents(base_path('docker/coolify-realtime/terminal-server.js'));
|
||||
|
||||
expect($terminalServer)
|
||||
->toContain("const terminalDebugEnabled = ['local', 'development'].includes(")
|
||||
->toContain('if (!terminalDebugEnabled) {')
|
||||
->not->toContain("console.log('Coolify realtime terminal server listening on port 6002. Let the hacking begin!');");
|
||||
});
|
||||
@@ -0,0 +1,51 @@
|
||||
<?php
|
||||
|
||||
use App\Models\PrivateKey;
|
||||
use App\Models\Server;
|
||||
use App\Models\Team;
|
||||
use App\Models\User;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
beforeEach(function () {
|
||||
config()->set('app.env', 'local');
|
||||
|
||||
$this->user = User::factory()->create();
|
||||
$this->team = Team::factory()->create();
|
||||
$this->user->teams()->attach($this->team, ['role' => 'owner']);
|
||||
$this->actingAs($this->user);
|
||||
session(['currentTeam' => $this->team]);
|
||||
|
||||
$this->privateKey = PrivateKey::create([
|
||||
'name' => 'Test Key',
|
||||
'private_key' => '-----BEGIN OPENSSH PRIVATE KEY-----
|
||||
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtzc2gtZW
|
||||
QyNTUxOQAAACBbhpqHhqv6aI67Mj9abM3DVbmcfYhZAhC7ca4d9UCevAAAAJi/QySHv0Mk
|
||||
hwAAAAtzc2gtZWQyNTUxOQAAACBbhpqHhqv6aI67Mj9abM3DVbmcfYhZAhC7ca4d9UCevA
|
||||
AAAECBQw4jg1WRT2IGHMncCiZhURCts2s24HoDS0thHnnRKVuGmoeGq/pojrsyP1pszcNV
|
||||
uZx9iFkCELtxrh31QJ68AAAAEXNhaWxANzZmZjY2ZDJlMmRkAQIDBA==
|
||||
-----END OPENSSH PRIVATE KEY-----',
|
||||
'team_id' => $this->team->id,
|
||||
]);
|
||||
});
|
||||
|
||||
it('includes development terminal host aliases for authenticated users', function () {
|
||||
Server::factory()->create([
|
||||
'name' => 'Localhost',
|
||||
'ip' => 'coolify-testing-host',
|
||||
'team_id' => $this->team->id,
|
||||
'private_key_id' => $this->privateKey->id,
|
||||
]);
|
||||
|
||||
$response = $this->postJson('/terminal/auth/ips');
|
||||
|
||||
$response->assertSuccessful();
|
||||
$response->assertJsonPath('ipAddresses.0', 'coolify-testing-host');
|
||||
|
||||
expect($response->json('ipAddresses'))
|
||||
->toContain('coolify-testing-host')
|
||||
->toContain('localhost')
|
||||
->toContain('127.0.0.1')
|
||||
->toContain('host.docker.internal');
|
||||
});
|
||||
@@ -8,9 +8,7 @@ afterEach(function () {
|
||||
Mockery::close();
|
||||
});
|
||||
|
||||
it('categorizes images correctly into PR and regular images', function () {
|
||||
// Test the image categorization logic
|
||||
// Build images (*-build) are excluded from retention and handled by docker image prune
|
||||
it('categorizes images correctly into PR, build, and regular images', function () {
|
||||
$images = collect([
|
||||
['repository' => 'app-uuid', 'tag' => 'abc123', 'created_at' => '2024-01-01', 'image_ref' => 'app-uuid:abc123'],
|
||||
['repository' => 'app-uuid', 'tag' => 'def456', 'created_at' => '2024-01-02', 'image_ref' => 'app-uuid:def456'],
|
||||
@@ -25,6 +23,11 @@ it('categorizes images correctly into PR and regular images', function () {
|
||||
expect($prImages)->toHaveCount(2);
|
||||
expect($prImages->pluck('tag')->toArray())->toContain('pr-123', 'pr-456');
|
||||
|
||||
// Build images (tags ending with '-build', excluding PR builds)
|
||||
$buildImages = $images->filter(fn ($image) => ! str_starts_with($image['tag'], 'pr-') && str_ends_with($image['tag'], '-build'));
|
||||
expect($buildImages)->toHaveCount(2);
|
||||
expect($buildImages->pluck('tag')->toArray())->toContain('abc123-build', 'def456-build');
|
||||
|
||||
// Regular images (neither PR nor build) - these are subject to retention policy
|
||||
$regularImages = $images->filter(fn ($image) => ! str_starts_with($image['tag'], 'pr-') && ! str_ends_with($image['tag'], '-build'));
|
||||
expect($regularImages)->toHaveCount(2);
|
||||
@@ -340,3 +343,128 @@ it('protects current infrastructure images from any registry even when no applic
|
||||
// Other images should not be protected
|
||||
expect(preg_match($pattern, 'nginx:alpine'))->toBe(0);
|
||||
});
|
||||
|
||||
it('deletes build images not matching retained regular images', function () {
|
||||
// Simulates the Nixpacks scenario from issue #8765:
|
||||
// Many -build images accumulate because they were excluded from both cleanup paths
|
||||
$images = collect([
|
||||
['repository' => 'app-uuid', 'tag' => 'commit1', 'created_at' => '2024-01-01 10:00:00', 'image_ref' => 'app-uuid:commit1'],
|
||||
['repository' => 'app-uuid', 'tag' => 'commit2', 'created_at' => '2024-01-02 10:00:00', 'image_ref' => 'app-uuid:commit2'],
|
||||
['repository' => 'app-uuid', 'tag' => 'commit3', 'created_at' => '2024-01-03 10:00:00', 'image_ref' => 'app-uuid:commit3'],
|
||||
['repository' => 'app-uuid', 'tag' => 'commit4', 'created_at' => '2024-01-04 10:00:00', 'image_ref' => 'app-uuid:commit4'],
|
||||
['repository' => 'app-uuid', 'tag' => 'commit5', 'created_at' => '2024-01-05 10:00:00', 'image_ref' => 'app-uuid:commit5'],
|
||||
['repository' => 'app-uuid', 'tag' => 'commit1-build', 'created_at' => '2024-01-01 09:00:00', 'image_ref' => 'app-uuid:commit1-build'],
|
||||
['repository' => 'app-uuid', 'tag' => 'commit2-build', 'created_at' => '2024-01-02 09:00:00', 'image_ref' => 'app-uuid:commit2-build'],
|
||||
['repository' => 'app-uuid', 'tag' => 'commit3-build', 'created_at' => '2024-01-03 09:00:00', 'image_ref' => 'app-uuid:commit3-build'],
|
||||
['repository' => 'app-uuid', 'tag' => 'commit4-build', 'created_at' => '2024-01-04 09:00:00', 'image_ref' => 'app-uuid:commit4-build'],
|
||||
['repository' => 'app-uuid', 'tag' => 'commit5-build', 'created_at' => '2024-01-05 09:00:00', 'image_ref' => 'app-uuid:commit5-build'],
|
||||
]);
|
||||
|
||||
$currentTag = 'commit5';
|
||||
$imagesToKeep = 2;
|
||||
|
||||
$buildImages = $images->filter(fn ($image) => ! str_starts_with($image['tag'], 'pr-') && str_ends_with($image['tag'], '-build'));
|
||||
$regularImages = $images->filter(fn ($image) => ! str_starts_with($image['tag'], 'pr-') && ! str_ends_with($image['tag'], '-build'));
|
||||
|
||||
$sortedRegularImages = $regularImages
|
||||
->filter(fn ($image) => $image['tag'] !== $currentTag)
|
||||
->sortByDesc('created_at')
|
||||
->values();
|
||||
|
||||
// Determine kept tags: current + N newest rollback
|
||||
$keptTags = $sortedRegularImages->take($imagesToKeep)->pluck('tag');
|
||||
if (! empty($currentTag)) {
|
||||
$keptTags = $keptTags->push($currentTag);
|
||||
}
|
||||
|
||||
// Kept tags should be: commit5 (running), commit4, commit3 (2 newest rollback)
|
||||
expect($keptTags->toArray())->toContain('commit5', 'commit4', 'commit3');
|
||||
|
||||
// Build images to delete: those whose base tag is NOT in keptTags
|
||||
$buildImagesToDelete = $buildImages->filter(function ($image) use ($keptTags) {
|
||||
$baseTag = preg_replace('/-build$/', '', $image['tag']);
|
||||
|
||||
return ! $keptTags->contains($baseTag);
|
||||
});
|
||||
|
||||
// Should delete commit1-build and commit2-build (their base tags are not kept)
|
||||
expect($buildImagesToDelete)->toHaveCount(2);
|
||||
expect($buildImagesToDelete->pluck('tag')->toArray())->toContain('commit1-build', 'commit2-build');
|
||||
|
||||
// Should keep commit3-build, commit4-build, commit5-build (matching retained images)
|
||||
$buildImagesToKeep = $buildImages->filter(function ($image) use ($keptTags) {
|
||||
$baseTag = preg_replace('/-build$/', '', $image['tag']);
|
||||
|
||||
return $keptTags->contains($baseTag);
|
||||
});
|
||||
expect($buildImagesToKeep)->toHaveCount(3);
|
||||
expect($buildImagesToKeep->pluck('tag')->toArray())->toContain('commit5-build', 'commit4-build', 'commit3-build');
|
||||
});
|
||||
|
||||
it('deletes all build images when retention is disabled', function () {
|
||||
$images = collect([
|
||||
['repository' => 'app-uuid', 'tag' => 'commit1', 'created_at' => '2024-01-01 10:00:00', 'image_ref' => 'app-uuid:commit1'],
|
||||
['repository' => 'app-uuid', 'tag' => 'commit2', 'created_at' => '2024-01-02 10:00:00', 'image_ref' => 'app-uuid:commit2'],
|
||||
['repository' => 'app-uuid', 'tag' => 'commit1-build', 'created_at' => '2024-01-01 09:00:00', 'image_ref' => 'app-uuid:commit1-build'],
|
||||
['repository' => 'app-uuid', 'tag' => 'commit2-build', 'created_at' => '2024-01-02 09:00:00', 'image_ref' => 'app-uuid:commit2-build'],
|
||||
]);
|
||||
|
||||
$currentTag = 'commit2';
|
||||
$imagesToKeep = 0; // Retention disabled
|
||||
|
||||
$buildImages = $images->filter(fn ($image) => ! str_starts_with($image['tag'], 'pr-') && str_ends_with($image['tag'], '-build'));
|
||||
$regularImages = $images->filter(fn ($image) => ! str_starts_with($image['tag'], 'pr-') && ! str_ends_with($image['tag'], '-build'));
|
||||
|
||||
$sortedRegularImages = $regularImages
|
||||
->filter(fn ($image) => $image['tag'] !== $currentTag)
|
||||
->sortByDesc('created_at')
|
||||
->values();
|
||||
|
||||
// With imagesToKeep=0, only current tag is kept
|
||||
$keptTags = $sortedRegularImages->take($imagesToKeep)->pluck('tag');
|
||||
if (! empty($currentTag)) {
|
||||
$keptTags = $keptTags->push($currentTag);
|
||||
}
|
||||
|
||||
$buildImagesToDelete = $buildImages->filter(function ($image) use ($keptTags) {
|
||||
$baseTag = preg_replace('/-build$/', '', $image['tag']);
|
||||
|
||||
return ! $keptTags->contains($baseTag);
|
||||
});
|
||||
|
||||
// commit1-build should be deleted (not retained), commit2-build kept (matches running)
|
||||
expect($buildImagesToDelete)->toHaveCount(1);
|
||||
expect($buildImagesToDelete->pluck('tag')->toArray())->toContain('commit1-build');
|
||||
});
|
||||
|
||||
it('preserves build image for currently running tag', function () {
|
||||
$images = collect([
|
||||
['repository' => 'app-uuid', 'tag' => 'commit1', 'created_at' => '2024-01-01 10:00:00', 'image_ref' => 'app-uuid:commit1'],
|
||||
['repository' => 'app-uuid', 'tag' => 'commit1-build', 'created_at' => '2024-01-01 09:00:00', 'image_ref' => 'app-uuid:commit1-build'],
|
||||
]);
|
||||
|
||||
$currentTag = 'commit1';
|
||||
$imagesToKeep = 2;
|
||||
|
||||
$buildImages = $images->filter(fn ($image) => ! str_starts_with($image['tag'], 'pr-') && str_ends_with($image['tag'], '-build'));
|
||||
$regularImages = $images->filter(fn ($image) => ! str_starts_with($image['tag'], 'pr-') && ! str_ends_with($image['tag'], '-build'));
|
||||
|
||||
$sortedRegularImages = $regularImages
|
||||
->filter(fn ($image) => $image['tag'] !== $currentTag)
|
||||
->sortByDesc('created_at')
|
||||
->values();
|
||||
|
||||
$keptTags = $sortedRegularImages->take($imagesToKeep)->pluck('tag');
|
||||
if (! empty($currentTag)) {
|
||||
$keptTags = $keptTags->push($currentTag);
|
||||
}
|
||||
|
||||
$buildImagesToDelete = $buildImages->filter(function ($image) use ($keptTags) {
|
||||
$baseTag = preg_replace('/-build$/', '', $image['tag']);
|
||||
|
||||
return ! $keptTags->contains($baseTag);
|
||||
});
|
||||
|
||||
// Build image for running tag should NOT be deleted
|
||||
expect($buildImagesToDelete)->toHaveCount(0);
|
||||
});
|
||||
Reference in new issue
Block a user