fix(proxy): remove legacy Traefik dashboard labels only in the traefik service

The line edits ran on the whole proxy compose file. When another service
had the same labels, the safety check failed and the dashboard fix was
skipped without a warning. Limit the edits to the traefik service block,
so other services stay unchanged and the fix is applied.
This commit is contained in:
Andras Bacsai
2026-10-02 17:03:55 +02:00
parent 8c2725a3f8
commit d6f89d1feb
2 changed files with 60 additions and 3 deletions
+41 -3
View File
@@ -566,10 +566,17 @@ function removeLegacyTraefikDashboardLabels(string $configuration): string
return $configuration;
}
$fixed = preg_replace('/^([ \t]*-[ \t]*([\'"]?))traefik\.enable=true(\2[ \t]*)(?=\R|\z)/m', '$1traefik.enable=false$3', $configuration);
foreach (array_slice($legacyLabels, 1) as $label) {
$fixed = preg_replace('/^[ \t]*-[ \t]*([\'"]?)'.preg_quote($label, '/').'\1[ \t]*(?:\R|\z)/m', '', $fixed);
// Edit only the lines of the traefik service, so equal labels of other services stay unchanged.
$block = traefikServiceBlockRange($configuration);
if ($block === null) {
return $configuration;
}
[$start, $length] = $block;
$traefikBlock = preg_replace('/^([ \t]*-[ \t]*([\'"]?))traefik\.enable=true(\2[ \t]*)(?=\R|\z)/m', '$1traefik.enable=false$3', substr($configuration, $start, $length));
foreach (array_slice($legacyLabels, 1) as $label) {
$traefikBlock = preg_replace('/^[ \t]*-[ \t]*([\'"]?)'.preg_quote($label, '/').'\1[ \t]*(?:\R|\z)/m', '', $traefikBlock);
}
$fixed = substr($configuration, 0, $start).$traefikBlock.substr($configuration, $start + $length);
// Keep the original when the line edit also changed other parts of the file.
try {
@@ -579,6 +586,37 @@ function removeLegacyTraefikDashboardLabels(string $configuration): string
}
}
/**
* The byte offset and length of the `traefik` service block under the top-level `services` key,
* or null when the block cannot be found in the text.
*
* @return array{0: int, 1: int}|null
*/
function traefikServiceBlockRange(string $configuration): ?array
{
if (! preg_match('/^services:[ \t]*(?:#.*)?$/m', $configuration, $services, PREG_OFFSET_CAPTURE)) {
return null;
}
$offset = $services[0][1] + strlen($services[0][0]);
// The services are the lines with the indent of the first content line below `services:`.
if (! preg_match('/^([ \t]+)[^\s#]/m', $configuration, $firstChild, PREG_OFFSET_CAPTURE, $offset)) {
return null;
}
$indent = preg_quote($firstChild[1][0], '/');
if (! preg_match('/^'.$indent.'([\'"]?)traefik\1:[ \t]*(?:#.*)?(?:\R|\z)/m', $configuration, $traefik, PREG_OFFSET_CAPTURE, $offset)) {
return null;
}
$start = $traefik[0][1] + strlen($traefik[0][0]);
// The block ends at the next content line that is not indented deeper than the service key.
$end = preg_match('/^(?!'.$indent.'[ \t])(?=[ \t]*[^\s#])/m', $configuration, $next, PREG_OFFSET_CAPTURE, $start)
? $next[0][1]
: strlen($configuration);
return [$start, $end - $start];
}
/**
* Saves the Traefik configuration without the legacy dashboard labels. The next proxy restart applies it.
*/
@@ -83,6 +83,25 @@ test('quoted legacy labels are also replaced', function () {
expect($labels)->toBe(['traefik.enable=false', 'coolify.managed=true', 'coolify.proxy=true']);
});
test('only the traefik service is changed when another proxy service has the same labels', function (bool $swarm, bool $whoamiFirst) {
$whoami = Yaml::dump(['whoami' => ['image' => 'traefik/whoami', 'labels' => [
'traefik.enable=true',
'traefik.http.routers.whoami.rule=Host(`whoami.example.com`)',
'traefik.http.services.traefik.loadbalancer.server.port=8080',
]]], 10, 2);
$whoami = preg_replace('/^/m', ' ', rtrim($whoami))."\n";
$configuration = $whoamiFirst
? str_replace("services:\n", "services:\n".$whoami, legacyTraefikConfiguration(swarm: $swarm))
: legacyTraefikConfiguration(swarm: $swarm).$whoami;
$fixed = removeLegacyTraefikDashboardLabels($configuration);
$parsed = Yaml::parse($fixed);
expect(data_get($parsed, $swarm ? 'services.traefik.deploy.labels' : 'services.traefik.labels'))->toBe(['traefik.enable=false', 'coolify.managed=true', 'coolify.proxy=true'])
->and(data_get($parsed, 'services.whoami'))->toBe(data_get(Yaml::parse($configuration), 'services.whoami'))
->and($fixed)->toContain('# my custom comment');
})->with(['standalone' => false, 'swarm' => true])->with(['whoami after traefik' => false, 'whoami before traefik' => true]);
test('a dashboard router that the user changed is kept', function (array $extraLabels) {
$configuration = legacyTraefikConfiguration($extraLabels);