diff --git a/cameleer-server-core/src/main/java/com/cameleer/server/core/runtime/DirtyStateCalculator.java b/cameleer-server-core/src/main/java/com/cameleer/server/core/runtime/DirtyStateCalculator.java index 5239922f..9010bf5a 100644 --- a/cameleer-server-core/src/main/java/com/cameleer/server/core/runtime/DirtyStateCalculator.java +++ b/cameleer-server-core/src/main/java/com/cameleer/server/core/runtime/DirtyStateCalculator.java @@ -23,8 +23,13 @@ import java.util.UUID; */ public class DirtyStateCalculator { + // Live-pushed fields are excluded from the deploy diff: changes to them take effect + // via SSE config-update without a redeploy, so they are not "pending deploy" when they + // differ from the last successful deployment snapshot. See ui/rules: the Traces & Taps + // and Route Recording tabs apply with ?apply=live and "never mark dirty". private static final Set AGENT_CONFIG_IGNORED_KEYS = Set.of( - "version", "updatedAt", "updatedBy", "environment", "application" + "version", "updatedAt", "updatedBy", "environment", "application", + "taps", "tapVersion", "tracedProcessors", "routeRecording" ); private final ObjectMapper mapper; diff --git a/cameleer-server-core/src/test/java/com/cameleer/server/core/runtime/DirtyStateCalculatorTest.java b/cameleer-server-core/src/test/java/com/cameleer/server/core/runtime/DirtyStateCalculatorTest.java index 600e5a40..fa035724 100644 --- a/cameleer-server-core/src/test/java/com/cameleer/server/core/runtime/DirtyStateCalculatorTest.java +++ b/cameleer-server-core/src/test/java/com/cameleer/server/core/runtime/DirtyStateCalculatorTest.java @@ -5,6 +5,7 @@ import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule; import org.junit.jupiter.api.Test; +import java.util.List; import java.util.Map; import java.util.UUID; @@ -114,9 +115,9 @@ class DirtyStateCalculatorTest { DirtyStateCalculator calc = CALC; ApplicationConfig deployed = new ApplicationConfig(); - deployed.setTracedProcessors(Map.of("proc-1", "DEBUG")); + deployed.setSensitiveKeys(List.of("password", "token")); ApplicationConfig desired = new ApplicationConfig(); - desired.setTracedProcessors(Map.of("proc-1", "TRACE")); + desired.setSensitiveKeys(List.of("password", "token", "secret")); UUID jarId = UUID.randomUUID(); DeploymentConfigSnapshot snap = new DeploymentConfigSnapshot(jarId, deployed, Map.of(), null); @@ -124,7 +125,29 @@ class DirtyStateCalculatorTest { assertThat(result.dirty()).isTrue(); assertThat(result.differences()).extracting(DirtyStateResult.Difference::field) - .contains("agentConfig.tracedProcessors.proc-1"); + .anyMatch(f -> f.startsWith("agentConfig.sensitiveKeys")); + } + + @Test + void livePushedFields_doNotMarkDirty() { + // Taps, tracedProcessors, and routeRecording apply via live SSE push (never redeploy), + // so they must not appear as "pending deploy" when they differ from the last deploy snapshot. + ApplicationConfig deployed = new ApplicationConfig(); + deployed.setTracedProcessors(Map.of("proc-1", "DEBUG")); + deployed.setRouteRecording(Map.of("route-a", true)); + deployed.setTapVersion(1); + + ApplicationConfig desired = new ApplicationConfig(); + desired.setTracedProcessors(Map.of("proc-1", "TRACE", "proc-2", "DEBUG")); + desired.setRouteRecording(Map.of("route-a", false, "route-b", true)); + desired.setTapVersion(5); + + UUID jarId = UUID.randomUUID(); + DeploymentConfigSnapshot snap = new DeploymentConfigSnapshot(jarId, deployed, Map.of(), null); + DirtyStateResult result = CALC.compute(jarId, desired, Map.of(), snap); + + assertThat(result.dirty()).isFalse(); + assertThat(result.differences()).isEmpty(); } @Test