Garden.io for Java Development Environments
/**
* 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:

  1. Complete Garden Configuration generation for Java microservices
  2. Multi-service Development Environment with Spring Boot, databases, and frontends
  3. Hot-reload Development Mode for rapid iteration
  4. Automated Testing integration
  5. Health Monitoring and status checks
  6. Kubernetes-native Development without complex kubectl commands
  7. Dockerfile Templates for common Java patterns
  8. Spring Boot Integration for easy adoption
  9. Environment Management with cleanup capabilities
  10. 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.

Leave a Reply

Your email address will not be published. Required fields are marked *


Macro Nepal Helper