I have a set of canary tests that regularly produce and consume on a Kubernetes Cluster via a SpringBoot Scheduler. I want to add a second test that will do a similar operation on our 2nd Geo-replicated Kubernetes cluster.
However, I cannot seem to change the environment variable CLUSTER_NAME
from our default PRODUCTION_0
value. I tried to implement this solution https://stackoverflow.com/a/7201825/10380766 but it still defaults to the PRODUCTION_0
cluster.
Implementation
HeartBeatJobProd
@Component
public class HeartBeatJobProd1 {
private static final Logger log = LoggerFactory.getLogger(ScheduledTasks.class);
private static final String CLUSTER_NAME = "PRODUCTION_1";
@Scheduled(fixedRate = 5000)
public void execute() throws EventPlatformClientException, InterruptedException, IOException {
Map<String, String> env = new HashMap<>(System.getenv());
env.put("CLUSTER_NAME", CLUSTER_NAME);
try {
EnvSetter.setEnv(env);
} catch (Exception e) {
throw new RuntimeException(e);
}
log.info(CLUSTER_NAME + ": Producer initializing...");
HeartBeatProducer.produceAll("Heartbeat 1 producer");
log.info(CLUSTER_NAME + ": Producer executed...");
log.info(CLUSTER_NAME + ": Consumer initializing...");
HeartBeatConsumer.consumeUntilTerminated("Heartbeat 1 consumer");
log.info(CLUSTER_NAME +": Consumer executed...");
}
}
EnvSetter
public class EnvSetter {
/**
* Sets environment variables programmatically.
*
* This method uses reflection to access and modify the current environment variables map. It tries two different
* approaches depending on the Java version running. If the current version of Java has the fields "theEnvironment" and
* "theCaseInsensitiveEnvironment" inside the "java.lang.ProcessEnvironment" class, it uses those fields to access and
* modify the current environment variables map. Otherwise, it gets the current environment variables map using
* System.getenv() and, if it finds the "UnmodifiableMap" class inside the "Collections" class, it uses reflection to
* access and modify the current environment variables map.
*
* @param newEnv the map of new environment variables to set
* @throws Exception if there is a problem setting the environment variables
*/
public static void setEnv(Map<String, String> newEnv) throws Exception {
try {
// Try to get the class that contains the environment variables
Class<?> processEnvironmentClass = Class.forName("java.lang.ProcessEnvironment");
// Get the field that contains the current environment variables
Field theEnvironmentField = processEnvironmentClass.getDeclaredField("theEnvironment");
theEnvironmentField.setAccessible(true);
// Get the current environment variables map and add the new environment variables to it
Map<String, String> env = (Map<String, String>) theEnvironmentField.get(null);
env.putAll(newEnv);
// Get the field that contains the case-insensitive environment variables
Field theCaseInsensitiveEnvironmentField = processEnvironmentClass.getDeclaredField("theCaseInsensitiveEnvironment");
theCaseInsensitiveEnvironmentField.setAccessible(true);
// Get the case-insensitive environment variables map and add the new environment variables to it
Map<String, String> ciEnv = (Map<String, String>) theCaseInsensitiveEnvironmentField.get(null);
ciEnv.putAll(newEnv);
} catch (NoSuchFieldException e) {
// If the fields above don't exist (probably running on an older version of Java),
// we'll try a different approach
// Get all the classes inside the Collections class
Class<?>[] classes = Collections.class.getDeclaredClasses();
// Get the current environment variables map
Map<String, String> env = System.getenv();
// Loop through all the classes
for (Class<?> cl : classes) {
// If we find the UnmodifiableMap class, get the field that contains the
// environment variables and add the new environment variables to it
if ("java.util.Collections$UnmodifiableMap".equals(cl.getName())) {
Field field = cl.getDeclaredField("m");
field.setAccessible(true);
Object obj = field.get(env);
Map<String, String> map = (Map<String, String>) obj;
map.clear();
map.putAll(newEnv);
}
}
}
}
}