feat: add TenantProvisioner interface with auto-detection
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
12
pom.xml
12
pom.xml
@@ -80,6 +80,18 @@
|
||||
<artifactId>spring-boot-starter-actuator</artifactId>
|
||||
</dependency>
|
||||
|
||||
<!-- Docker Java (tenant provisioning) -->
|
||||
<dependency>
|
||||
<groupId>com.github.docker-java</groupId>
|
||||
<artifactId>docker-java-core</artifactId>
|
||||
<version>3.4.1</version>
|
||||
</dependency>
|
||||
<dependency>
|
||||
<groupId>com.github.docker-java</groupId>
|
||||
<artifactId>docker-java-transport-zerodep</artifactId>
|
||||
<version>3.4.1</version>
|
||||
</dependency>
|
||||
|
||||
<!-- Test -->
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
package net.siegeln.cameleer.saas.provisioning;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
public class DisabledTenantProvisioner implements TenantProvisioner {
|
||||
private static final Logger log = LoggerFactory.getLogger(DisabledTenantProvisioner.class);
|
||||
|
||||
@Override public boolean isAvailable() { return false; }
|
||||
@Override public ProvisionResult provision(TenantProvisionRequest request) {
|
||||
log.warn("Provisioning disabled — no Docker socket or K8s detected");
|
||||
return ProvisionResult.fail("Provisioning not available");
|
||||
}
|
||||
@Override public void start(String slug) { log.warn("Cannot start: provisioning disabled"); }
|
||||
@Override public void stop(String slug) { log.warn("Cannot stop: provisioning disabled"); }
|
||||
@Override public void remove(String slug) { log.warn("Cannot remove: provisioning disabled"); }
|
||||
@Override public ServerStatus getStatus(String slug) { return ServerStatus.notFound(); }
|
||||
@Override public String getServerEndpoint(String slug) { return null; }
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
package net.siegeln.cameleer.saas.provisioning;
|
||||
|
||||
import com.github.dockerjava.core.DockerClientConfig;
|
||||
|
||||
public class DockerTenantProvisioner implements TenantProvisioner {
|
||||
private final DockerClientConfig config;
|
||||
private final ProvisioningProperties props;
|
||||
|
||||
public DockerTenantProvisioner(DockerClientConfig config, ProvisioningProperties props) {
|
||||
this.config = config;
|
||||
this.props = props;
|
||||
}
|
||||
|
||||
@Override public boolean isAvailable() { return true; }
|
||||
@Override public ProvisionResult provision(TenantProvisionRequest request) { throw new UnsupportedOperationException("Not yet implemented"); }
|
||||
@Override public void start(String slug) { throw new UnsupportedOperationException("Not yet implemented"); }
|
||||
@Override public void stop(String slug) { throw new UnsupportedOperationException("Not yet implemented"); }
|
||||
@Override public void remove(String slug) { throw new UnsupportedOperationException("Not yet implemented"); }
|
||||
@Override public ServerStatus getStatus(String slug) { return ServerStatus.notFound(); }
|
||||
@Override public String getServerEndpoint(String slug) { return "http://cameleer-server-" + slug + ":8081"; }
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package net.siegeln.cameleer.saas.provisioning;
|
||||
|
||||
public record ProvisionResult(
|
||||
boolean success,
|
||||
String serverEndpoint,
|
||||
String error
|
||||
) {
|
||||
public static ProvisionResult ok(String endpoint) {
|
||||
return new ProvisionResult(true, endpoint, null);
|
||||
}
|
||||
public static ProvisionResult fail(String error) {
|
||||
return new ProvisionResult(false, null, error);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
package net.siegeln.cameleer.saas.provisioning;
|
||||
|
||||
import org.springframework.boot.context.properties.ConfigurationProperties;
|
||||
|
||||
@ConfigurationProperties(prefix = "cameleer.provisioning")
|
||||
public record ProvisioningProperties(
|
||||
String serverImage,
|
||||
String serverUiImage,
|
||||
String networkName,
|
||||
String traefikNetwork,
|
||||
String publicHost,
|
||||
String publicProtocol,
|
||||
String datasourceUrl,
|
||||
String oidcIssuerUri,
|
||||
String oidcJwkSetUri,
|
||||
String corsOrigins
|
||||
) {}
|
||||
@@ -0,0 +1,22 @@
|
||||
package net.siegeln.cameleer.saas.provisioning;
|
||||
|
||||
public record ServerStatus(
|
||||
State state,
|
||||
String containerId,
|
||||
String error
|
||||
) {
|
||||
public enum State { RUNNING, STOPPED, NOT_FOUND, ERROR }
|
||||
|
||||
public static ServerStatus running(String containerId) {
|
||||
return new ServerStatus(State.RUNNING, containerId, null);
|
||||
}
|
||||
public static ServerStatus stopped(String containerId) {
|
||||
return new ServerStatus(State.STOPPED, containerId, null);
|
||||
}
|
||||
public static ServerStatus notFound() {
|
||||
return new ServerStatus(State.NOT_FOUND, null, null);
|
||||
}
|
||||
public static ServerStatus error(String error) {
|
||||
return new ServerStatus(State.ERROR, null, error);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
package net.siegeln.cameleer.saas.provisioning;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public record TenantProvisionRequest(
|
||||
UUID tenantId,
|
||||
String slug,
|
||||
String tier,
|
||||
String licenseToken
|
||||
) {}
|
||||
@@ -0,0 +1,11 @@
|
||||
package net.siegeln.cameleer.saas.provisioning;
|
||||
|
||||
public interface TenantProvisioner {
|
||||
boolean isAvailable();
|
||||
ProvisionResult provision(TenantProvisionRequest request);
|
||||
void start(String slug);
|
||||
void stop(String slug);
|
||||
void remove(String slug);
|
||||
ServerStatus getStatus(String slug);
|
||||
String getServerEndpoint(String slug);
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
package net.siegeln.cameleer.saas.provisioning;
|
||||
|
||||
import com.github.dockerjava.core.DefaultDockerClientConfig;
|
||||
import com.github.dockerjava.core.DockerClientConfig;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
import org.springframework.boot.context.properties.EnableConfigurationProperties;
|
||||
import org.springframework.context.annotation.Bean;
|
||||
import org.springframework.context.annotation.Configuration;
|
||||
|
||||
import java.nio.file.Files;
|
||||
import java.nio.file.Path;
|
||||
|
||||
@Configuration
|
||||
@EnableConfigurationProperties(ProvisioningProperties.class)
|
||||
public class TenantProvisionerAutoConfig {
|
||||
private static final Logger log = LoggerFactory.getLogger(TenantProvisionerAutoConfig.class);
|
||||
|
||||
@Bean
|
||||
TenantProvisioner tenantProvisioner(ProvisioningProperties props) {
|
||||
if (Files.exists(Path.of("/var/run/docker.sock"))) {
|
||||
log.info("Docker socket detected — enabling Docker tenant provisioner");
|
||||
DockerClientConfig config = DefaultDockerClientConfig.createDefaultConfigBuilder()
|
||||
.withDockerHost("unix:///var/run/docker.sock")
|
||||
.build();
|
||||
return new DockerTenantProvisioner(config, props);
|
||||
}
|
||||
log.info("No Docker socket — tenant provisioning disabled");
|
||||
return new DisabledTenantProvisioner();
|
||||
}
|
||||
}
|
||||
@@ -41,3 +41,14 @@ cameleer:
|
||||
spa-client-id: ${LOGTO_SPA_CLIENT_ID:}
|
||||
audience: ${CAMELEER_OIDC_AUDIENCE:https://api.cameleer.local}
|
||||
server-endpoint: ${CAMELEER3_SERVER_ENDPOINT:http://cameleer3-server:8081}
|
||||
provisioning:
|
||||
server-image: ${CAMELEER_SERVER_IMAGE:gitea.siegeln.net/cameleer/cameleer3-server:latest}
|
||||
server-ui-image: ${CAMELEER_SERVER_UI_IMAGE:gitea.siegeln.net/cameleer/cameleer3-server-ui:latest}
|
||||
network-name: ${CAMELEER_NETWORK:cameleer-saas_cameleer}
|
||||
traefik-network: ${CAMELEER_TRAEFIK_NETWORK:cameleer-traefik}
|
||||
public-host: ${PUBLIC_HOST:localhost}
|
||||
public-protocol: ${PUBLIC_PROTOCOL:https}
|
||||
datasource-url: ${CAMELEER_SERVER_DB_URL:jdbc:postgresql://postgres:5432/cameleer3}
|
||||
oidc-issuer-uri: ${PUBLIC_PROTOCOL:https}://${PUBLIC_HOST:localhost}/oidc
|
||||
oidc-jwk-set-uri: http://logto:3001/oidc/jwks
|
||||
cors-origins: ${PUBLIC_PROTOCOL:https}://${PUBLIC_HOST:localhost}
|
||||
|
||||
Reference in New Issue
Block a user