LocalEmulatorPaths.java

package com.cloudforge.core.local;

import java.nio.file.Files;
import java.nio.file.Path;

/**
 * Resolves on-disk paths used by local emulator runtimes.
 */
public final class LocalEmulatorPaths {

    private LocalEmulatorPaths() {
    }

    /** Resolve persisted LocalStack data directory (matches former compose bind mount). */
    public static Path localStackVolumeDir() {
        return localStackVolumeDir(Path.of("").toAbsolutePath().normalize());
    }

    public static Path localStackVolumeDir(Path workingDirectory) {
        String configured = System.getenv(LocalEmulatorDefaults.LOCALSTACK_VOLUME_DIR_ENV);
        if (configured != null && !configured.isBlank()) {
            return Path.of(configured);
        }
        Path repoDefault = workingDirectory.resolve(LocalEmulatorDefaults.LOCALSTACK_VOLUME_DIR_NAME);
        if (Files.exists(repoDefault) || Files.exists(workingDirectory.resolve("pom.xml"))) {
            return repoDefault;
        }
        return Path.of(
            System.getProperty("user.home"),
            ".cloudforge",
            LocalEmulatorDefaults.LOCALSTACK_VOLUME_DIR_NAME);
    }

    public static String dockerSocketMount() {
        return dockerSocketHostPath() + ":/var/run/docker.sock";
    }

    /**
     * Host-side path of the Docker socket, honoring {@link LocalEmulatorDefaults#DOCKER_SOCKET_ENV}
     * for non-default setups (Colima, Rancher Desktop, etc). Just the source half of
     * {@link #dockerSocketMount()} — for callers building an ECS task's {@code Host.SourcePath}
     * directly (e.g. giving CloudForge Manager's own container the same Docker access this
     * process already grants LocalStack's own container) rather than a docker-CLI {@code -v} arg.
     */
    public static String dockerSocketHostPath() {
        return System.getenv().getOrDefault(
            LocalEmulatorDefaults.DOCKER_SOCKET_ENV,
            LocalEmulatorDefaults.DEFAULT_DOCKER_SOCKET);
    }

    /** Persisted StackPort endpoint config (endpoints.json) per emulator target. */
    public static Path stackPortVolumeDir(DeploymentTarget target) {
        return stackPortVolumeDir(Path.of("").toAbsolutePath().normalize(), target);
    }

    public static Path stackPortVolumeDir(Path workingDirectory, DeploymentTarget target) {
        String subdir = target == DeploymentTarget.MINISTACK ? "ministack" : "localstack";
        Path repoDefault = workingDirectory
            .resolve(LocalEmulatorDefaults.STACKPORT_VOLUME_DIR_NAME)
            .resolve(subdir);
        if (Files.exists(repoDefault.getParent()) || Files.exists(workingDirectory.resolve("pom.xml"))) {
            return repoDefault;
        }
        return Path.of(
            System.getProperty("user.home"),
            ".cloudforge",
            LocalEmulatorDefaults.STACKPORT_VOLUME_DIR_NAME,
            subdir);
    }

    /** Runtime nginx conf dir ({@code conf.d}, generated vhosts). */
    public static Path emulatorEdgeDir() {
        return emulatorEdgeDir(Path.of("").toAbsolutePath().normalize());
    }

    public static Path emulatorEdgeDir(Path workingDirectory) {
        Path repoDefault = workingDirectory.resolve(LocalEmulatorDefaults.EMULATOR_EDGE_VOLUME_DIR_NAME);
        if (Files.exists(repoDefault)
            || Files.exists(workingDirectory.resolve("pom.xml"))
            || Files.exists(workingDirectory.resolve("docker/emulator-edge"))) {
            return repoDefault;
        }
        Path parent = workingDirectory.getParent();
        if (parent != null) {
            Path parentDefault = parent.resolve(LocalEmulatorDefaults.EMULATOR_EDGE_VOLUME_DIR_NAME);
            if (Files.exists(parent.resolve("docker/emulator-edge"))
                || Files.exists(parent.resolve("pom.xml"))) {
                return parentDefault;
            }
        }
        return Path.of(
            System.getProperty("user.home"),
            ".cloudforge",
            LocalEmulatorDefaults.EMULATOR_EDGE_VOLUME_DIR_NAME);
    }

    /** Prefer repo {@code docker/emulator-edge/nginx.conf}, else classpath extract location. */
    public static Path emulatorEdgeNginxConf(Path workingDirectory) {
        Path[] candidates = {
            workingDirectory.resolve("docker/emulator-edge/nginx.conf"),
            workingDirectory.resolve("../docker/emulator-edge/nginx.conf").normalize(),
            emulatorEdgeDir(workingDirectory).resolve("nginx.conf")
        };
        for (Path candidate : candidates) {
            if (Files.isRegularFile(candidate)) {
                return candidate;
            }
        }
        return emulatorEdgeDir(workingDirectory).resolve("nginx.conf");
    }
}