fix(logs): show container lookup errors and reload containers after status checks

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Andras Bacsai
2026-09-28 11:09:13 +02:00
co-authored by Claude Opus 5.5
parent e62658467f
commit d59bcf5ae3
3 changed files with 101 additions and 2 deletions
+7 -2
View File
@@ -28,6 +28,9 @@ class Logs extends Component
public array $serverContainers = [];
/** @var array<int, string> */
public array $serverErrors = [];
public $container = [];
public $parameters;
@@ -47,7 +50,7 @@ class Logs extends Component
$teamId = auth()->user()->currentTeam()->id;
return [
"echo-private:team.{$teamId},ServiceChecked" => '$refresh',
"echo-private:team.{$teamId},ServiceChecked" => 'loadAllContainers',
];
}
@@ -55,6 +58,7 @@ class Logs extends Component
{
try {
foreach ($this->servers as $server) {
unset($this->serverErrors[$server->id]);
$this->serverContainers[$server->id] = $this->getContainersForServer($server);
}
$this->containersLoaded = true;
@@ -107,7 +111,8 @@ class Logs extends Component
return [];
} catch (\Exception $e) {
// Log error but don't fail the entire operation
// Keep the error for this server so the page does not report it as "no containers".
$this->serverErrors[$server->id] = $e->getMessage();
return [];
}
@@ -56,6 +56,10 @@
<x-empty size="lg" title="Runtime logs unavailable"
description="No functional servers are available, so container logs cannot be loaded."
icon-name="file-content" />
@elseif (count($serverErrors) > 0)
<x-empty size="lg" title="Runtime logs unavailable"
description="Could not load containers: {{ collect($serverErrors)->first() }}"
icon-name="file-content" />
@else
<x-empty size="lg" title="Runtime logs unavailable"
description="No containers are running, so there are no runtime logs to show."
@@ -68,6 +72,10 @@
<x-callout type="warning" title="Server unavailable">
{{ $server->name }} is not functional, so its container logs cannot be loaded.
</x-callout>
@elseif (isset($serverErrors[$server->id]))
<x-callout type="warning" title="Could not load containers">
{{ $server->name }}: {{ $serverErrors[$server->id] }}
</x-callout>
@elseif (isset($serverContainers[$server->id]) && count($serverContainers[$server->id]) > 0)
@if ($servers->count() > 1)
<div class="flex items-center gap-2 px-1">
@@ -0,0 +1,86 @@
<?php
use App\Livewire\Project\Shared\Logs;
use App\Models\Application;
use App\Models\Environment;
use App\Models\Project;
use App\Models\Server;
use App\Models\Team;
use App\Models\User;
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Storage;
uses(RefreshDatabase::class);
function runtimeLogsApplication(): array
{
$user = User::factory()->create();
$team = Team::factory()->create();
$user->teams()->attach($team, ['role' => 'owner']);
test()->actingAs($user);
session(['currentTeam' => $team]);
$server = Server::factory()->create(['team_id' => $team->id]);
$server->settings->update(['is_reachable' => true, 'is_usable' => true]);
$destination = $server->standaloneDockers()->firstOrFail();
$environment = Environment::factory()->create([
'project_id' => Project::factory()->create(['team_id' => $team->id])->id,
]);
$application = Application::factory()->create([
'environment_id' => $environment->id,
'destination_id' => $destination->id,
'destination_type' => $destination->getMorphClass(),
]);
return [$team, $server, $application];
}
it('reloads containers after a status check when the first load found none', function () {
[$team, $server, $application] = runtimeLogsApplication();
$server->settings->update(['is_swarm_manager' => true]);
$component = new Logs;
$component->resource = $application;
$component->servers = collect([$server]);
// The first load happened while no container was running (for example during a redeploy).
$component->serverContainers = [$server->id => []];
$component->containersLoaded = true;
$listener = $component->getListeners()["echo-private:team.{$team->id},ServiceChecked"];
expect($listener)->toBe('loadAllContainers');
$component->{$listener}();
expect($component->serverContainers[$server->id])->toHaveCount(1)
->and($component->serverContainers[$server->id][0]['Names'])->toBe($application->uuid.'_'.$application->uuid);
});
it('keeps the container lookup error instead of reporting no running containers', function () {
Storage::fake('ssh-keys');
Storage::fake('ssh-mux');
[, $server, $application] = runtimeLogsApplication();
// A server without its private key fails the SSH lookup before any command runs.
$server->updateQuietly(['private_key_id' => 999999]);
$component = new Logs;
$component->resource = $application;
$component->servers = collect([$server]);
$component->loadAllContainers();
expect($component->serverContainers[$server->id])->toBe([])
->and($component->serverErrors[$server->id])->toContain('No query results for model [App\\Models\\PrivateKey]');
$html = view('livewire.project.shared.logs', [
'type' => null,
'resource' => $application,
'servers' => $component->servers,
'serverContainers' => $component->serverContainers,
'serverErrors' => $component->serverErrors,
'containersLoaded' => true,
])->render();
expect($html)->toContain('Could not load containers')
->and($html)->toContain('No query results for model [App\\Models\\PrivateKey]')
->and($html)->not->toContain('No containers are running');
});