feat: ResolvedContainerConfig record and three-layer ConfigMerger
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,96 @@
|
||||
package com.cameleer3.server.core.runtime;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public final class ConfigMerger {
|
||||
|
||||
private ConfigMerger() {}
|
||||
|
||||
public static ResolvedContainerConfig resolve(
|
||||
GlobalRuntimeDefaults global,
|
||||
Map<String, Object> envConfig,
|
||||
Map<String, Object> appConfig) {
|
||||
|
||||
return new ResolvedContainerConfig(
|
||||
intVal(appConfig, envConfig, "memoryLimitMb", global.memoryLimitMb()),
|
||||
intOrNull(appConfig, envConfig, "memoryReserveMb"),
|
||||
intVal(appConfig, envConfig, "cpuShares", global.cpuShares()),
|
||||
doubleOrNull(appConfig, envConfig, "cpuLimit"),
|
||||
intVal(appConfig, envConfig, "appPort", 8080),
|
||||
intList(appConfig, envConfig, "exposedPorts"),
|
||||
stringMap(appConfig, envConfig, "customEnvVars"),
|
||||
boolVal(appConfig, envConfig, "stripPathPrefix", true),
|
||||
boolVal(appConfig, envConfig, "sslOffloading", true),
|
||||
stringVal(appConfig, envConfig, "routingMode", global.routingMode()),
|
||||
stringVal(appConfig, envConfig, "routingDomain", global.routingDomain()),
|
||||
stringVal(appConfig, envConfig, "serverUrl", global.serverUrl()),
|
||||
intVal(appConfig, envConfig, "replicas", 1),
|
||||
stringVal(appConfig, envConfig, "deploymentStrategy", "blue-green")
|
||||
);
|
||||
}
|
||||
|
||||
private static int intVal(Map<String, Object> app, Map<String, Object> env, String key, int fallback) {
|
||||
if (app.containsKey(key) && app.get(key) instanceof Number n) return n.intValue();
|
||||
if (env.containsKey(key) && env.get(key) instanceof Number n) return n.intValue();
|
||||
return fallback;
|
||||
}
|
||||
|
||||
private static Integer intOrNull(Map<String, Object> app, Map<String, Object> env, String key) {
|
||||
if (app.containsKey(key) && app.get(key) instanceof Number n) return n.intValue();
|
||||
if (env.containsKey(key) && env.get(key) instanceof Number n) return n.intValue();
|
||||
return null;
|
||||
}
|
||||
|
||||
private static Double doubleOrNull(Map<String, Object> app, Map<String, Object> env, String key) {
|
||||
if (app.containsKey(key) && app.get(key) instanceof Number n) return n.doubleValue();
|
||||
if (env.containsKey(key) && env.get(key) instanceof Number n) return n.doubleValue();
|
||||
return null;
|
||||
}
|
||||
|
||||
private static boolean boolVal(Map<String, Object> app, Map<String, Object> env, String key, boolean fallback) {
|
||||
if (app.containsKey(key) && app.get(key) instanceof Boolean b) return b;
|
||||
if (env.containsKey(key) && env.get(key) instanceof Boolean b) return b;
|
||||
return fallback;
|
||||
}
|
||||
|
||||
private static String stringVal(Map<String, Object> app, Map<String, Object> env, String key, String fallback) {
|
||||
if (app.containsKey(key) && app.get(key) instanceof String s) return s;
|
||||
if (env.containsKey(key) && env.get(key) instanceof String s) return s;
|
||||
return fallback;
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static List<Integer> intList(Map<String, Object> app, Map<String, Object> env, String key) {
|
||||
Object val = app.containsKey(key) ? app.get(key) : env.get(key);
|
||||
if (val instanceof List<?> list) {
|
||||
return list.stream()
|
||||
.filter(Number.class::isInstance)
|
||||
.map(n -> ((Number) n).intValue())
|
||||
.toList();
|
||||
}
|
||||
return List.of();
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private static Map<String, String> stringMap(Map<String, Object> app, Map<String, Object> env, String key) {
|
||||
Object val = app.containsKey(key) ? app.get(key) : env.get(key);
|
||||
if (val instanceof Map<?, ?> map) {
|
||||
Map<String, String> result = new HashMap<>();
|
||||
map.forEach((k, v) -> result.put(String.valueOf(k), String.valueOf(v)));
|
||||
return Collections.unmodifiableMap(result);
|
||||
}
|
||||
return Map.of();
|
||||
}
|
||||
|
||||
/** Global defaults extracted from application.yml @Value fields */
|
||||
public record GlobalRuntimeDefaults(
|
||||
int memoryLimitMb,
|
||||
int cpuShares,
|
||||
String routingMode,
|
||||
String routingDomain,
|
||||
String serverUrl
|
||||
) {}
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
package com.cameleer3.server.core.runtime;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public record ResolvedContainerConfig(
|
||||
int memoryLimitMb,
|
||||
Integer memoryReserveMb,
|
||||
int cpuShares,
|
||||
Double cpuLimit,
|
||||
int appPort,
|
||||
List<Integer> exposedPorts,
|
||||
Map<String, String> customEnvVars,
|
||||
boolean stripPathPrefix,
|
||||
boolean sslOffloading,
|
||||
String routingMode,
|
||||
String routingDomain,
|
||||
String serverUrl,
|
||||
int replicas,
|
||||
String deploymentStrategy
|
||||
) {
|
||||
public long memoryLimitBytes() {
|
||||
return (long) memoryLimitMb * 1024 * 1024;
|
||||
}
|
||||
|
||||
public Long memoryReserveBytes() {
|
||||
return memoryReserveMb != null ? (long) memoryReserveMb * 1024 * 1024 : null;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user