feat(deploy): externalRouting toggle to keep apps off Traefik
Adds a boolean `externalRouting` flag (default `true`) on
ResolvedContainerConfig. When `false`, TraefikLabelBuilder emits only
the identity labels (`managed-by`, `cameleer.*`) and skips every
`traefik.*` label, so the container is not published by Traefik.
Sibling containers on `cameleer-traefik` / `cameleer-env-{tenant}-{env}`
can still reach it via Docker DNS on whatever port the app listens on.
TDD: new TraefikLabelBuilderTest covers enabled (default labels present),
disabled (zero traefik.* labels), and disabled (identity labels retained)
cases. Full module unit suite: 208/0/0.
Plumbed through ConfigMerger read, DeploymentExecutor snapshot, UI form
state, Resources tab toggle, POST payload, and snapshot-to-form mapping.
Rule files updated.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -605,6 +605,7 @@ public class DeploymentExecutor {
|
||||
map.put("runtimeType", config.runtimeType());
|
||||
map.put("customArgs", config.customArgs());
|
||||
map.put("extraNetworks", config.extraNetworks());
|
||||
map.put("externalRouting", config.externalRouting());
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,7 +19,6 @@ public final class TraefikLabelBuilder {
|
||||
String instanceId = envSlug + "-" + appSlug + "-" + replicaIndex + "-" + generation;
|
||||
Map<String, String> labels = new LinkedHashMap<>();
|
||||
|
||||
labels.put("traefik.enable", "true");
|
||||
labels.put("managed-by", "cameleer-server");
|
||||
labels.put("cameleer.tenant", tenantId);
|
||||
labels.put("cameleer.app", appSlug);
|
||||
@@ -28,6 +27,11 @@ public final class TraefikLabelBuilder {
|
||||
labels.put("cameleer.generation", generation);
|
||||
labels.put("cameleer.instance-id", instanceId);
|
||||
|
||||
if (!config.externalRouting()) {
|
||||
return labels;
|
||||
}
|
||||
|
||||
labels.put("traefik.enable", "true");
|
||||
labels.put("traefik.http.services." + svc + ".loadbalancer.server.port",
|
||||
String.valueOf(config.appPort()));
|
||||
|
||||
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.cameleer.server.app.runtime;
|
||||
|
||||
import com.cameleer.server.core.runtime.ResolvedContainerConfig;
|
||||
import org.junit.jupiter.api.Test;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.*;
|
||||
|
||||
class TraefikLabelBuilderTest {
|
||||
|
||||
private static ResolvedContainerConfig config(boolean externalRouting) {
|
||||
return new ResolvedContainerConfig(
|
||||
512, null, 500, null,
|
||||
8080, List.of(), Map.of(),
|
||||
true, true,
|
||||
"path", "example.com", "https://cameleer.example.com",
|
||||
1, "blue-green",
|
||||
true, true,
|
||||
"spring-boot", "", List.of(),
|
||||
externalRouting
|
||||
);
|
||||
}
|
||||
|
||||
@Test
|
||||
void build_emitsTraefikLabelsWhenExternalRoutingEnabled() {
|
||||
Map<String, String> labels = TraefikLabelBuilder.build(
|
||||
"myapp", "dev", "acme", config(true), 0, "abcdef01");
|
||||
|
||||
assertEquals("true", labels.get("traefik.enable"));
|
||||
assertEquals("8080", labels.get("traefik.http.services.dev-myapp.loadbalancer.server.port"));
|
||||
assertEquals("PathPrefix(`/dev/myapp/`)", labels.get("traefik.http.routers.dev-myapp.rule"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void build_omitsAllTraefikLabelsWhenExternalRoutingDisabled() {
|
||||
Map<String, String> labels = TraefikLabelBuilder.build(
|
||||
"myapp", "dev", "acme", config(false), 0, "abcdef01");
|
||||
|
||||
long traefikLabelCount = labels.keySet().stream()
|
||||
.filter(k -> k.startsWith("traefik."))
|
||||
.count();
|
||||
assertEquals(0, traefikLabelCount, "expected no traefik.* labels but found: " + labels);
|
||||
}
|
||||
|
||||
@Test
|
||||
void build_preservesIdentityLabelsWhenExternalRoutingDisabled() {
|
||||
Map<String, String> labels = TraefikLabelBuilder.build(
|
||||
"myapp", "dev", "acme", config(false), 2, "abcdef01");
|
||||
|
||||
assertEquals("cameleer-server", labels.get("managed-by"));
|
||||
assertEquals("acme", labels.get("cameleer.tenant"));
|
||||
assertEquals("myapp", labels.get("cameleer.app"));
|
||||
assertEquals("dev", labels.get("cameleer.environment"));
|
||||
assertEquals("2", labels.get("cameleer.replica"));
|
||||
assertEquals("abcdef01", labels.get("cameleer.generation"));
|
||||
assertEquals("dev-myapp-2-abcdef01", labels.get("cameleer.instance-id"));
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user