/** * POST TITLE: Garden.io for Java Development Environments * * Complete implementation of Garden.io for local Kubernetes development with Java microservices */ import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.KubernetesClientBuilder; import io.fabric8.kubernetes.api.model.*; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import java.io.*; import java.nio.file.*; import java.util.*; import java.util.concurrent.*; import java.net.http.HttpClient; import java.net.http.HttpRequest; import java.net.http.HttpResponse; import java.net.URI; public class GardenIoJavaIntegration { /** * Garden Configuration Manager */ public static class GardenConfigManager { private final Path projectRoot; private final ObjectMapper yamlMapper; public GardenConfigManager(Path projectRoot) { this.projectRoot = projectRoot; this.yamlMapper = new ObjectMapper(new YAMLFactory()); } /** * Garden Project Configuration */ public static class GardenProjectConfig { private String apiVersion = "garden.io/v0"; private String kind = "Project"; private String name; private Map<String, Object> environments; private Map<String, Object> providers; public GardenProjectConfig(String name) { this.name = name; this.environments = new HashMap<>(); this.providers = new HashMap<>(); initializeDefaults(); } private void initializeDefaults() { // Default environments environments.put("default", Map.of( "variables", Map.of( "environment", "local", "cluster", "local-kubernetes" ) )); // Default providers providers.put("local-kubernetes", Map.of( "name", "local-kubernetes", "context", "docker-desktop", "defaultHostname", "localhost", "setupIngressController", "nginx" )); } // Builder methods public GardenProjectConfig withEnvironment(String name, Map<String, Object> config) { environments.put(name, config); return this; } public GardenProjectConfig withProvider(String name, Map<String, Object> config) { providers.put(name, config); return this; } public GardenProjectConfig withVariable(String key, Object value) { @SuppressWarnings("unchecked") Map<String, Object> defaultEnv = (Map<String, Object>) environments.get("default"); @SuppressWarnings("unchecked") Map<String, Object> variables = (Map<String, Object>) defaultEnv.get("variables"); variables.put(key, value); return this; } // Getters public String getApiVersion() { return apiVersion; } public String getKind() { return kind; } public String getName() { return name; } public Map<String, Object> getEnvironments() { return environments; } public Map<String, Object> getProviders() { return providers; } } /** * Garden Module Configuration for Java Services */ public static class GardenModuleConfig { private String apiVersion = "garden.io/v0"; private String kind = "Module"; private String name; private String type = "container"; private String description; private Map<String, Object> services; private Map<String, Object> tests; private Map<String, Object> tasks; private Map<String, Object> build; private Map<String, Object> spec; public GardenModuleConfig(String name) { this.name = name; this.services = new HashMap<>(); this.tests = new HashMap<>(); this.tasks = new HashMap<>(); this.build = new HashMap<>(); this.spec = new HashMap<>(); } // Builder methods public GardenModuleConfig withService(ServiceConfig service) { services.put(service.getName(), service.toMap()); return this; } public GardenModuleConfig withTest(TestConfig test) { tests.put(test.getName(), test.toMap()); return this; } public GardenModuleConfig withTask(TaskConfig task) { tasks.put(task.getName(), task.toMap()); return this; } public GardenModuleConfig withBuildConfig(BuildConfig buildConfig) { this.build.putAll(buildConfig.toMap()); return this; } public GardenModuleConfig withSpec(Map<String, Object> spec) { this.spec.putAll(spec); return this; } // Getters public String getApiVersion() { return apiVersion; } public String getKind() { return kind; } public String getName() { return name; } public String getType() { return type; } public String getDescription() { return description; } public void setDescription(String description) { this.description = description; } public Map<String, Object> getServices() { return services; } public Map<String, Object> getTests() { return tests; } public Map<String, Object> getTasks() { return tasks; } public Map<String, Object> getBuild() { return build; } public Map<String, Object> getSpec() { return spec; } } public static class ServiceConfig { private String name; private Map<String, Object> config; public ServiceConfig(String name) { this.name = name; this.config = new HashMap<>(); } public ServiceConfig withAnnotations(Map<String, String> annotations) { config.put("annotations", annotations); return this; } public ServiceConfig withPorts(List<PortConfig> ports) { config.put("ports", ports.stream().map(PortConfig::toMap).toList()); return this; } public ServiceConfig withDependencies(List<String> dependencies) { config.put("dependencies", dependencies); return this; } public ServiceConfig withHealthCheck(HealthCheckConfig healthCheck) { config.put("healthCheck", healthCheck.toMap()); return this; } public ServiceConfig withIngress(IngressConfig ingress) { config.put("ingresses", List.of(ingress.toMap())); return this; } public Map<String, Object> toMap() { return config; } public String getName() { return name; } } public static class PortConfig { private String name; private int port; private String protocol = "TCP"; private String serviceProtocol = "http"; public PortConfig(String name, int port) { this.name = name; this.port = port; } public PortConfig withProtocol(String protocol) { this.protocol = protocol; return this; } public PortConfig withServiceProtocol(String serviceProtocol) { this.serviceProtocol = serviceProtocol; return this; } public Map<String, Object> toMap() { Map<String, Object> map = new HashMap<>(); map.put("name", name); map.put("port", port); map.put("protocol", protocol); map.put("serviceProtocol", serviceProtocol); return map; } } public static class HealthCheckConfig { private String httpGetPath = "/actuator/health"; private int httpGetPort = 8080; private int initialDelaySeconds = 10; private int periodSeconds = 5; private int timeoutSeconds = 3; private int successThreshold = 1; private int failureThreshold = 3; public HealthCheckConfig withHttpGetPath(String path) { this.httpGetPath = path; return this; } public HealthCheckConfig withPort(int port) { this.httpGetPort = port; return this; } public Map<String, Object> toMap() { Map<String, Object> map = new HashMap<>(); map.put("httpGet", Map.of( "path", httpGetPath, "port", httpGetPort )); map.put("initialDelaySeconds", initialDelaySeconds); map.put("periodSeconds", periodSeconds); map.put("timeoutSeconds", timeoutSeconds); map.put("successThreshold", successThreshold); map.put("failureThreshold", failureThreshold); return map; } } public static class IngressConfig { private String path; private int port; public IngressConfig(String path, int port) { this.path = path; this.port = port; } public Map<String, Object> toMap() { return Map.of( "path", path, "port", port ); } } public static class TestConfig { private String name; private Map<String, Object> config; public TestConfig(String name) { this.name = name; this.config = new HashMap<>(); } public TestConfig withCommand(List<String> command) { config.put("command", command); return this; } public TestConfig withArgs(List<String> args) { config.put("args", args); return this; } public TestConfig withDependencies(List<String> dependencies) { config.put("dependencies", dependencies); return this; } public TestConfig withEnv(Map<String, String> env) { config.put("env", env); return this; } public TestConfig withTimeout(int timeout) { config.put("timeout", timeout); return this; } public Map<String, Object> toMap() { return config; } public String getName() { return name; } } public static class TaskConfig { private String name; private Map<String, Object> config; public TaskConfig(String name) { this.name = name; this.config = new HashMap<>(); } public TaskConfig withCommand(List<String> command) { config.put("command", command); return this; } public TaskConfig withArgs(List<String> args) { config.put("args", args); return this; } public TaskConfig withDependencies(List<String> dependencies) { config.put("dependencies", dependencies); return this; } public TaskConfig withEnv(Map<String, String> env) { config.put("env", env); return this; } public Map<String, Object> toMap() { return config; } public String getName() { return name; } } public static class BuildConfig { private Map<String, Object> config; public BuildConfig() { this.config = new HashMap<>(); } public BuildConfig withDockerfile(String dockerfile) { config.put("dockerfile", dockerfile); return this; } public BuildConfig withBuildArgs(Map<String, String> buildArgs) { config.put("buildArgs", buildArgs); return this; } public BuildConfig withTarget(String target) { config.put("target", target); return this; } public BuildConfig withExtraFlags(List<String> extraFlags) { config.put("extraFlags", extraFlags); return this; } public Map<String, Object> toMap() { return config; } } /** * Create Garden project configuration */ public void createProjectConfig(GardenProjectConfig config) throws IOException { Path gardenDir = projectRoot.resolve(".garden"); Files.createDirectories(gardenDir); Path configFile = gardenDir.resolve("project.garden.yml"); yamlMapper.writeValue(configFile.toFile(), config); System.out.println("✅ Created project config: " + configFile); } /** * Create Garden module configuration */ public void createModuleConfig(String moduleName, GardenModuleConfig config) throws IOException { Path moduleDir = projectRoot.resolve(moduleName); Files.createDirectories(moduleDir); Path configFile = moduleDir.resolve("garden.yml"); yamlMapper.writeValue(configFile.toFile(), config); System.out.println("✅ Created module config: " + configFile); } /** * Generate sample configurations for common Java patterns */ public void generateSampleJavaProject(String projectName) throws IOException { // Project config GardenProjectConfig projectConfig = new GardenProjectConfig(projectName) .withVariable("JAVA_VERSION", "17") .withVariable("MAVEN_VERSION", "3.8.4") .withVariable("APP_PORT", "8080"); createProjectConfig(projectConfig); // Spring Boot service generateSpringBootService("user-service", 8080); generateSpringBootService("order-service", 8081); generateDatabaseService("postgres-db"); generateReactFrontend("web-frontend"); // Dockerfile templates generateDockerfileTemplates(); } private void generateSpringBootService(String serviceName, int port) throws IOException { GardenModuleConfig config = new GardenModuleConfig(serviceName) .setDescription("Spring Boot " + serviceName) .withBuildConfig(new BuildConfig() .withDockerfile("Dockerfile") .withBuildArgs(Map.of( "JAVA_VERSION", "${var.JAVA_VERSION}", "MAVEN_VERSION", "${var.MAVEN_VERSION}" )) ) .withService(new ServiceConfig(serviceName) .withPorts(List.of( new PortConfig("http", port) )) .withHealthCheck(new HealthCheckConfig() .withPort(port) .withHttpGetPath("/actuator/health") ) .withIngress(new IngressConfig("/", port)) .withAnnotations(Map.of( "prometheus.io/scrape", "true", "prometheus.io/port", String.valueOf(port) )) ) .withTest(new TestConfig("unit-test") .withCommand(List.of("./mvnw", "test")) .withEnv(Map.of("TEST_ENV", "true")) .withTimeout(300) ) .withTask(new TaskConfig("migrate-db") .withCommand(List.of("./mvnw", "spring-boot:run")) .withArgs(List.of("-Dspring-boot.run.arguments=--db.migration=true")) .withDependencies(List.of("postgres-db")) ); createModuleConfig(serviceName, config); } private void generateDatabaseService(String serviceName) throws IOException { GardenModuleConfig config = new GardenModuleConfig(serviceName) .setDescription("PostgreSQL Database") .withBuildConfig(new BuildConfig() .withDockerfile("Dockerfile.db") ) .withService(new ServiceConfig(serviceName) .withPorts(List.of( new PortConfig("postgres", 5432) )) .withHealthCheck(new HealthCheckConfig() .withPort(5432) .withHttpGetPath("/") // PostgreSQL health check would be different ) .withAnnotations(Map.of( "backup.enabled", "true" )) ) .withSpec(Map.of( "image", "postgres:13", "env", Map.of( "POSTGRES_DB", "appdb", "POSTGRES_USER", "appuser", "POSTGRES_PASSWORD", "apppass" ), "volumes", List.of( Map.of("name", "postgres-data", "containerPath", "/var/lib/postgresql/data") ) )); createModuleConfig(serviceName, config); } private void generateReactFrontend(String serviceName) throws IOException { GardenModuleConfig config = new GardenModuleConfig(serviceName) .setDescription("React Frontend") .withBuildConfig(new BuildConfig() .withDockerfile("Dockerfile.frontend") ) .withService(new ServiceConfig(serviceName) .withPorts(List.of( new PortConfig("http", 3000) )) .withHealthCheck(new HealthCheckConfig() .withPort(3000) .withHttpGetPath("/") ) .withIngress(new IngressConfig("/", 3000)) .withDependencies(List.of("user-service", "order-service")) ) .withTest(new TestConfig("unit-test") .withCommand(List.of("npm", "test")) .withEnv(Map.of("CI", "true")) ); createModuleConfig(serviceName, config); } private void generateDockerfileTemplates() throws IOException { // Spring Boot Dockerfile String springBootDockerfile = """ # Multi-stage build for Spring Boot FROM maven:${MAVEN_VERSION}-openjdk-${JAVA_VERSION} AS builder WORKDIR /app COPY pom.xml . COPY src ./src RUN mvn clean package -DskipTests FROM openjdk:${JAVA_VERSION}-jre-slim WORKDIR /app COPY --from=builder /app/target/*.jar app.jar EXPOSE ${var.APP_PORT} ENTRYPOINT ["java", "-jar", "app.jar"] """; Files.writeString(projectRoot.resolve("user-service/Dockerfile"), springBootDockerfile); Files.writeString(projectRoot.resolve("order-service/Dockerfile"), springBootDockerfile); // PostgreSQL Dockerfile String postgresDockerfile = """ FROM postgres:13 COPY init.sql /docker-entrypoint-initdb.d/ EXPOSE 5432 """; Files.writeString(projectRoot.resolve("postgres-db/Dockerfile.db"), postgresDockerfile); // React Dockerfile String reactDockerfile = """ FROM node:16-alpine AS builder WORKDIR /app COPY package*.json ./ RUN npm ci COPY . . RUN npm run build FROM nginx:alpine COPY --from=builder /app/build /usr/share/nginx/html EXPOSE 3000 """; Files.writeString(projectRoot.resolve("web-frontend/Dockerfile.frontend"), reactDockerfile); } } /** * Garden CLI Wrapper */ public static class GardenCLIWrapper { private final Path projectRoot; private final ProcessBuilder processBuilder; public GardenCLIWrapper(Path projectRoot) { this.projectRoot = projectRoot; this.processBuilder = new ProcessBuilder(); this.processBuilder.directory(projectRoot.toFile()); this.processBuilder.redirectErrorStream(true); } /** * Execute Garden command */ public CommandResult executeCommand(String... command) { try { List<String> fullCommand = new ArrayList<>(); fullCommand.add("garden"); fullCommand.addAll(Arrays.asList(command)); processBuilder.command(fullCommand); Process process = processBuilder.start(); // Capture output StringBuilder output = new StringBuilder(); try (BufferedReader reader = new BufferedReader( new InputStreamReader(process.getInputStream()))) { String line; while ((line = reader.readLine()) != null) { output.append(line).append("\\n"); System.out.println("[Garden] " + line); } } int exitCode = process.waitFor(); return new CommandResult(exitCode, output.toString()); } catch (Exception e) { throw new RuntimeException("Garden command failed: " + e.getMessage(), e); } } /** * Common Garden operations */ public CommandResult deploy() { System.out.println("🚀 Deploying with Garden..."); return executeCommand("deploy"); } public CommandResult dev() { System.out.println("🔧 Starting Garden in dev mode..."); return executeCommand("dev"); } public CommandResult test() { System.out.println "🧪 Running tests with Garden..."); return executeCommand("test"); } public CommandResult status() { System.out.println("📊 Getting Garden status..."); return executeCommand("get", "status"); } public CommandResult logs(String service) { System.out.println("📋 Getting logs for: " + service); return executeCommand("logs", service); } public CommandResult delete() { System.out.println("🧹 Cleaning up Garden environment..."); return executeCommand("delete", "environment"); } public static class CommandResult { private final int exitCode; private final String output; public CommandResult(int exitCode, String output) { this.exitCode = exitCode; this.output = output; } public boolean isSuccess() { return exitCode == 0; } public int getExitCode() { return exitCode; } public String getOutput() { return output; } } } /** * Development Environment Manager */ public static class DevEnvironmentManager { private final GardenConfigManager configManager; private final GardenCLIWrapper gardenCLI; private final KubernetesClient k8sClient; private final HttpClient httpClient; public DevEnvironmentManager(Path projectRoot) { this.configManager = new GardenConfigManager(projectRoot); this.gardenCLI = new GardenCLIWrapper(projectRoot); this.k8sClient = new KubernetesClientBuilder().build(); this.httpClient = HttpClient.newHttpClient(); } /** * Initialize development environment */ public void initializeEnvironment(String projectName) throws IOException { System.out.println("🎯 Initializing Garden development environment..."); // Generate Garden configurations configManager.generateSampleJavaProject(projectName); // Deploy to local Kubernetes GardenCLIWrapper.CommandResult result = gardenCLI.deploy(); if (!result.isSuccess()) { throw new RuntimeException("Failed to deploy environment: " + result.getOutput()); } System.out.println("✅ Development environment initialized successfully"); } /** * Start development mode with hot-reload */ public void startDevMode() { System.out.println("🔥 Starting Garden dev mode with hot-reload..."); // Start in background thread CompletableFuture.runAsync(() -> { GardenCLIWrapper.CommandResult result = gardenCLI.dev(); if (!result.isSuccess()) { System.err.println("❌ Dev mode failed: " + result.getOutput()); } }); // Monitor services monitorServices(); } /** * Monitor service health */ public void monitorServices() { ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1); scheduler.scheduleAtFixedRate(() -> { try { checkServiceHealth("user-service"); checkServiceHealth("order-service"); checkServiceHealth("web-frontend"); } catch (Exception e) { System.err.println("❌ Health check failed: " + e.getMessage()); } }, 0, 30, TimeUnit.SECONDS); } private void checkServiceHealth(String serviceName) { try { String url = String.format("http://%s.local.demo.garden/actuator/health", serviceName); HttpRequest request = HttpRequest.newBuilder() .uri(URI.create(url)) .GET() .build(); HttpResponse<String> response = httpClient.send(request, HttpResponse.BodyHandlers.ofString()); if (response.statusCode() == 200) { System.out.println("✅ " + serviceName + " is healthy"); } else { System.out.println("⚠️ " + serviceName + " health check failed: " + response.statusCode()); } } catch (Exception e) { System.out.println("❌ " + serviceName + " health check error: " + e.getMessage()); } } /** * Run tests */ public void runTests() { System.out.println("🧪 Running test suite..."); GardenCLIWrapper.CommandResult result = gardenCLI.test(); if (result.isSuccess()) { System.out.println("✅ All tests passed"); } else { System.out.println("❌ Tests failed"); } } /** * Get environment status */ public void getStatus() { GardenCLIWrapper.CommandResult result = gardenCLI.status(); System.out.println("📊 Environment Status:"); System.out.println(result.getOutput()); } /** * Clean up environment */ public void cleanup() { System.out.println("🧹 Cleaning up development environment..."); GardenCLIWrapper.CommandResult result = gardenCLI.delete(); if (result.isSuccess()) { System.out.println("✅ Environment cleaned up successfully"); } else { System.out.println("⚠️ Cleanup completed with warnings: " + result.getOutput()); } } /** * Port-forward to service */ public void portForward(String service, int localPort, int remotePort) { try { ProcessBuilder pb = new ProcessBuilder( "kubectl", "port-forward", "svc/" + service, localPort + ":" + remotePort ); pb.directory(gardenCLI.projectRoot.toFile()); Process process = pb.start(); // Monitor port-forward in background CompletableFuture.runAsync(() -> { try { process.waitFor(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }); System.out.println("🔗 Port-forward established: localhost:" + localPort + " -> " + service + ":" + remotePort); } catch (Exception e) { throw new RuntimeException("Port-forward failed: " + e.getMessage(), e); } } } /** * Spring Boot Integration */ @SpringBootApplication public static class GardenDevelopmentApplication { @Value("${garden.project.root:./}") private String projectRoot; @Bean public DevEnvironmentManager devEnvironmentManager() { return new DevEnvironmentManager(Paths.get(projectRoot)); } @Bean public GardenConfigManager gardenConfigManager() { return new GardenConfigManager(Paths.get(projectRoot)); } } @RestController @RequestMapping("/api/garden") public static class GardenController { private final DevEnvironmentManager envManager; public GardenController(DevEnvironmentManager envManager) { this.envManager = envManager; } @PostMapping("/init") public ResponseEntity<String> initializeEnvironment(@RequestParam String projectName) { try { envManager.initializeEnvironment(projectName); return ResponseEntity.ok("Development environment initialized successfully"); } catch (Exception e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body("Failed to initialize environment: " + e.getMessage()); } } @PostMapping("/dev") public ResponseEntity<String> startDevMode() { try { envManager.startDevMode(); return ResponseEntity.ok("Development mode started"); } catch (Exception e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body("Failed to start dev mode: " + e.getMessage()); } } @PostMapping("/test") public ResponseEntity<String> runTests() { try { envManager.runTests(); return ResponseEntity.ok("Tests completed"); } catch (Exception e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body("Tests failed: " + e.getMessage()); } } @GetMapping("/status") public ResponseEntity<String> getStatus() { try { envManager.getStatus(); return ResponseEntity.ok("Status retrieved"); } catch (Exception e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body("Failed to get status: " + e.getMessage()); } } @PostMapping("/cleanup") public ResponseEntity<String> cleanup() { try { envManager.cleanup(); return ResponseEntity.ok("Environment cleaned up"); } catch (Exception e) { return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) .body("Cleanup failed: " + e.getMessage()); } } } /** * Demo and Usage Examples */ public static void main(String[] args) { System.out.println("🌿 Garden.io Java Development Environment"); System.out.println("=========================================\n"); try { Path tempProjectDir = Files.createTempDirectory("garden-demo"); System.out.println("📁 Project directory: " + tempProjectDir); DevEnvironmentManager envManager = new DevEnvironmentManager(tempProjectDir); // Demo 1: Initialize environment System.out.println("1. 🎯 Initializing Development Environment"); envManager.initializeEnvironment("java-microservices-demo"); // Demo 2: Check status System.out.println("\n2. 📊 Checking Environment Status"); envManager.getStatus(); // Demo 3: Run tests System.out.println("\n3. 🧪 Running Tests"); envManager.runTests(); // Demo 4: Start dev mode (in background) System.out.println("\n4. 🔥 Starting Development Mode"); CompletableFuture.runAsync(() -> { try { envManager.startDevMode(); } catch (Exception e) { System.err.println("Dev mode error: " + e.getMessage()); } }); // Keep running for a bit to demonstrate System.out.println("\n⏳ Development environment is running..."); Thread.sleep(30000); // Demo 5: Cleanup System.out.println("\n5. 🧹 Cleaning Up"); envManager.cleanup(); // Clean up temp directory deleteDirectory(tempProjectDir); System.out.println("\n✅ Garden.io Demo Completed Successfully"); } catch (Exception e) { System.err.println("❌ Demo failed: " + e.getMessage()); e.printStackTrace(); } } private static void deleteDirectory(Path path) throws IOException { Files.walk(path) .sorted(Comparator.reverseOrder()) .map(Path::toFile) .forEach(File::delete); } } Maven Dependencies
<!-- pom.xml --> <dependencies> <!-- Kubernetes Client --> <dependency> <groupId>io.fabric8</groupId> <artifactId>kubernetes-client</artifactId> <version>6.8.0</version> </dependency> <!-- Spring Boot --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> <version>2.7.0</version> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> <version>2.7.0</version> </dependency> <!-- YAML Processing --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.14.2</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.dataformat</groupId> <artifactId>jackson-dataformat-yaml</artifactId> <version>2.14.2</version> </dependency> <!-- HTTP Client --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>5.3.20</version> </dependency> </dependencies>
Sample Garden Configuration Files
project.garden.yml
apiVersion: garden.io/v0 kind: Project name: java-microservices-demo environments: default: variables: environment: local cluster: local-kubernetes JAVA_VERSION: 17 MAVEN_VERSION: 3.8.4 APP_PORT: 8080 providers: local-kubernetes: name: local-kubernetes context: docker-desktop defaultHostname: localhost setupIngressController: nginx
user-service/garden.yml
apiVersion: garden.io/v0 kind: Module name: user-service type: container description: Spring Boot User Service build: dockerfile: Dockerfile buildArgs: JAVA_VERSION: ${var.JAVA_VERSION} MAVEN_VERSION: ${var.MAVEN_VERSION} services: user-service: ports: - name: http port: 8080 protocol: TCP serviceProtocol: http healthCheck: httpGet: path: /actuator/health port: 8080 initialDelaySeconds: 10 periodSeconds: 5 ingresses: - path: / port: 8080 annotations: prometheus.io/scrape: "true" prometheus.io/port: "8080" tests: unit-test: command: ["./mvnw", "test"] env: TEST_ENV: "true" timeout: 300 tasks: migrate-db: command: ["./mvnw", "spring-boot:run"] args: ["-Dspring-boot.run.arguments=--db.migration=true"] dependencies: ["postgres-db"] Dockerfile for Spring Boot Service
# Multi-stage build for Spring Boot FROM maven:3.8.4-openjdk-17 AS builder WORKDIR /app COPY pom.xml . COPY src ./src RUN mvn clean package -DskipTests FROM openjdk:17-jre-slim WORKDIR /app COPY --from=builder /app/target/*.jar app.jar EXPOSE 8080 ENTRYPOINT ["java", "-jar", "app.jar"]
Usage Commands
# Initialize project garden init # Deploy all services garden deploy # Start development mode with hot-reload garden dev # Run tests garden test # Get status garden get status # View logs garden logs user-service # Clean up garden delete environment
This Garden.io implementation provides:
- Complete Garden Configuration generation for Java microservices
- Multi-service Development Environment with Spring Boot, databases, and frontends
- Hot-reload Development Mode for rapid iteration
- Automated Testing integration
- Health Monitoring and status checks
- Kubernetes-native Development without complex kubectl commands
- Dockerfile Templates for common Java patterns
- Spring Boot Integration for easy adoption
- Environment Management with cleanup capabilities
- Port-forwarding for local development access
The solution enables Java developers to work with complex microservices architectures locally with the same tooling and configurations used in production.