diff --git a/src/main/java/net/siegeln/cameleer/saas/provisioning/DockerTenantProvisioner.java b/src/main/java/net/siegeln/cameleer/saas/provisioning/DockerTenantProvisioner.java index 8d5d894..35711c6 100644 --- a/src/main/java/net/siegeln/cameleer/saas/provisioning/DockerTenantProvisioner.java +++ b/src/main/java/net/siegeln/cameleer/saas/provisioning/DockerTenantProvisioner.java @@ -114,14 +114,22 @@ public class DockerTenantProvisioner implements TenantProvisioner { private void createServerContainer(TenantProvisionRequest req, String name) { String slug = req.slug(); - Map labels = Map.of( - "traefik.enable", "true", - "traefik.http.routers.server-" + slug + ".rule", "PathPrefix(`/t/" + slug + "/api`) || PathPrefix(`/t/" + slug + "/actuator`)", - "traefik.http.routers.server-" + slug + ".tls", "true", - "traefik.http.services.server-" + slug + ".loadbalancer.server.port", "8081", - "cameleer.tenant", slug, - "cameleer.role", "server" - ); + String prefix = "/t/" + slug; + + // Traefik labels — need >10 entries, use HashMap + var labels = new java.util.HashMap(); + labels.put("traefik.enable", "true"); + // Router: match /t/{slug}/api/* and /t/{slug}/actuator/* + labels.put("traefik.http.routers.server-" + slug + ".rule", + "PathPrefix(`" + prefix + "/api`) || PathPrefix(`" + prefix + "/actuator`)"); + labels.put("traefik.http.routers.server-" + slug + ".tls", "true"); + labels.put("traefik.http.routers.server-" + slug + ".priority", "10"); + // Strip the /t/{slug} prefix so server sees /api/... and /actuator/... + labels.put("traefik.http.middlewares.server-strip-" + slug + ".stripprefix.prefixes", prefix); + labels.put("traefik.http.routers.server-" + slug + ".middlewares", "server-strip-" + slug); + labels.put("traefik.http.services.server-" + slug + ".loadbalancer.server.port", "8081"); + labels.put("cameleer.tenant", slug); + labels.put("cameleer.role", "server"); List env = List.of( "SPRING_DATASOURCE_URL=" + props.datasourceUrl(), @@ -140,12 +148,18 @@ public class DockerTenantProvisioner implements TenantProvisioner { "CAMELEER_RUNTIME_ENABLED=true", "CAMELEER_SERVER_URL=http://" + name + ":8081", "CAMELEER_ROUTING_DOMAIN=" + props.publicHost(), - "CAMELEER_ROUTING_MODE=path" + "CAMELEER_ROUTING_MODE=path", + "CAMELEER_JAR_STORAGE_PATH=/data/jars", + "CAMELEER_DOCKER_NETWORK=" + props.networkName(), + "CAMELEER_JAR_DOCKER_VOLUME=cameleer-jars-" + slug ); + // Docker socket mount for app deployment within tenant server HostConfig hostConfig = HostConfig.newHostConfig() .withRestartPolicy(RestartPolicy.unlessStoppedRestart()) - .withNetworkMode(props.networkName()); + .withNetworkMode(props.networkName()) + .withBinds(new Bind("/var/run/docker.sock", new Volume("/var/run/docker.sock"))) + .withGroupAdd(List.of("0")); CreateContainerResponse resp = docker.createContainerCmd(props.serverImage()) .withName(name) @@ -159,7 +173,7 @@ public class DockerTenantProvisioner implements TenantProvisioner { .withRetries(12)) .exec(); - // Connect to traefik network + // Connect to traefik network with DNS alias docker.connectToNetworkCmd() .withNetworkId(props.traefikNetwork()) .withContainerId(resp.getId()) @@ -168,20 +182,23 @@ public class DockerTenantProvisioner implements TenantProvisioner { } private void createUiContainer(String slug, String uiName, String serverName) { - Map labels = Map.of( - "traefik.enable", "true", - "traefik.http.routers.ui-" + slug + ".rule", "PathPrefix(`/t/" + slug + "`)", - "traefik.http.routers.ui-" + slug + ".tls", "true", - "traefik.http.routers.ui-" + slug + ".priority", "1", - "traefik.http.services.ui-" + slug + ".loadbalancer.server.port", "80", - "traefik.http.middlewares.ui-strip-" + slug + ".stripprefix.prefixes", "/t/" + slug, - "traefik.http.routers.ui-" + slug + ".middlewares", "ui-strip-" + slug, - "cameleer.tenant", slug, - "cameleer.role", "server-ui" - ); + String prefix = "/t/" + slug; + + var labels = new java.util.HashMap(); + labels.put("traefik.enable", "true"); + // Router: catch-all for /t/{slug}/* (lower priority than server API) + labels.put("traefik.http.routers.ui-" + slug + ".rule", "PathPrefix(`" + prefix + "`)"); + labels.put("traefik.http.routers.ui-" + slug + ".tls", "true"); + labels.put("traefik.http.routers.ui-" + slug + ".priority", "5"); + // Strip /t/{slug} prefix so UI sees / + labels.put("traefik.http.middlewares.ui-strip-" + slug + ".stripprefix.prefixes", prefix); + labels.put("traefik.http.routers.ui-" + slug + ".middlewares", "ui-strip-" + slug); + labels.put("traefik.http.services.ui-" + slug + ".loadbalancer.server.port", "80"); + labels.put("cameleer.tenant", slug); + labels.put("cameleer.role", "server-ui"); List env = List.of( - "BASE_PATH=/t/" + slug, + "BASE_PATH=" + prefix, "API_URL=http://" + serverName + ":8081" );