mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-06 13:10:37 -04:00
fix(proxy): connect coolify-proxy to destination network before compose up
Add StandaloneDocker::proxyConnectCommand() and run it inline when a destination is created, replacing the synchronous ConnectProxyToNetworksJob dispatch. Deployments also run it before docker compose up, so the proxy is attached to the destination network even when it was missing.
This commit is contained in:
@@ -4699,6 +4699,7 @@ COPY ./nginx.conf /etc/nginx/conf.d/default.conf");
|
||||
// Ensure .env file exists before docker compose tries to load it (defensive programming)
|
||||
$this->execute_remote_command(
|
||||
["touch {$this->configuration_dir}/.env", 'hidden' => true],
|
||||
[$this->destination->proxyConnectCommand(), 'hidden' => true, 'ignore_errors' => true],
|
||||
);
|
||||
|
||||
if ($this->application->build_pack === 'dockerimage') {
|
||||
|
||||
@@ -1,55 +0,0 @@
|
||||
<?php
|
||||
|
||||
namespace App\Jobs;
|
||||
|
||||
use App\Models\Server;
|
||||
use Illuminate\Bus\Queueable;
|
||||
use Illuminate\Contracts\Queue\ShouldBeEncrypted;
|
||||
use Illuminate\Contracts\Queue\ShouldQueue;
|
||||
use Illuminate\Foundation\Bus\Dispatchable;
|
||||
use Illuminate\Queue\InteractsWithQueue;
|
||||
use Illuminate\Queue\Middleware\WithoutOverlapping;
|
||||
use Illuminate\Queue\SerializesModels;
|
||||
use Laravel\Horizon\Contracts\Silenced;
|
||||
|
||||
/**
|
||||
* Asynchronously connects the coolify-proxy to Docker networks.
|
||||
*
|
||||
* This job is dispatched from PushServerUpdateJob when the proxy is found running
|
||||
* to ensure it's connected to all required networks without blocking the status update.
|
||||
*/
|
||||
class ConnectProxyToNetworksJob implements ShouldBeEncrypted, ShouldQueue, Silenced
|
||||
{
|
||||
use Dispatchable, InteractsWithQueue, Queueable, SerializesModels;
|
||||
|
||||
public $tries = 1;
|
||||
|
||||
public $timeout = 60;
|
||||
|
||||
public function middleware(): array
|
||||
{
|
||||
// Prevent overlapping executions for the same server and throttle to max once per 10 seconds
|
||||
return [
|
||||
(new WithoutOverlapping('connect-proxy-networks-'.$this->server->uuid))
|
||||
->expireAfter(60)
|
||||
->dontRelease(),
|
||||
];
|
||||
}
|
||||
|
||||
public function __construct(public Server $server) {}
|
||||
|
||||
public function handle()
|
||||
{
|
||||
if (! $this->server->isFunctional()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$connectProxyToDockerNetworks = connectProxyToNetworks($this->server);
|
||||
|
||||
if (empty($connectProxyToDockerNetworks)) {
|
||||
return;
|
||||
}
|
||||
|
||||
instant_remote_process($connectProxyToDockerNetworks, $this->server, false);
|
||||
}
|
||||
}
|
||||
@@ -2,7 +2,6 @@
|
||||
|
||||
namespace App\Models;
|
||||
|
||||
use App\Jobs\ConnectProxyToNetworksJob;
|
||||
use App\Support\ValidationPatterns;
|
||||
use App\Traits\HasSafeStringAttribute;
|
||||
use Illuminate\Database\Eloquent\Collection;
|
||||
@@ -45,8 +44,8 @@ class StandaloneDocker extends BaseModel
|
||||
$server = $newStandaloneDocker->server;
|
||||
instant_remote_process([
|
||||
$newStandaloneDocker->networkCreateCommand(),
|
||||
$newStandaloneDocker->proxyConnectCommand(),
|
||||
], $server, false);
|
||||
ConnectProxyToNetworksJob::dispatchSync($server);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -57,6 +56,13 @@ class StandaloneDocker extends BaseModel
|
||||
return "docker network inspect {$safeNetwork} >/dev/null 2>&1 || docker network create --attachable {$safeNetwork} >/dev/null";
|
||||
}
|
||||
|
||||
public function proxyConnectCommand(): string
|
||||
{
|
||||
$safeNetwork = escapeshellarg($this->network);
|
||||
|
||||
return "docker network connect {$safeNetwork} coolify-proxy >/dev/null 2>&1 || true";
|
||||
}
|
||||
|
||||
public function setNetworkAttribute(string $value): void
|
||||
{
|
||||
if (! ValidationPatterns::isValidDockerNetwork($value)) {
|
||||
|
||||
@@ -381,6 +381,7 @@ test('a Dockerfile deployment to an additional server writes missing content fil
|
||||
'application_deployment_queue' => $queue,
|
||||
'server' => $otherServer,
|
||||
'mainServer' => $otherServer,
|
||||
'destination' => $this->destination,
|
||||
'deployment_uuid' => 'deployment-uuid',
|
||||
'workdir' => '/artifacts/deployment-uuid',
|
||||
'configuration_dir' => $this->application->workdir(),
|
||||
@@ -403,6 +404,10 @@ test('a Dockerfile deployment to an additional server writes missing content fil
|
||||
->and($write)->toBeLessThan($up)
|
||||
->and(ContentFilesUpRecorder::ssh()[$write])->toContain("@'{$otherServer->ip}'")
|
||||
->and($this->logEntries)->toContain(['Writing 1 missing configuration file.', 'stdout']);
|
||||
|
||||
$connect = ContentFilesUpRecorder::indexOf('deployment', "docker network connect '{$this->destination->network}' coolify-proxy");
|
||||
expect($connect)->not->toBeNull()
|
||||
->and($connect)->toBeLessThan($up);
|
||||
});
|
||||
|
||||
test('the content file check and write are safe for non-root servers', function () {
|
||||
|
||||
@@ -35,6 +35,14 @@ it('creates standalone destinations with a bridge network', function () {
|
||||
->toBe("docker network inspect 'test-network' >/dev/null 2>&1 || docker network create --attachable 'test-network' >/dev/null");
|
||||
});
|
||||
|
||||
it('connects the proxy to a new standalone destination network', function () {
|
||||
$model = new StandaloneDocker;
|
||||
$model->network = 'test-network';
|
||||
|
||||
expect($model->proxyConnectCommand())
|
||||
->toBe("docker network connect 'test-network' coolify-proxy >/dev/null 2>&1 || true");
|
||||
});
|
||||
|
||||
it('SwarmDocker rejects network names with shell metacharacters', function (string $network) {
|
||||
$model = new SwarmDocker;
|
||||
$model->network = $network;
|
||||
|
||||
Reference in New Issue
Block a user