From fef0239b1d879f5e5c43d4cbf42fe9f9c4b6c0a8 Mon Sep 17 00:00:00 2001 From: hsiegeln <37154749+hsiegeln@users.noreply.github.com> Date: Wed, 8 Apr 2026 20:16:57 +0200 Subject: [PATCH] feat: update PostgresDeploymentRepository for orchestration columns Co-Authored-By: Claude Sonnet 4.6 --- .../server/app/config/RuntimeBeanConfig.java | 4 +- .../storage/PostgresDeploymentRepository.java | 65 +++++++++++++++++-- 2 files changed, 61 insertions(+), 8 deletions(-) diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/RuntimeBeanConfig.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/RuntimeBeanConfig.java index d24ca5b8..e9f870c3 100644 --- a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/RuntimeBeanConfig.java +++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/config/RuntimeBeanConfig.java @@ -44,8 +44,8 @@ public class RuntimeBeanConfig { } @Bean - public DeploymentRepository deploymentRepository(JdbcTemplate jdbc) { - return new PostgresDeploymentRepository(jdbc); + public DeploymentRepository deploymentRepository(JdbcTemplate jdbc, ObjectMapper objectMapper) { + return new PostgresDeploymentRepository(jdbc, objectMapper); } @Bean diff --git a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/PostgresDeploymentRepository.java b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/PostgresDeploymentRepository.java index 1aad0e38..4efc89ec 100644 --- a/cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/PostgresDeploymentRepository.java +++ b/cameleer3-server-app/src/main/java/com/cameleer3/server/app/storage/PostgresDeploymentRepository.java @@ -3,6 +3,8 @@ package com.cameleer3.server.app.storage; import com.cameleer3.server.core.runtime.Deployment; import com.cameleer3.server.core.runtime.DeploymentRepository; import com.cameleer3.server.core.runtime.DeploymentStatus; +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.jdbc.core.JdbcTemplate; import java.sql.ResultSet; @@ -10,35 +12,43 @@ import java.sql.SQLException; import java.sql.Timestamp; import java.time.Instant; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.UUID; public class PostgresDeploymentRepository implements DeploymentRepository { - private final JdbcTemplate jdbc; + private static final String SELECT_COLS = + "id, app_id, app_version_id, environment_id, status, target_state, deployment_strategy, " + + "replica_states, deploy_stage, container_id, container_name, error_message, " + + "deployed_at, stopped_at, created_at"; - public PostgresDeploymentRepository(JdbcTemplate jdbc) { + private final JdbcTemplate jdbc; + private final ObjectMapper objectMapper; + + public PostgresDeploymentRepository(JdbcTemplate jdbc, ObjectMapper objectMapper) { this.jdbc = jdbc; + this.objectMapper = objectMapper; } @Override public List findByAppId(UUID appId) { return jdbc.query( - "SELECT id, app_id, app_version_id, environment_id, status, container_id, container_name, error_message, deployed_at, stopped_at, created_at FROM deployments WHERE app_id = ? ORDER BY created_at DESC", + "SELECT " + SELECT_COLS + " FROM deployments WHERE app_id = ? ORDER BY created_at DESC", (rs, rowNum) -> mapRow(rs), appId); } @Override public List findByEnvironmentId(UUID environmentId) { return jdbc.query( - "SELECT id, app_id, app_version_id, environment_id, status, container_id, container_name, error_message, deployed_at, stopped_at, created_at FROM deployments WHERE environment_id = ? ORDER BY created_at DESC", + "SELECT " + SELECT_COLS + " FROM deployments WHERE environment_id = ? ORDER BY created_at DESC", (rs, rowNum) -> mapRow(rs), environmentId); } @Override public Optional findById(UUID id) { var results = jdbc.query( - "SELECT id, app_id, app_version_id, environment_id, status, container_id, container_name, error_message, deployed_at, stopped_at, created_at FROM deployments WHERE id = ?", + "SELECT " + SELECT_COLS + " FROM deployments WHERE id = ?", (rs, rowNum) -> mapRow(rs), id); return results.isEmpty() ? Optional.empty() : Optional.of(results.get(0)); } @@ -46,7 +56,8 @@ public class PostgresDeploymentRepository implements DeploymentRepository { @Override public Optional findActiveByAppIdAndEnvironmentId(UUID appId, UUID environmentId) { var results = jdbc.query( - "SELECT id, app_id, app_version_id, environment_id, status, container_id, container_name, error_message, deployed_at, stopped_at, created_at FROM deployments WHERE app_id = ? AND environment_id = ? AND status IN ('STARTING', 'RUNNING') ORDER BY created_at DESC LIMIT 1", + "SELECT " + SELECT_COLS + " FROM deployments WHERE app_id = ? AND environment_id = ? " + + "AND status IN ('STARTING', 'RUNNING', 'DEGRADED') ORDER BY created_at DESC LIMIT 1", (rs, rowNum) -> mapRow(rs), appId, environmentId); return results.isEmpty() ? Optional.empty() : Optional.of(results.get(0)); } @@ -75,15 +86,57 @@ public class PostgresDeploymentRepository implements DeploymentRepository { jdbc.update("UPDATE deployments SET stopped_at = now() WHERE id = ?", id); } + public void updateReplicaStates(UUID id, List> replicaStates) { + try { + String json = objectMapper.writeValueAsString(replicaStates); + jdbc.update("UPDATE deployments SET replica_states = ?::jsonb WHERE id = ?", json, id); + } catch (Exception e) { + throw new RuntimeException("Failed to serialize replicaStates", e); + } + } + + public void updateDeployStage(UUID id, String stage) { + jdbc.update("UPDATE deployments SET deploy_stage = ? WHERE id = ?", stage, id); + } + + public void updateTargetState(UUID id, String targetState) { + jdbc.update("UPDATE deployments SET target_state = ? WHERE id = ?", targetState, id); + } + + public void updateDeploymentStrategy(UUID id, String strategy) { + jdbc.update("UPDATE deployments SET deployment_strategy = ? WHERE id = ?", strategy, id); + } + + public Optional findByContainerId(String containerId) { + var results = jdbc.query( + "SELECT " + SELECT_COLS + " FROM deployments WHERE replica_states::text LIKE ? " + + "AND status IN ('STARTING', 'RUNNING', 'DEGRADED') ORDER BY created_at DESC LIMIT 1", + (rs, rowNum) -> mapRow(rs), "%" + containerId + "%"); + return results.isEmpty() ? Optional.empty() : Optional.of(results.get(0)); + } + private Deployment mapRow(ResultSet rs) throws SQLException { Timestamp deployedAt = rs.getTimestamp("deployed_at"); Timestamp stoppedAt = rs.getTimestamp("stopped_at"); + String replicaStatesJson = rs.getString("replica_states"); + List> replicaStates = null; + if (replicaStatesJson != null) { + try { + replicaStates = objectMapper.readValue(replicaStatesJson, new TypeReference<>() {}); + } catch (Exception e) { + throw new SQLException("Failed to deserialize replica_states", e); + } + } return new Deployment( UUID.fromString(rs.getString("id")), UUID.fromString(rs.getString("app_id")), UUID.fromString(rs.getString("app_version_id")), UUID.fromString(rs.getString("environment_id")), DeploymentStatus.valueOf(rs.getString("status")), + rs.getString("target_state"), + rs.getString("deployment_strategy"), + replicaStates, + rs.getString("deploy_stage"), rs.getString("container_id"), rs.getString("container_name"), rs.getString("error_message"),