Add dry-run provisioning command generation

This commit is contained in:
litoral05
2026-05-05 12:29:33 +01:00
parent c09aff2fcb
commit 6f70cbfe67
4 changed files with 42 additions and 9 deletions
@@ -2,6 +2,7 @@ package com.litoralregas.openvpn.openvpn;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
import java.util.UUID;
public interface IpAllocationRepository extends JpaRepository<IpAllocation, UUID> {
@@ -11,4 +12,5 @@ public interface IpAllocationRepository extends JpaRepository<IpAllocation, UUID
boolean existsByLanSubnet(String lanSubnet);
boolean existsByVpnIp(String vpnIp);
Optional<IpAllocation> findByRouterId(UUID routerId);
}
@@ -113,4 +113,9 @@ public class IpAllocationService {
public void delete(UUID id) {
repository.deleteById(id);
}
public IpAllocation findByRouterId(UUID routerId) {
return repository.findByRouterId(routerId)
.orElseThrow(() -> new IllegalArgumentException("No IP allocation found for router: " + routerId));
}
}
@@ -64,4 +64,22 @@ public class OpenVpnService {
return null;
}
public String buildProvisionCommand(String clientName, String lanSubnet, String vpnIp) {
validateShellSafe(clientName);
validateShellSafe(lanSubnet);
validateShellSafe(vpnIp);
return properties.getToolsPath()
+ "/provision-client.sh "
+ clientName + " "
+ lanSubnet + " "
+ vpnIp;
}
private void validateShellSafe(String value) {
if (value == null || !value.matches("^[a-zA-Z0-9._/-]+$")) {
throw new IllegalArgumentException("Unsafe command value: " + value);
}
}
}
@@ -3,6 +3,8 @@ package com.litoralregas.openvpn.router;
import com.litoralregas.openvpn.deployment.DeploymentAction;
import com.litoralregas.openvpn.deployment.DeploymentResponse;
import com.litoralregas.openvpn.deployment.DeploymentService;
import com.litoralregas.openvpn.openvpn.IpAllocationService;
import com.litoralregas.openvpn.openvpn.OpenVpnService;
import com.litoralregas.openvpn.ssh.SshService;
import jakarta.validation.Valid;
import org.springframework.web.bind.annotation.*;
@@ -17,11 +19,15 @@ public class RouterController {
private final RouterService service;
private final DeploymentService deploymentService;
private final SshService sshService;
private final IpAllocationService ipAllocationService;
private final OpenVpnService openVpnService;
public RouterController(RouterService service, DeploymentService deploymentService, SshService sshService) {
public RouterController(RouterService service, DeploymentService deploymentService, SshService sshService, IpAllocationService ipAllocationService, OpenVpnService openVpnService) {
this.service = service;
this.deploymentService = deploymentService;
this.sshService = sshService;
this.ipAllocationService = ipAllocationService;
this.openVpnService = openVpnService;
}
@GetMapping
@@ -61,20 +67,22 @@ public class RouterController {
try {
service.forceStatus(id, RouterStatus.PROVISIONING);
var result = sshService.executeOnConfiguredVps(
"echo 'Provisioning router: " + router.getName() + "' && whoami && hostname"
);
var allocation = ipAllocationService.findByRouterId(id);
if (result.exitCode() != 0) {
throw new IllegalStateException(result.stderr());
}
String command = openVpnService.buildProvisionCommand(
allocation.getClientName(),
allocation.getLanSubnet(),
allocation.getVpnIp()
);
var finishedDeployment = deploymentService.finishSuccess(
deployment,
result.stdout()
"DRY RUN ONLY. Would execute: " + command
);
service.forceStatus(id, RouterStatus.REMOVING);
// Keep this as PROVISIONING? No — dry run succeeded but real provision did not happen.
// So we should NOT mark as PROVISIONED yet.
service.forceStatus(id, RouterStatus.PENDING);
return DeploymentResponse.from(finishedDeployment);
} catch (Exception exception) {