diff --git a/app/Livewire/Project/Shared/Logs.php b/app/Livewire/Project/Shared/Logs.php index 2b7c21836a..553b0485ef 100644 --- a/app/Livewire/Project/Shared/Logs.php +++ b/app/Livewire/Project/Shared/Logs.php @@ -28,6 +28,9 @@ class Logs extends Component public array $serverContainers = []; + /** @var array */ + 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 []; } diff --git a/resources/views/livewire/project/shared/logs.blade.php b/resources/views/livewire/project/shared/logs.blade.php index 0429dcafd3..1a11a80a34 100644 --- a/resources/views/livewire/project/shared/logs.blade.php +++ b/resources/views/livewire/project/shared/logs.blade.php @@ -56,6 +56,10 @@ + @elseif (count($serverErrors) > 0) + @else {{ $server->name }} is not functional, so its container logs cannot be loaded. + @elseif (isset($serverErrors[$server->id])) + + {{ $server->name }}: {{ $serverErrors[$server->id] }} + @elseif (isset($serverContainers[$server->id]) && count($serverContainers[$server->id]) > 0) @if ($servers->count() > 1)
diff --git a/tests/Feature/RuntimeLogsContainerRefreshTest.php b/tests/Feature/RuntimeLogsContainerRefreshTest.php new file mode 100644 index 0000000000..2219c602e9 --- /dev/null +++ b/tests/Feature/RuntimeLogsContainerRefreshTest.php @@ -0,0 +1,86 @@ +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'); +});