feat!: scope per-app config and settings by environment
All checks were successful
CI / cleanup-branch (push) Has been skipped
CI / build (push) Successful in 1m27s
CI / docker (push) Successful in 1m10s
CI / deploy-feature (push) Has been skipped
CI / deploy (push) Successful in 1m40s
SonarQube / sonarqube (push) Successful in 4m29s
All checks were successful
CI / cleanup-branch (push) Has been skipped
CI / build (push) Successful in 1m27s
CI / docker (push) Successful in 1m10s
CI / deploy-feature (push) Has been skipped
CI / deploy (push) Successful in 1m40s
SonarQube / sonarqube (push) Successful in 4m29s
BREAKING: wipe dev PostgreSQL before deploying — V1 checksum changes. Agents must now send environmentId on registration (400 if missing). Two tables previously keyed on app name alone caused cross-environment data bleed: writing config for (app=X, env=dev) would overwrite the row used by (app=X, env=prod) agents, and agent startup fetches ignored env entirely. - V1 schema: application_config and app_settings are now PK (app, env). - Repositories: env-keyed finders/saves; env is the authoritative column, stamped on the stored JSON so the row agrees with itself. - ApplicationConfigController.getConfig is dual-mode — AGENT role uses JWT env claim (agents cannot spoof env); non-agent callers provide env via ?environment= query param. - AppSettingsController endpoints now require ?environment=. - SensitiveKeysAdminController fan-out iterates (app, env) slices so each env gets its own merged keys. - DiagramController ingestion stamps env on TaggedDiagram; ClickHouse route_diagrams INSERT + findProcessorRouteMapping are env-scoped. - AgentRegistrationController: environmentId is required on register; removed all "default" fallbacks from register/refresh/heartbeat auto-heal. - UI hooks (useApplicationConfig, useProcessorRouteMapping, useAppSettings, useAllAppSettings, useUpdateAppSettings) take env, wired to useEnvironmentStore at all call sites. - New ConfigEnvIsolationIT covers env-isolation for both repositories. Plan in docs/superpowers/plans/2026-04-16-environment-scoping.md. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4,6 +4,7 @@ import java.time.Instant;
|
||||
|
||||
public record AppSettings(
|
||||
String applicationId,
|
||||
String environment,
|
||||
int slaThresholdMs,
|
||||
double healthErrorWarn,
|
||||
double healthErrorCrit,
|
||||
@@ -12,8 +13,8 @@ public record AppSettings(
|
||||
Instant createdAt,
|
||||
Instant updatedAt) {
|
||||
|
||||
public static AppSettings defaults(String applicationId) {
|
||||
public static AppSettings defaults(String applicationId, String environment) {
|
||||
Instant now = Instant.now();
|
||||
return new AppSettings(applicationId, 300, 1.0, 5.0, 99.0, 95.0, now, now);
|
||||
return new AppSettings(applicationId, environment, 300, 1.0, 5.0, 99.0, 95.0, now, now);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,8 +4,8 @@ import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public interface AppSettingsRepository {
|
||||
Optional<AppSettings> findByApplicationId(String applicationId);
|
||||
List<AppSettings> findAll();
|
||||
Optional<AppSettings> findByApplicationAndEnvironment(String applicationId, String environment);
|
||||
List<AppSettings> findByEnvironment(String environment);
|
||||
AppSettings save(AppSettings settings);
|
||||
void delete(String applicationId);
|
||||
void delete(String applicationId, String environment);
|
||||
}
|
||||
|
||||
@@ -3,9 +3,11 @@ package com.cameleer.server.core.ingestion;
|
||||
import com.cameleer.common.graph.RouteGraph;
|
||||
|
||||
/**
|
||||
* Pairs a {@link RouteGraph} with the authenticated agent identity.
|
||||
* Pairs a {@link RouteGraph} with the authenticated agent identity and environment.
|
||||
* <p>
|
||||
* The agent ID is extracted from the SecurityContext in the controller layer
|
||||
* and carried through the write buffer so the flush scheduler can persist it.
|
||||
* The agent ID is extracted from the SecurityContext in the controller layer,
|
||||
* the environment from the agent registry (which in turn came from the agent's JWT
|
||||
* at registration), and all are carried through the write buffer so the flush
|
||||
* scheduler can persist them.
|
||||
*/
|
||||
public record TaggedDiagram(String instanceId, String applicationId, RouteGraph graph) {}
|
||||
public record TaggedDiagram(String instanceId, String applicationId, String environment, RouteGraph graph) {}
|
||||
|
||||
@@ -17,5 +17,5 @@ public interface DiagramStore {
|
||||
|
||||
Optional<String> findContentHashForRouteByAgents(String routeId, List<String> instanceIds);
|
||||
|
||||
Map<String, String> findProcessorRouteMapping(String applicationId);
|
||||
Map<String, String> findProcessorRouteMapping(String applicationId, String environment);
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@ class ChunkAccumulatorTest {
|
||||
public Optional<com.cameleer.common.graph.RouteGraph> findByContentHash(String h) { return Optional.empty(); }
|
||||
public Optional<String> findContentHashForRoute(String r, String a) { return Optional.empty(); }
|
||||
public Optional<String> findContentHashForRouteByAgents(String r, List<String> a) { return Optional.empty(); }
|
||||
public Map<String, String> findProcessorRouteMapping(String app) { return Map.of(); }
|
||||
public Map<String, String> findProcessorRouteMapping(String app, String env) { return Map.of(); }
|
||||
};
|
||||
|
||||
private CopyOnWriteArrayList<MergedExecution> executionSink;
|
||||
|
||||
Reference in New Issue
Block a user