fix: re-provision containers when restart finds them missing
When Docker containers have been removed (e.g. manual cleanup or image update), restart now falls back to full re-provisioning instead of failing with 404. Applies to both vendor and tenant portal restart. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -9,6 +9,10 @@ import net.siegeln.cameleer.saas.provisioning.ProvisioningProperties;
|
|||||||
import net.siegeln.cameleer.saas.provisioning.TenantProvisioner;
|
import net.siegeln.cameleer.saas.provisioning.TenantProvisioner;
|
||||||
import net.siegeln.cameleer.saas.tenant.TenantEntity;
|
import net.siegeln.cameleer.saas.tenant.TenantEntity;
|
||||||
import net.siegeln.cameleer.saas.tenant.TenantService;
|
import net.siegeln.cameleer.saas.tenant.TenantService;
|
||||||
|
import net.siegeln.cameleer.saas.vendor.VendorTenantService;
|
||||||
|
import org.slf4j.Logger;
|
||||||
|
import org.slf4j.LoggerFactory;
|
||||||
|
import org.springframework.context.annotation.Lazy;
|
||||||
import org.springframework.stereotype.Service;
|
import org.springframework.stereotype.Service;
|
||||||
|
|
||||||
import java.time.Instant;
|
import java.time.Instant;
|
||||||
@@ -20,25 +24,30 @@ import java.util.UUID;
|
|||||||
@Service
|
@Service
|
||||||
public class TenantPortalService {
|
public class TenantPortalService {
|
||||||
|
|
||||||
|
private static final Logger log = LoggerFactory.getLogger(TenantPortalService.class);
|
||||||
|
|
||||||
private final TenantService tenantService;
|
private final TenantService tenantService;
|
||||||
private final LicenseService licenseService;
|
private final LicenseService licenseService;
|
||||||
private final ServerApiClient serverApiClient;
|
private final ServerApiClient serverApiClient;
|
||||||
private final LogtoManagementClient logtoClient;
|
private final LogtoManagementClient logtoClient;
|
||||||
private final TenantProvisioner tenantProvisioner;
|
private final TenantProvisioner tenantProvisioner;
|
||||||
private final ProvisioningProperties provisioningProps;
|
private final ProvisioningProperties provisioningProps;
|
||||||
|
private final VendorTenantService vendorTenantService;
|
||||||
|
|
||||||
public TenantPortalService(TenantService tenantService,
|
public TenantPortalService(TenantService tenantService,
|
||||||
LicenseService licenseService,
|
LicenseService licenseService,
|
||||||
ServerApiClient serverApiClient,
|
ServerApiClient serverApiClient,
|
||||||
LogtoManagementClient logtoClient,
|
LogtoManagementClient logtoClient,
|
||||||
TenantProvisioner tenantProvisioner,
|
TenantProvisioner tenantProvisioner,
|
||||||
ProvisioningProperties provisioningProps) {
|
ProvisioningProperties provisioningProps,
|
||||||
|
@Lazy VendorTenantService vendorTenantService) {
|
||||||
this.tenantService = tenantService;
|
this.tenantService = tenantService;
|
||||||
this.licenseService = licenseService;
|
this.licenseService = licenseService;
|
||||||
this.serverApiClient = serverApiClient;
|
this.serverApiClient = serverApiClient;
|
||||||
this.logtoClient = logtoClient;
|
this.logtoClient = logtoClient;
|
||||||
this.tenantProvisioner = tenantProvisioner;
|
this.tenantProvisioner = tenantProvisioner;
|
||||||
this.provisioningProps = provisioningProps;
|
this.provisioningProps = provisioningProps;
|
||||||
|
this.vendorTenantService = vendorTenantService;
|
||||||
}
|
}
|
||||||
|
|
||||||
// --- Inner records ---
|
// --- Inner records ---
|
||||||
@@ -180,9 +189,22 @@ public class TenantPortalService {
|
|||||||
|
|
||||||
public void restartServer() {
|
public void restartServer() {
|
||||||
TenantEntity tenant = resolveTenant();
|
TenantEntity tenant = resolveTenant();
|
||||||
if (tenantProvisioner.isAvailable()) {
|
if (!tenantProvisioner.isAvailable()) return;
|
||||||
tenantProvisioner.stop(tenant.getSlug());
|
|
||||||
|
tenantProvisioner.stop(tenant.getSlug());
|
||||||
|
try {
|
||||||
tenantProvisioner.start(tenant.getSlug());
|
tenantProvisioner.start(tenant.getSlug());
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
if (e.getMessage() != null && e.getMessage().contains("re-provision required")) {
|
||||||
|
log.info("Containers missing for '{}' — re-provisioning", tenant.getSlug());
|
||||||
|
tenantProvisioner.remove(tenant.getSlug());
|
||||||
|
var license = licenseService.getActiveLicense(tenant.getId()).orElse(null);
|
||||||
|
String token = license != null ? license.getToken() : "";
|
||||||
|
vendorTenantService.provisionAsync(
|
||||||
|
tenant.getId(), tenant.getSlug(), tenant.getTier().name(), token, null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -73,6 +73,9 @@ public class DockerTenantProvisioner implements TenantProvisioner {
|
|||||||
try {
|
try {
|
||||||
docker.startContainerCmd(serverContainerName(slug)).exec();
|
docker.startContainerCmd(serverContainerName(slug)).exec();
|
||||||
docker.startContainerCmd(uiContainerName(slug)).exec();
|
docker.startContainerCmd(uiContainerName(slug)).exec();
|
||||||
|
} catch (NotFoundException e) {
|
||||||
|
log.warn("Containers for '{}' not found — cannot start (re-provision needed)", slug);
|
||||||
|
throw new RuntimeException("Containers not found for '" + slug + "' — re-provision required", e);
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("Failed to start containers for '{}'", slug, e);
|
log.error("Failed to start containers for '{}'", slug, e);
|
||||||
throw new RuntimeException("Start failed: " + e.getMessage(), e);
|
throw new RuntimeException("Start failed: " + e.getMessage(), e);
|
||||||
|
|||||||
@@ -220,9 +220,21 @@ public class VendorTenantService {
|
|||||||
public void restartServer(UUID tenantId) {
|
public void restartServer(UUID tenantId) {
|
||||||
TenantEntity tenant = tenantService.getById(tenantId)
|
TenantEntity tenant = tenantService.getById(tenantId)
|
||||||
.orElseThrow(() -> new IllegalArgumentException("Tenant not found"));
|
.orElseThrow(() -> new IllegalArgumentException("Tenant not found"));
|
||||||
if (tenantProvisioner.isAvailable()) {
|
if (!tenantProvisioner.isAvailable()) return;
|
||||||
tenantProvisioner.stop(tenant.getSlug());
|
|
||||||
|
tenantProvisioner.stop(tenant.getSlug());
|
||||||
|
try {
|
||||||
tenantProvisioner.start(tenant.getSlug());
|
tenantProvisioner.start(tenant.getSlug());
|
||||||
|
} catch (RuntimeException e) {
|
||||||
|
if (e.getMessage() != null && e.getMessage().contains("re-provision required")) {
|
||||||
|
log.info("Containers missing for '{}' — re-provisioning", tenant.getSlug());
|
||||||
|
tenantProvisioner.remove(tenant.getSlug());
|
||||||
|
var license = licenseService.getActiveLicense(tenantId).orElse(null);
|
||||||
|
String token = license != null ? license.getToken() : "";
|
||||||
|
provisionAsync(tenantId, tenant.getSlug(), tenant.getTier().name(), token, null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
throw e;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ class TenantPortalServiceTest {
|
|||||||
@BeforeEach
|
@BeforeEach
|
||||||
void setUp() {
|
void setUp() {
|
||||||
TenantContext.setTenantId(tenantId);
|
TenantContext.setTenantId(tenantId);
|
||||||
tenantPortalService = new TenantPortalService(tenantService, licenseService, serverApiClient, logtoClient, tenantProvisioner, provisioningProps);
|
tenantPortalService = new TenantPortalService(tenantService, licenseService, serverApiClient, logtoClient, tenantProvisioner, provisioningProps, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
@AfterEach
|
@AfterEach
|
||||||
|
|||||||
Reference in New Issue
Block a user