mirror of
https://github.com/coollabsio/coolify.git
synced 2026-11-06 13:10:37 -04:00
fix(proxy): require coolify-proxy container name outside Swarm (#12225)
This commit is contained in:
@@ -6,6 +6,8 @@ use App\Models\Server;
|
||||
use App\Services\ProxyPortParser;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
use Lorisleiva\Actions\Concerns\AsAction;
|
||||
use Symfony\Component\Yaml\Tag\TaggedValue;
|
||||
use Symfony\Component\Yaml\Yaml;
|
||||
|
||||
class SaveProxyConfiguration
|
||||
{
|
||||
@@ -23,6 +25,10 @@ class SaveProxyConfiguration
|
||||
]);
|
||||
}
|
||||
|
||||
if (! $server->isSwarm()) {
|
||||
$this->ensureDefaultContainerName($configuration);
|
||||
}
|
||||
|
||||
$proxy_path = $server->proxyPath();
|
||||
$docker_compose_yml_base64 = base64_encode($configuration);
|
||||
$new_hash = str($docker_compose_yml_base64)->pipe('md5')->value;
|
||||
@@ -60,4 +66,29 @@ class SaveProxyConfiguration
|
||||
|
||||
instant_remote_process($commands, $server);
|
||||
}
|
||||
|
||||
/**
|
||||
* Coolify finds the proxy by its container name, so a renamed or unnamed proxy shows as stopped (#7083).
|
||||
*/
|
||||
private function ensureDefaultContainerName(string $configuration): void
|
||||
{
|
||||
$services = data_get(Yaml::parse($configuration, Yaml::PARSE_CUSTOM_TAGS), 'services');
|
||||
|
||||
foreach (['traefik', 'caddy'] as $proxyService) {
|
||||
if (! is_array($services) || ! array_key_exists($proxyService, $services)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$containerName = data_get($services, "{$proxyService}.container_name");
|
||||
if ($containerName instanceof TaggedValue) {
|
||||
$containerName = $containerName->getValue();
|
||||
}
|
||||
|
||||
if ($containerName !== 'coolify-proxy') {
|
||||
throw ValidationException::withMessages([
|
||||
'configuration' => ["The {$proxyService} container_name must be coolify-proxy. Coolify uses this name to manage the proxy."],
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -112,8 +112,8 @@ it('deploys the swarm proxy stack with an absolute compose file path', function
|
||||
it('builds restart commands without entering proxy directories as the SSH user', function (string $proxyType, string $proxyPath) {
|
||||
useNonRootProxy($this->server, $proxyType);
|
||||
$configuration = $this->server->proxyType() === ProxyTypes::CADDY->value
|
||||
? "services:\n caddy:\n image: lucaslorentz/caddy-docker-proxy:2.13-alpine\n"
|
||||
: "services:\n traefik:\n image: traefik:v3.6\n";
|
||||
? "services:\n caddy:\n container_name: coolify-proxy\n image: lucaslorentz/caddy-docker-proxy:2.13-alpine\n"
|
||||
: "services:\n traefik:\n container_name: coolify-proxy\n image: traefik:v3.6\n";
|
||||
|
||||
$method = new ReflectionMethod(RestartProxyJob::class, 'buildRestartCommands');
|
||||
$commands = $method->invoke(new RestartProxyJob($this->server), $configuration);
|
||||
@@ -133,7 +133,7 @@ it('backs up and prunes proxy configurations through sudo without bash', functio
|
||||
$this->server->proxy->last_saved_settings = 'a1b2c3d4e5f6';
|
||||
$this->server->save();
|
||||
|
||||
SaveProxyConfiguration::run($this->server, "services:\n caddy:\n image: lucaslorentz/caddy-docker-proxy:2.13-alpine\n");
|
||||
SaveProxyConfiguration::run($this->server, "services:\n caddy:\n container_name: coolify-proxy\n image: lucaslorentz/caddy-docker-proxy:2.13-alpine\n");
|
||||
|
||||
$lines = nonRootProxyScriptLines();
|
||||
$backups = '/data/coolify/proxy/caddy/backups';
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
<?php
|
||||
|
||||
use App\Actions\Proxy\SaveProxyConfiguration;
|
||||
use App\Enums\ProxyTypes;
|
||||
use App\Models\InstanceSettings;
|
||||
use App\Models\PrivateKey;
|
||||
use App\Models\Server;
|
||||
use App\Models\Team;
|
||||
use Illuminate\Foundation\Testing\RefreshDatabase;
|
||||
use Illuminate\Support\Facades\Process;
|
||||
use Illuminate\Validation\ValidationException;
|
||||
|
||||
uses(RefreshDatabase::class);
|
||||
|
||||
beforeEach(function () {
|
||||
InstanceSettings::forceCreate(['id' => 0]);
|
||||
Process::fake();
|
||||
|
||||
$team = Team::factory()->create();
|
||||
$this->server = Server::factory()->create([
|
||||
'team_id' => $team->id,
|
||||
'private_key_id' => PrivateKey::factory()->create(['team_id' => $team->id])->id,
|
||||
]);
|
||||
$this->server->settings->update(['is_reachable' => true, 'is_usable' => true, 'force_disabled' => false]);
|
||||
$this->server->proxy->type = ProxyTypes::TRAEFIK->value;
|
||||
$this->server->save();
|
||||
$this->server->refresh();
|
||||
});
|
||||
|
||||
it('rejects a renamed proxy container before database or remote writes', function () {
|
||||
$originalProxy = $this->server->proxy->toArray();
|
||||
$configuration = "services:\n traefik:\n container_name: utility-servers-proxy\n image: traefik:v3.7\n";
|
||||
|
||||
expect(fn () => SaveProxyConfiguration::run($this->server, $configuration))
|
||||
->toThrow(ValidationException::class, 'The traefik container_name must be coolify-proxy.');
|
||||
|
||||
expect($this->server->fresh()->proxy->toArray())->toBe($originalProxy);
|
||||
Process::assertNothingRan();
|
||||
});
|
||||
|
||||
it('rejects a proxy service without a container name', function () {
|
||||
$configuration = "services:\n traefik:\n image: traefik:v3.7\n";
|
||||
|
||||
expect(fn () => SaveProxyConfiguration::run($this->server, $configuration))
|
||||
->toThrow(ValidationException::class, 'The traefik container_name must be coolify-proxy.');
|
||||
|
||||
Process::assertNothingRan();
|
||||
});
|
||||
|
||||
it('saves a proxy configuration with the default container name', function () {
|
||||
$configuration = "services:\n traefik:\n container_name: coolify-proxy\n image: traefik:v3.7\n traefik-logrotate:\n container_name: coolify-proxy-logrotate\n";
|
||||
|
||||
SaveProxyConfiguration::run($this->server, $configuration);
|
||||
|
||||
expect($this->server->fresh()->proxy->last_saved_proxy_configuration)->toBe($configuration);
|
||||
});
|
||||
|
||||
it('allows any container name on a swarm server', function () {
|
||||
$this->server->settings()->update(['is_swarm_manager' => true]);
|
||||
$configuration = "services:\n traefik:\n container_name: utility-servers-proxy\n image: traefik:v3.7\n";
|
||||
|
||||
SaveProxyConfiguration::run($this->server->fresh(), $configuration);
|
||||
|
||||
expect($this->server->fresh()->proxy->last_saved_proxy_configuration)->toBe($configuration);
|
||||
});
|
||||
@@ -54,7 +54,7 @@ dataset('proxy port variables', [
|
||||
|
||||
function proxyConfigurationWithPort(string $port): string
|
||||
{
|
||||
return "services:\n traefik:\n image: traefik:v3.6\n ports:\n - '{$port}'\n - '443:443'\n";
|
||||
return "services:\n traefik:\n container_name: coolify-proxy\n image: traefik:v3.6\n ports:\n - '{$port}'\n - '443:443'\n";
|
||||
}
|
||||
|
||||
it('saves a proxy configuration whose ports use variables', function (string $port) {
|
||||
@@ -87,7 +87,7 @@ it('restarts a proxy whose ports use variables', function (string $port) {
|
||||
})->with('proxy port variables');
|
||||
|
||||
it('asks to start a stopped proxy whose ports use variables and checks only concrete ports', function () {
|
||||
$this->server->proxy->last_saved_proxy_configuration = "services:\n traefik:\n image: traefik:v3.6\n ports:\n - '\${HTTP_PORT:-8081}:80'\n - '\${HTTPS_PORT}:443'\n";
|
||||
$this->server->proxy->last_saved_proxy_configuration = "services:\n traefik:\n container_name: coolify-proxy\n image: traefik:v3.6\n ports:\n - '\${HTTP_PORT:-8081}:80'\n - '\${HTTPS_PORT}:443'\n";
|
||||
$this->server->save();
|
||||
|
||||
expect(CheckProxy::run($this->server))->toBeTrue();
|
||||
@@ -97,7 +97,7 @@ it('asks to start a stopped proxy whose ports use variables and checks only conc
|
||||
});
|
||||
|
||||
it('asks to start a stopped proxy whose ports all use variables without defaults', function () {
|
||||
$this->server->proxy->last_saved_proxy_configuration = "services:\n traefik:\n image: traefik:v3.6\n ports:\n - '\${HTTP_PORT}:80'\n - '\${HTTPS_PORT}:443'\n";
|
||||
$this->server->proxy->last_saved_proxy_configuration = "services:\n traefik:\n container_name: coolify-proxy\n image: traefik:v3.6\n ports:\n - '\${HTTP_PORT}:80'\n - '\${HTTPS_PORT}:443'\n";
|
||||
$this->server->save();
|
||||
|
||||
expect(CheckProxy::run($this->server))->toBeTrue();
|
||||
@@ -106,14 +106,14 @@ it('asks to start a stopped proxy whose ports all use variables without defaults
|
||||
});
|
||||
|
||||
it('does not ask to start a stopped proxy without published ports', function () {
|
||||
$this->server->proxy->last_saved_proxy_configuration = "services:\n traefik:\n image: traefik:v3.6\n ports:\n";
|
||||
$this->server->proxy->last_saved_proxy_configuration = "services:\n traefik:\n container_name: coolify-proxy\n image: traefik:v3.6\n ports:\n";
|
||||
$this->server->save();
|
||||
|
||||
expect(CheckProxy::run($this->server))->toBeFalse();
|
||||
});
|
||||
|
||||
it('saves and starts a proxy whose ports key is empty', function () {
|
||||
$configuration = "services:\n traefik:\n image: traefik:v3.6\n ports:\n";
|
||||
$configuration = "services:\n traefik:\n container_name: coolify-proxy\n image: traefik:v3.6\n ports:\n";
|
||||
|
||||
SaveProxyConfiguration::run($this->server, $configuration);
|
||||
StartProxy::run($this->server->fresh(), async: false, force: true);
|
||||
|
||||
Reference in New Issue
Block a user