From d87ab279fb098adc05292610af9a9683159a4647 Mon Sep 17 00:00:00 2001 From: Andras Bacsai <5845193+andrasbacsai@users.noreply.github.com> Date: Tue, 2 Jun 2026 14:33:57 +0200 Subject: [PATCH] fix(log-drain): connect drain to service networks Ensure the log drain container joins enabled service destination networks when services start or the log drain restarts. --- app/Actions/Server/StartLogDrain.php | 20 ++++ app/Actions/Service/StartService.php | 22 +++++ .../LogDrainNetworkConnectCommandTest.php | 97 +++++++++++++++++++ 3 files changed, 139 insertions(+) create mode 100644 tests/Feature/LogDrain/LogDrainNetworkConnectCommandTest.php diff --git a/app/Actions/Server/StartLogDrain.php b/app/Actions/Server/StartLogDrain.php index e4df5a061..eb419992d 100644 --- a/app/Actions/Server/StartLogDrain.php +++ b/app/Actions/Server/StartLogDrain.php @@ -3,6 +3,7 @@ namespace App\Actions\Server; use App\Models\Server; +use App\Models\Service; use Lorisleiva\Actions\Concerns\AsAction; class StartLogDrain @@ -201,10 +202,29 @@ public function handle(Server $server) "echo 'Starting Fluent Bit'", "cd $config_path && docker compose up -d", ]; + $command = array_merge($command, $this->logDrainNetworkConnectCommands($server)); return instant_remote_process($command, $server); } catch (\Throwable $e) { return handleError($e); } } + + private function logDrainNetworkConnectCommands(Server $server): array + { + if (! $server->isLogDrainEnabled()) { + return []; + } + + return $server->services() + ->with('destination') + ->where('connect_to_docker_network', true) + ->get() + ->map(fn (Service $service) => data_get($service, 'destination.network')) + ->filter() + ->unique() + ->map(fn (string $network) => 'docker network connect '.escapeshellarg($network).' coolify-log-drain >/dev/null 2>&1 || true') + ->values() + ->all(); + } } diff --git a/app/Actions/Service/StartService.php b/app/Actions/Service/StartService.php index 89817506d..463a8ad5b 100644 --- a/app/Actions/Service/StartService.php +++ b/app/Actions/Service/StartService.php @@ -50,10 +50,32 @@ public function handle(Service $service, bool $pullLatestImages = false, bool $s $commands[] = "docker network connect --alias {$serviceName}-{$service->uuid} {$safeNetwork} {$serviceName}-{$service->uuid} >/dev/null 2>&1 || true"; } } + $commands = array_merge($commands, $this->logDrainNetworkConnectCommands($service)); return remote_process($commands, $service->server, type_uuid: $service->uuid, callEventOnFinish: 'ServiceStatusChanged'); } + private function logDrainNetworkConnectCommands(Service $service): array + { + if (! data_get($service, 'connect_to_docker_network')) { + return []; + } + + if (! $service->destination?->server?->isLogDrainEnabled()) { + return []; + } + + $network = data_get($service, 'destination.network'); + + if (blank($network)) { + return []; + } + + return [ + 'docker network connect '.escapeshellarg($network).' coolify-log-drain >/dev/null 2>&1 || true', + ]; + } + private function shouldStopBeforeStarting(bool $pullLatestImages, bool $stopBeforeStart): bool { return $stopBeforeStart && ! $pullLatestImages; diff --git a/tests/Feature/LogDrain/LogDrainNetworkConnectCommandTest.php b/tests/Feature/LogDrain/LogDrainNetworkConnectCommandTest.php new file mode 100644 index 000000000..ab3144da3 --- /dev/null +++ b/tests/Feature/LogDrain/LogDrainNetworkConnectCommandTest.php @@ -0,0 +1,97 @@ +setAccessible(true); + + return $method->invoke($action, $model); +} + +function createServerWithTeam(): Server +{ + $team = Team::factory()->create(); + + return Server::factory()->create(['team_id' => $team->id]); +} + +function createServiceOnServer(Server $server, string $network, bool $connectToDockerNetwork = true): Service +{ + $team = Team::factory()->create(); + $project = Project::factory()->create(['team_id' => $team->id]); + $environment = Environment::factory()->create(['project_id' => $project->id]); + $destination = StandaloneDocker::query()->firstOrCreate( + ['server_id' => $server->id, 'network' => $network], + ['uuid' => fake()->uuid(), 'name' => fake()->unique()->word()] + ); + + return Service::factory()->create([ + 'server_id' => $server->id, + 'environment_id' => $environment->id, + 'destination_id' => $destination->id, + 'destination_type' => StandaloneDocker::class, + 'connect_to_docker_network' => $connectToDockerNetwork, + 'docker_compose' => "services:\n signoz:\n image: signoz/signoz:latest\n", + ]); +} + +it('connects the log drain container to a service preferred network when the server log drain is enabled', function () { + $server = createServerWithTeam(); + $server->settings()->update(['is_logdrain_custom_enabled' => true]); + $service = createServiceOnServer($server, 'signoz-net', true); + + $commands = reflectedLogDrainNetworkCommands(new StartService, $service->fresh(['destination.server.settings'])); + + expect($commands)->toContain("docker network connect 'signoz-net' coolify-log-drain >/dev/null 2>&1 || true"); +}); + +it('does not connect the log drain container when service preferred network is disabled', function () { + $server = createServerWithTeam(); + $server->settings()->update(['is_logdrain_custom_enabled' => true]); + $service = createServiceOnServer($server, 'signoz-net', false); + + $commands = reflectedLogDrainNetworkCommands(new StartService, $service->fresh(['destination.server.settings'])); + + expect($commands)->toBeEmpty(); +}); + +it('does not connect the log drain container when the server log drain is disabled', function () { + $server = createServerWithTeam(); + $service = createServiceOnServer($server, 'signoz-net', true); + + $commands = reflectedLogDrainNetworkCommands(new StartService, $service->fresh(['destination.server.settings'])); + + expect($commands)->toBeEmpty(); +}); + +it('connects a restarted log drain container to all enabled service preferred networks on the server', function () { + $server = createServerWithTeam(); + $server->settings()->update(['is_logdrain_custom_enabled' => true]); + createServiceOnServer($server, 'signoz-net', true); + createServiceOnServer($server, 'ignored-net', false); + createServiceOnServer($server, 'signoz-net', true); + + $otherServer = createServerWithTeam(); + createServiceOnServer($otherServer, 'other-server-net', true); + + $commands = reflectedLogDrainNetworkCommands(new StartLogDrain, $server->fresh(['settings'])); + + expect($commands) + ->toContain("docker network connect 'signoz-net' coolify-log-drain >/dev/null 2>&1 || true") + ->not->toContain("docker network connect 'ignored-net' coolify-log-drain >/dev/null 2>&1 || true") + ->not->toContain("docker network connect 'other-server-net' coolify-log-drain >/dev/null 2>&1 || true"); + + expect($commands)->toHaveCount(1); +});