diff --git a/nebulous/ems-core/baguette-client-install/pom.xml b/nebulous/ems-core/baguette-client-install/pom.xml index 66e1895..b7f2946 100644 --- a/nebulous/ems-core/baguette-client-install/pom.xml +++ b/nebulous/ems-core/baguette-client-install/pom.xml @@ -45,11 +45,12 @@ + @@ -61,6 +62,34 @@ org.yaml snakeyaml + + + + io.fabric8 + kubernetes-client + 6.10.0 + + + com.squareup.okhttp3 + * + + + com.squareup.okio + * + + + + + com.squareup.okhttp3 + okhttp + 4.12.0 + + + com.squareup.okio + okio + 3.8.0 + + diff --git a/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/ClientInstaller.java b/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/ClientInstaller.java index 9b3a931..0c23ab6 100644 --- a/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/ClientInstaller.java +++ b/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/ClientInstaller.java @@ -9,30 +9,32 @@ package gr.iccs.imu.ems.baguette.client.install; +import gr.iccs.imu.ems.baguette.client.install.installer.K8sClientInstaller; +import gr.iccs.imu.ems.baguette.client.install.installer.SshClientInstaller; +import gr.iccs.imu.ems.baguette.client.install.installer.SshJsClientInstaller; import gr.iccs.imu.ems.baguette.server.BaguetteServer; import gr.iccs.imu.ems.baguette.server.ClientShellCommand; import gr.iccs.imu.ems.baguette.server.NodeRegistryEntry; import gr.iccs.imu.ems.brokercep.BrokerCepService; import gr.iccs.imu.ems.common.plugin.PluginManager; -import lombok.NoArgsConstructor; +import gr.iccs.imu.ems.util.ConfigWriteService; +import gr.iccs.imu.ems.util.PasswordUtil; +import jakarta.jms.JMSException; +import jakarta.validation.constraints.NotNull; import lombok.NonNull; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.InitializingBean; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import jakarta.jms.JMSException; -import jakarta.validation.constraints.NotNull; import java.io.Serializable; import java.time.Instant; -import java.util.Collections; -import java.util.LinkedHashMap; -import java.util.Map; -import java.util.Objects; +import java.util.*; import java.util.concurrent.ExecutorService; import java.util.concurrent.Executors; import java.util.concurrent.atomic.AtomicLong; +import java.util.function.BiFunction; import static gr.iccs.imu.ems.baguette.client.install.ClientInstallationTask.TASK_TYPE; @@ -41,18 +43,18 @@ import static gr.iccs.imu.ems.baguette.client.install.ClientInstallationTask.TAS */ @Slf4j @Service -@NoArgsConstructor +@RequiredArgsConstructor public class ClientInstaller implements InitializingBean { private static ClientInstaller singleton; - @Autowired - private ClientInstallationProperties properties; - @Autowired - private BrokerCepService brokerCepService; - @Autowired - private BaguetteServer baguetteServer; - @Autowired - private PluginManager pluginManager; + private final ClientInstallationProperties properties; + private final BrokerCepService brokerCepService; + private final BaguetteServer baguetteServer; + private final PluginManager pluginManager; + + private final List clientInstallerPluginList; + private final ConfigWriteService configWriteService; + private final PasswordUtil passwordUtil; private final AtomicLong taskCounter = new AtomicLong(); private ExecutorService executorService; @@ -130,6 +132,12 @@ public class ClientInstaller implements InitializingBean { return executeVmOrBaremetalTask(task, taskCounter); } else + if ("K8S".equalsIgnoreCase(task.getType())) { + if (baguetteServer.getNodeRegistry().getCoordinator()==null) + throw new IllegalStateException("Baguette Server Coordinator has not yet been initialized"); + + return executeKubernetesTask(task, taskCounter); + } else //if ("DIAGNOSTICS".equalsIgnoreCase(task.getType())) { if (task.getTaskType()==TASK_TYPE.DIAGNOSTICS) { return executeDiagnosticsTask(task, taskCounter); @@ -139,7 +147,10 @@ public class ClientInstaller implements InitializingBean { return false; } - private boolean executeVmOrBaremetalTask(ClientInstallationTask task, long taskCounter) { + private boolean executeInstaller(ClientInstallationTask task, long taskCounter, + BiFunction condition, + BiFunction installerExecution) + { NodeRegistryEntry entry = baguetteServer.getNodeRegistry().getNodeByAddress(task.getAddress()); if (task.isNodeMustBeInRegistry() && entry==null) throw new IllegalStateException("Node entry has been removed from Node Registry before installation: Node IP address: "+ task.getAddress()); @@ -156,7 +167,7 @@ public class ClientInstaller implements InitializingBean { } boolean success; - if (! task.getInstructionSets().isEmpty()) { + if (condition==null || condition.apply(task, taskCounter)) { // Starting installation entry.nodeInstalling(task); @@ -166,7 +177,7 @@ public class ClientInstaller implements InitializingBean { .forEach(plugin -> ((InstallationContextProcessorPlugin) plugin).processBeforeInstallation(task, taskCounter)); log.debug("ClientInstaller: INSTALLATION: Executing installation task: task-counter={}, task={}", taskCounter, task); - success = executeVmTask(task, taskCounter); + success = installerExecution.apply(task, taskCounter); log.debug("ClientInstaller: NODE_REGISTRY_ENTRY after installation execution: \n{}", task.getNodeRegistryEntry()); if (entry.getState() == NodeRegistryEntry.STATE.INSTALLING) { @@ -179,7 +190,7 @@ public class ClientInstaller implements InitializingBean { pluginManager.getActivePlugins(InstallationContextProcessorPlugin.class) .forEach(plugin -> ((InstallationContextProcessorPlugin) plugin).processAfterInstallation(task, taskCounter, success)); } else { - log.debug("ClientInstaller: SKIP INSTALLATION: Task has no instructions sets. Skipping execution: Node IP address: "+ task.getAddress()); + log.debug("ClientInstaller: SKIP INSTALLATION: due to condition. Skipping execution: Node IP address: "+ task.getAddress()); success = true; } @@ -203,6 +214,27 @@ public class ClientInstaller implements InitializingBean { return success; } + private boolean executeVmOrBaremetalTask(ClientInstallationTask task, long taskCounter) { + return executeInstaller(task, taskCounter, (t,c) -> ! t.getInstructionSets().isEmpty(), this::executeVmTask); + } + + private boolean executeKubernetesTask(ClientInstallationTask task, long taskCounter) { + return executeInstaller(task, taskCounter, (t,c) -> true, (t,c) -> { + boolean result; + log.info("ClientInstaller: Using K8sClientInstaller for task #{}", taskCounter); + result = K8sClientInstaller.builder() + .task(task) + .taskCounter(taskCounter) + .properties(properties) + .configWriteService(configWriteService) + .passwordUtil(passwordUtil) + .build() + .execute(); + log.info("ClientInstaller: Task execution result #{}: success={}", taskCounter, result); + return result; + }); + } + private boolean executeDiagnosticsTask(ClientInstallationTask task, long taskCounter) { log.debug("ClientInstaller: DIAGNOSTICS: Executing diagnostics task: task-counter={}, task={}", taskCounter, task); boolean success = executeVmTask(task, taskCounter); diff --git a/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/helper/AbstractInstallationHelper.java b/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/helper/AbstractInstallationHelper.java index e4711ee..8845e1d 100644 --- a/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/helper/AbstractInstallationHelper.java +++ b/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/helper/AbstractInstallationHelper.java @@ -23,15 +23,14 @@ import org.apache.commons.lang3.StringUtils; import org.apache.commons.text.StringSubstitutor; import org.apache.tomcat.util.net.SSLHostConfig; import org.apache.tomcat.util.net.SSLHostConfigCertificate; -import org.rauschig.jarchivelib.Archiver; -import org.rauschig.jarchivelib.ArchiverFactory; +//import org.rauschig.jarchivelib.Archiver; +//import org.rauschig.jarchivelib.ArchiverFactory; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.web.context.WebServerInitializedEvent; import org.springframework.boot.web.embedded.tomcat.TomcatWebServer; import org.springframework.context.ApplicationListener; import org.springframework.core.io.FileSystemResource; -import org.springframework.stereotype.Service; import java.io.*; import java.nio.file.*; @@ -43,31 +42,24 @@ import java.util.stream.Collectors; * Baguette Client installation helper */ @Slf4j -@Service public abstract class AbstractInstallationHelper implements InitializingBean, ApplicationListener, InstallationHelper { protected final static String LINUX_OS_FAMILY = "LINUX"; protected final static String WINDOWS_OS_FAMILY = "WINDOWS"; - protected static AbstractInstallationHelper instance = null; - @Autowired @Getter @Setter protected ClientInstallationProperties properties; @Autowired protected PasswordUtil passwordUtil; - protected String archiveBase64; +//XXX: Commented a not used feature with dependency with vulnerability +// protected String archiveBase64; protected boolean isServerSecure; protected String serverCert; - public synchronized static AbstractInstallationHelper getInstance() { - return instance; - } - @Override public void afterPropertiesSet() { log.debug("AbstractInstallationHelper.afterPropertiesSet(): class={}: configuration: {}", getClass().getName(), properties); - AbstractInstallationHelper.instance = this; } @Override @@ -115,7 +107,7 @@ public abstract class AbstractInstallationHelper implements InitializingBean, Ap String certPem = KeystoreUtil.exportCertificateAsPEM(c); log.debug("AbstractInstallationHelper.initServerCertificate(): SSL certificate[{}]: {}: \n{}", n, m, certPem); // Append PEM certificate to 'sb' - sb.append(certPem).append(System.getProperty("line.separator")); + sb.append(certPem).append(System.lineSeparator()); m++; } // The first entry is used as the server certificate @@ -182,6 +174,7 @@ public abstract class AbstractInstallationHelper implements InitializingBean, Ap } // Create baguette client configuration archive + /*XXX: Commented a not used feature with dependency with vulnerability Archiver archiver = ArchiverFactory.createArchiver(archiveFile); String tempFileName = "archive_" + System.currentTimeMillis(); log.debug("AbstractInstallationHelper: Temp. archive name: {}", tempFileName); @@ -198,6 +191,7 @@ public abstract class AbstractInstallationHelper implements InitializingBean, Ap byte[] archiveBytes = Files.readAllBytes(archiveFile.toPath()); this.archiveBase64 = Base64.getEncoder().encodeToString(archiveBytes); log.debug("AbstractInstallationHelper: Archive Base64 encoded: {}", archiveBase64); + */ } private String getResourceAsString(String resourcePath) throws IOException { diff --git a/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/helper/InstallationHelperFactory.java b/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/helper/InstallationHelperFactory.java index c3ab4df..7f2d727 100644 --- a/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/helper/InstallationHelperFactory.java +++ b/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/helper/InstallationHelperFactory.java @@ -39,8 +39,12 @@ public class InstallationHelperFactory implements InitializingBean { public InstallationHelper createInstallationHelper(NodeRegistryEntry entry) { String nodeType = entry.getPreregistration().get("type"); + log.debug("InstallationHelperFactory: Node type: {}", nodeType); if ("VM".equalsIgnoreCase(nodeType) || "baremetal".equalsIgnoreCase(nodeType)) { return createVmInstallationHelper(entry); + } else + if ("K8S".equalsIgnoreCase(nodeType)) { + return createK8sInstallationHelper(entry); } throw new IllegalArgumentException("Unsupported or missing Node type: "+nodeType); } @@ -58,6 +62,12 @@ public class InstallationHelperFactory implements InitializingBean { } private InstallationHelper createVmInstallationHelper(NodeRegistryEntry entry) { + log.debug("InstallationHelperFactory: Returning VmInstallationHelper"); return VmInstallationHelper.getInstance(); } + + private InstallationHelper createK8sInstallationHelper(NodeRegistryEntry entry) { + log.debug("InstallationHelperFactory: Returning K8sInstallationHelper"); + return K8sInstallationHelper.getInstance(); + } } diff --git a/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/helper/K8sInstallationHelper.java b/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/helper/K8sInstallationHelper.java new file mode 100644 index 0000000..296994b --- /dev/null +++ b/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/helper/K8sInstallationHelper.java @@ -0,0 +1,118 @@ +/* + * Copyright (C) 2017-2025 Institute of Communication and Computer Systems (imu.iccs.gr) + * + * This Source Code Form is subject to the terms of the Mozilla Public License, v2.0, unless + * Esper library is used, in which case it is subject to the terms of General Public License v2.0. + * If a copy of the MPL was not distributed with this file, you can obtain one at + * https://www.mozilla.org/en-US/MPL/2.0/ + */ + +package gr.iccs.imu.ems.baguette.client.install.helper; + +import gr.iccs.imu.ems.baguette.client.install.ClientInstallationTask; +import gr.iccs.imu.ems.baguette.client.install.instruction.InstructionsSet; +import gr.iccs.imu.ems.baguette.server.NodeRegistryEntry; +import gr.iccs.imu.ems.translate.TranslationContext; +import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.util.List; +import java.util.Map; + +/** + * Baguette Client installation helper for Kubernetes + */ +@Slf4j +@Service +public class K8sInstallationHelper extends AbstractInstallationHelper { + private static K8sInstallationHelper instance; + + public static AbstractInstallationHelper getInstance() { + return instance; + } + + @Override + public void afterPropertiesSet() { + log.debug("K8sInstallationHelper.afterPropertiesSet(): configuration: {}", properties); + instance = this; + } + + @Override + public ClientInstallationTask createClientInstallationTask(NodeRegistryEntry entry, TranslationContext translationContext) throws IOException { + return createClientTask(ClientInstallationTask.TASK_TYPE.INSTALL, entry, translationContext); + } + + @Override + public ClientInstallationTask createClientReinstallTask(NodeRegistryEntry entry, TranslationContext translationContext) throws IOException { + return createClientTask(ClientInstallationTask.TASK_TYPE.REINSTALL, entry, translationContext); + } + + @Override + public ClientInstallationTask createClientUninstallTask(NodeRegistryEntry entry, TranslationContext translationContext) throws Exception { + return createClientTask(ClientInstallationTask.TASK_TYPE.UNINSTALL, entry, translationContext); + } + + private ClientInstallationTask createClientTask(@NonNull ClientInstallationTask.TASK_TYPE taskType, + NodeRegistryEntry entry, + TranslationContext translationContext) + { + Map nodeMap = initializeNodeMap(entry); + + String baseUrl = nodeMap.get("BASE_URL"); + String clientId = nodeMap.get("CLIENT_ID"); + String ipSetting = nodeMap.get("IP_SETTING"); + String requestId = nodeMap.get("requestId"); + + // Extract node identification and type information + String nodeId = nodeMap.get("id"); + String nodeAddress = nodeMap.get("address"); + String nodeType = nodeMap.get("type"); + String nodeName = nodeMap.get("name"); + String nodeProvider = nodeMap.get("provider"); + + if (StringUtils.isBlank(nodeType)) nodeType = "K8S"; + + // Create Installation Task for VM node + ClientInstallationTask task = ClientInstallationTask.builder() + .id(clientId) + .taskType(taskType) + .nodeId(nodeId) + .requestId(requestId) + .name(nodeName) + .address(nodeAddress) + .type(nodeType) + .provider(nodeProvider) + .nodeRegistryEntry(entry) + .translationContext(translationContext) + .build(); + log.debug("K8sInstallationHelper.createClientTask(): Created client task: {}", task); + return task; + } + + private Map initializeNodeMap(NodeRegistryEntry entry) { + return entry.getPreregistration(); + } + + @Override + public List prepareInstallationInstructionsForWin(NodeRegistryEntry entry) { + return null; + } + + @Override + public List prepareInstallationInstructionsForLinux(NodeRegistryEntry entry) throws IOException { + return null; + } + + @Override + public List prepareUninstallInstructionsForWin(NodeRegistryEntry entry) { + return null; + } + + @Override + public List prepareUninstallInstructionsForLinux(NodeRegistryEntry entry) throws IOException { + return null; + } +} diff --git a/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/helper/VmInstallationHelper.java b/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/helper/VmInstallationHelper.java index dae0db0..0ac0110 100644 --- a/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/helper/VmInstallationHelper.java +++ b/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/helper/VmInstallationHelper.java @@ -23,11 +23,10 @@ import gr.iccs.imu.ems.translate.TranslationContext; import gr.iccs.imu.ems.util.CredentialsMap; import gr.iccs.imu.ems.util.NetUtil; import lombok.NonNull; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; import org.apache.commons.text.StringSubstitutor; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.core.io.ResourceLoader; import org.springframework.stereotype.Service; import java.io.IOException; @@ -44,6 +43,7 @@ import java.util.stream.Collectors; */ @Slf4j @Service +@RequiredArgsConstructor public class VmInstallationHelper extends AbstractInstallationHelper { private final static SimpleDateFormat tsW3C = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSXXX"); private final static SimpleDateFormat tsUTC = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS"); @@ -54,10 +54,19 @@ public class VmInstallationHelper extends AbstractInstallationHelper { tsFile.setTimeZone(TimeZone.getDefault()); } - @Autowired - private ResourceLoader resourceLoader; - @Autowired - private ClientInstallationProperties clientInstallationProperties; + private static VmInstallationHelper instance; + + private final ClientInstallationProperties clientInstallationProperties; + + public static AbstractInstallationHelper getInstance() { + return instance; + } + + @Override + public void afterPropertiesSet() { + log.debug("VmInstallationHelper.afterPropertiesSet(): configuration: {}", properties); + instance = this; + } @Override public ClientInstallationTask createClientInstallationTask(NodeRegistryEntry entry, TranslationContext translationContext) throws IOException { diff --git a/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/installer/K8sClientInstaller.java b/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/installer/K8sClientInstaller.java new file mode 100644 index 0000000..d90a27e --- /dev/null +++ b/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/installer/K8sClientInstaller.java @@ -0,0 +1,269 @@ +/* + * Copyright (C) 2017-2025 Institute of Communication and Computer Systems (imu.iccs.gr) + * + * This Source Code Form is subject to the terms of the Mozilla Public License, v2.0, unless + * Esper library is used, in which case it is subject to the terms of General Public License v2.0. + * If a copy of the MPL was not distributed with this file, you can obtain one at + * https://www.mozilla.org/en-US/MPL/2.0/ + */ + +package gr.iccs.imu.ems.baguette.client.install.installer; + +import gr.iccs.imu.ems.baguette.client.install.ClientInstallationProperties; +import gr.iccs.imu.ems.baguette.client.install.ClientInstallationTask; +import gr.iccs.imu.ems.baguette.client.install.ClientInstallerPlugin; +import gr.iccs.imu.ems.util.ConfigWriteService; +import gr.iccs.imu.ems.util.EmsConstant; +import gr.iccs.imu.ems.util.EmsRelease; +import gr.iccs.imu.ems.util.PasswordUtil; +import io.fabric8.kubernetes.api.model.ConfigMap; +import io.fabric8.kubernetes.api.model.ConfigMapBuilder; +import io.fabric8.kubernetes.api.model.apps.DaemonSet; +import io.fabric8.kubernetes.client.Config; +import io.fabric8.kubernetes.client.ConfigBuilder; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.KubernetesClientBuilder; +import io.fabric8.kubernetes.client.dsl.Resource; +import lombok.Builder; +import lombok.Getter; +import lombok.NonNull; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.RandomStringUtils; +import org.apache.commons.lang3.StringUtils; +import org.apache.commons.text.StringSubstitutor; +import org.springframework.util.StreamUtils; + +import java.io.*; +import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.time.Instant; +import java.util.LinkedHashMap; +import java.util.Map; + +/** + * EMS client installer on a Kubernetes cluster + */ +@Slf4j +@Getter +public class K8sClientInstaller implements ClientInstallerPlugin { + private static final String K8S_SERVICE_ACCOUNT_SECRETS_PATH_DEFAULT = "/var/run/secrets/kubernetes.io/serviceaccount"; + private static final String APP_CONFIG_MAP_NAME_DEFAULT = "monitoring-configmap"; + private static final String EMS_CLIENT_CONFIG_MAP_NAME_DEFAULT = "ems-client-configmap"; + + private static final String EMS_CLIENT_DAEMONSET_SPECIFICATION_FILE_DEFAULT = "/ems-client-daemonset.yaml"; + private static final String EMS_CLIENT_DAEMONSET_NAME_DEFAULT = "ems-client-daemonset"; + private static final String EMS_CLIENT_DAEMONSET_IMAGE_REPOSITORY_DEFAULT = "registry.gitlab.com/nebulous-project/ems-main/ems-client"; + private static final String EMS_CLIENT_DAEMONSET_IMAGE_TAG_DEFAULT = EmsRelease.EMS_VERSION; + private static final String EMS_CLIENT_DAEMONSET_IMAGE_PULL_POLICY_DEFAULT = "Always"; + + private final ClientInstallationTask task; + private final long taskCounter; + private final ClientInstallationProperties properties; + private final ConfigWriteService configWriteService; + private final PasswordUtil passwordUtil; + + private String additionalCredentials; // Those specified in EMS_CLIENT_ADDITIONAL_BROKER_CREDENTIALS, plus one generated + private String brokerUsername; + private String brokerPassword; + + @Builder + public K8sClientInstaller(ClientInstallationTask task, long taskCounter, ClientInstallationProperties properties, + ConfigWriteService configWriteService, PasswordUtil passwordUtil) + { + this.task = task; + this.taskCounter = taskCounter; + this.properties = properties; + this.configWriteService = configWriteService; + this.passwordUtil = passwordUtil; + + initializeAdditionalCredentials(); + } + + private void initializeAdditionalCredentials() { + brokerUsername = getConfig("EMS_CLIENT_BROKER_USERNAME", "user-" + RandomStringUtils.randomAlphanumeric(32)); + brokerPassword = getConfig("EMS_CLIENT_BROKER_PASSWORD", RandomStringUtils.randomAlphanumeric(32)); + + StringBuilder sb = new StringBuilder(getConfig("EMS_CLIENT_ADDITIONAL_BROKER_CREDENTIALS", "")); + if (StringUtils.isNotBlank(sb)) + sb.append(", "); + sb.append(brokerUsername).append("/").append(brokerPassword); + additionalCredentials = sb.toString(); + } + + private String getConfig(@NonNull String key, String defaultValue) { + String value = System.getenv(key); + return value==null ? defaultValue : value; + } + + @Override + public boolean executeTask() { + boolean success = true; + try { + deployOnCluster(); + } catch (Exception ex) { + log.error("K8sClientInstaller: Failed executing installation instructions for task #{}, Exception: ", taskCounter, ex); + success = false; + } + + if (success) log.info("K8sClientInstaller: Task completed successfully #{}", taskCounter); + else log.info("K8sClientInstaller: Error occurred while executing task #{}", taskCounter); + return true; + } + + private void deployOnCluster() throws IOException { + boolean dryRun = Boolean.parseBoolean( getConfig("EMS_CLIENT_DEPLOYMENT_DRY_RUN", "false") ); + if (dryRun) + log.warn("K8sClientInstaller: NOTE: Dry Run set!! Will not make any changes to cluster"); + deployOnCluster(dryRun); + } + + private void deployOnCluster(boolean dryRun) throws IOException { + String serviceAccountPath = getConfig("K8S_SERVICE_ACCOUNT_SECRETS_PATH", K8S_SERVICE_ACCOUNT_SECRETS_PATH_DEFAULT); + String masterUrl = getConfig("KUBERNETES_SERVICE_HOST", null); + String caCert = Files.readString(Paths.get(serviceAccountPath, "ca.crt")); + String token = Files.readString(Paths.get(serviceAccountPath, "token")); + String namespace = Files.readString(Paths.get(serviceAccountPath, "namespace")); + log.debug(""" + K8sClientInstaller: + Master URL: {} + CA cert.: + {} + Token: {} + Namespace: {}""", + masterUrl, caCert.trim(), passwordUtil.encodePassword(token), namespace); + + // Configure and start Kubernetes API client + Config config = new ConfigBuilder() + .withMasterUrl(masterUrl) + .withCaCertData(caCert) + .withOauthToken(token) + .build(); + try (KubernetesClient client = new KubernetesClientBuilder().withConfig(config).build()) { + // Prepare ems client config map + createEmsClientConfigMap(dryRun, client, namespace); + + // Prepare application config map + createAppConfigMap(dryRun, client, namespace); + + // Deploy ems client daemonset + createEmsClientDaemonSet(dryRun, client, namespace); + + task.getNodeRegistryEntry().nodeInstallationComplete(null); + } + } + + private void createEmsClientConfigMap(boolean dryRun, KubernetesClient client, String namespace) { + log.debug("K8sClientInstaller.createEmsClientConfigMap: BEGIN: dry-run={}, namespace={}", dryRun, namespace); + + // Get ems client configmap name + String configMapName = getConfig("EMS_CLIENT_CONFIG_MAP_NAME", EMS_CLIENT_CONFIG_MAP_NAME_DEFAULT); + log.debug("K8sClientInstaller: configMap: name: {}", configMapName); + + // Get ems client configuration + Map configMapMap = configWriteService + .getConfigFile(EmsConstant.EMS_CLIENT_K8S_CONFIG_MAP_FILE).getContentMap(); + log.debug("K8sClientInstaller: configMap: data:\n{}", configMapMap); + + // Create ems client configmap + Resource configMapResource = client.configMaps() + .inNamespace(namespace) + .resource(new ConfigMapBuilder() + .withNewMetadata().withName(configMapName).endMetadata() + .addToData("creationTimestamp", Long.toString(Instant.now().getEpochSecond())) + .addToData(configMapMap) + .build()); + log.trace("K8sClientInstaller: ConfigMap to create: {}", configMapResource); + if (!dryRun) { + ConfigMap configMap = configMapResource.serverSideApply(); + log.debug("K8sClientInstaller: ConfigMap created: {}", configMap); + } else { + log.warn("K8sClientInstaller: DRY-RUN: Didn't create ems client configmap"); + } + } + + private void createAppConfigMap(boolean dryRun, KubernetesClient client, String namespace) { + log.debug("K8sClientInstaller.createAppConfigMap: BEGIN: dry-run={}, namespace={}", dryRun, namespace); + + // Get ems client configmap name + String configMapName = getConfig("APP_CONFIG_MAP_NAME", APP_CONFIG_MAP_NAME_DEFAULT); + log.debug("K8sClientInstaller: App configMap: name: {}", configMapName); + + // Get App ems-client-related configuration + Map configMapMap = new LinkedHashMap<>(); + configMapMap.put("BROKER_USERNAME", brokerUsername); + configMapMap.put("BROKER_PASSWORD", brokerPassword); + log.debug("K8sClientInstaller: App configMap: data:\n{}", configMapMap); + + // Create ems client configmap + Resource configMapResource = client.configMaps() + .inNamespace(namespace) + .resource(new ConfigMapBuilder() + .withNewMetadata().withName(configMapName).endMetadata() + .addToData("creationTimestamp", Long.toString(Instant.now().getEpochSecond())) + .addToData(configMapMap) + .build()); + log.trace("K8sClientInstaller: App ConfigMap to create: {}", configMapResource); + if (!dryRun) { + ConfigMap configMap = configMapResource.serverSideApply(); + log.debug("K8sClientInstaller: App ConfigMap created: {}", configMap); + } else { + log.warn("K8sClientInstaller: DRY-RUN: Didn't create App ems client configmap"); + } + } + + private void createEmsClientDaemonSet(boolean dryRun, KubernetesClient client, String namespace) throws IOException { + log.debug("K8sClientInstaller.createEmsClientDaemonSet: BEGIN: dry-run={}, namespace={}", dryRun, namespace); + + String resourceName = getConfig("EMS_CLIENT_DAEMONSET_SPECIFICATION_FILE", EMS_CLIENT_DAEMONSET_SPECIFICATION_FILE_DEFAULT); + Map values = Map.of( + "EMS_CLIENT_DAEMONSET_NAME", getConfig("EMS_CLIENT_DAEMONSET_NAME", EMS_CLIENT_DAEMONSET_NAME_DEFAULT), + "EMS_CLIENT_DAEMONSET_IMAGE_REPOSITORY", getConfig("EMS_CLIENT_DAEMONSET_IMAGE_REPOSITORY", EMS_CLIENT_DAEMONSET_IMAGE_REPOSITORY_DEFAULT), + "EMS_CLIENT_DAEMONSET_IMAGE_TAG", getConfig("EMS_CLIENT_DAEMONSET_IMAGE_TAG", EMS_CLIENT_DAEMONSET_IMAGE_TAG_DEFAULT), + "EMS_CLIENT_DAEMONSET_IMAGE_PULL_POLICY", getConfig("EMS_CLIENT_DAEMONSET_IMAGE_PULL_POLICY", EMS_CLIENT_DAEMONSET_IMAGE_PULL_POLICY_DEFAULT), + "EMS_CLIENT_CONFIG_MAP_NAME", getConfig("EMS_CLIENT_CONFIG_MAP_NAME", EMS_CLIENT_CONFIG_MAP_NAME_DEFAULT), + "EMS_CLIENT_ADDITIONAL_BROKER_CREDENTIALS", additionalCredentials, + "EMS_CLIENT_KEYSTORE_SECRET", getConfig("EMS_CLIENT_KEYSTORE_SECRET", ""), + "EMS_CLIENT_TRUSTSTORE_SECRET", getConfig("EMS_CLIENT_TRUSTSTORE_SECRET", "") + ); + log.debug("K8sClientInstaller: resourceName: {}", namespace); + log.debug("K8sClientInstaller: values: {}", values); + + String spec; + try (InputStream inputStream = K8sClientInstaller.class.getResourceAsStream(resourceName)) { + spec = StreamUtils.copyToString(inputStream, Charset.defaultCharset()); + log.trace("K8sClientInstaller: Ems client daemonset spec BEFORE:\n{}", spec); + spec = StringSubstitutor.replace(spec, values); + log.trace("K8sClientInstaller: Ems client daemonset spec AFTER :\n{}", spec); + } + + if (StringUtils.isNotBlank(spec)) { + try (InputStream stream = new ByteArrayInputStream(spec.getBytes(StandardCharsets.UTF_8))) { + // Load a DaemonSet object + DaemonSet daemonSet = client.apps().daemonSets().load(stream).item(); + log.debug("K8sClientInstaller: DaemonSet to create: {} :: {}", daemonSet.hashCode(), daemonSet.getMetadata().getName()); + + // Deploy the DaemonSet + if (!dryRun) { + DaemonSet ds = client.apps().daemonSets().inNamespace(namespace).resource(daemonSet).create(); + log.debug("K8sClientInstaller: DaemonSet created: {} :: {}", ds.hashCode(), ds.getMetadata().getName()); + } else { + log.warn("K8sClientInstaller: DRY-RUN: Didn't create ems client daemonset"); + } + } + } else { + log.warn("K8sClientInstaller: ERROR: Ems client daemonset spec is empty"); + } + } + + @Override + public void preProcessTask() { + // Throw exception to prevent task exception, if task data have problem + } + + @Override + public boolean postProcessTask() { + return true; + } +} diff --git a/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/SshClientInstaller.java b/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/installer/SshClientInstaller.java similarity index 97% rename from nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/SshClientInstaller.java rename to nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/installer/SshClientInstaller.java index b066c18..53d956f 100644 --- a/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/SshClientInstaller.java +++ b/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/installer/SshClientInstaller.java @@ -7,8 +7,9 @@ * https://www.mozilla.org/en-US/MPL/2.0/ */ -package gr.iccs.imu.ems.baguette.client.install; +package gr.iccs.imu.ems.baguette.client.install.installer; +import gr.iccs.imu.ems.baguette.client.install.*; import gr.iccs.imu.ems.baguette.client.install.instruction.INSTRUCTION_RESULT; import gr.iccs.imu.ems.baguette.client.install.instruction.Instruction; import gr.iccs.imu.ems.baguette.client.install.instruction.InstructionsService; @@ -32,6 +33,9 @@ import org.apache.sshd.mina.MinaServiceFactoryFactory; import org.apache.sshd.scp.client.DefaultScpClientCreator; import org.apache.sshd.scp.client.ScpClient; import org.apache.sshd.scp.client.ScpClientCreator; +import org.bouncycastle.openssl.PEMKeyPair; +import org.bouncycastle.openssl.PEMParser; +import org.bouncycastle.openssl.jcajce.JcaPEMKeyConverter; import org.bouncycastle.util.io.pem.PemObject; import org.bouncycastle.util.io.pem.PemReader; @@ -212,10 +216,12 @@ public class SshClientInstaller implements ClientInstallerPlugin { .verify(connectTimeout) .getSession(); if (StringUtils.isNotBlank(privateKey)) { - PrivateKey privKey = getPrivateKey(privateKey); + /*PrivateKey privKey = getPrivateKey(privateKey); //PublicKey pubKey = getPublicKey(publicKeyStr); PublicKey pubKey = getPublicKey(privKey); KeyPair keyPair = new KeyPair(pubKey, privKey); + */ + KeyPair keyPair = getKeyPairFromPEM(privateKey); session.addPublicKeyIdentity(keyPair); } if (StringUtils.isNotBlank(password)) { @@ -249,6 +255,28 @@ public class SshClientInstaller implements ClientInstallerPlugin { //PrivateKey privKey = kf.generatePrivate(keySpecPKCS8); } + private KeyPair getKeyPairFromPEM(String pemStr) throws IOException { + // Load the PEM file containing the private key + log.trace("SshClientInstaller: getKeyPairFromPEM: pemStr: {}", pemStr); + pemStr = pemStr.replace("\\n", "\n"); + try (StringReader keyReader = new StringReader(pemStr); PEMParser pemParser = new PEMParser(keyReader)) { + // Parse the PEM encoded private key + Object pemObject = pemParser.readObject(); + log.trace("SshClientInstaller: getKeyPairFromPEM: pemObject: {}", pemObject); + + // Convert the PEM object to a KeyPair + JcaPEMKeyConverter converter = new JcaPEMKeyConverter(); + if (pemObject instanceof PEMKeyPair pemKeyPair) { + KeyPair keyPair = converter.getKeyPair(pemKeyPair); + log.trace("SshClientInstaller: getKeyPairFromPEM: keyPair: {}", keyPair); + return keyPair; + } else { + log.warn("SshClientInstaller: getKeyPairFromPEM: Failed to parse PEM private key"); + throw new RuntimeException("Failed to parse PEM private key"); + } + } + } + private PublicKey getPublicKey(PrivateKey privateKey) throws NoSuchAlgorithmException, InvalidKeySpecException { KeyFactory factory = KeyFactory.getInstance(privateKey.getAlgorithm()); PKCS8EncodedKeySpec pubKeySpec = new PKCS8EncodedKeySpec(privateKey.getEncoded()); diff --git a/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/SshJsClientInstaller.java b/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/installer/SshJsClientInstaller.java similarity index 97% rename from nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/SshJsClientInstaller.java rename to nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/installer/SshJsClientInstaller.java index 80fb38f..2189796 100644 --- a/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/SshJsClientInstaller.java +++ b/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/installer/SshJsClientInstaller.java @@ -7,8 +7,10 @@ * https://www.mozilla.org/en-US/MPL/2.0/ */ -package gr.iccs.imu.ems.baguette.client.install; +package gr.iccs.imu.ems.baguette.client.install.installer; +import gr.iccs.imu.ems.baguette.client.install.ClientInstallationProperties; +import gr.iccs.imu.ems.baguette.client.install.ClientInstallationTask; import gr.iccs.imu.ems.baguette.client.install.instruction.INSTRUCTION_RESULT; import gr.iccs.imu.ems.baguette.client.install.instruction.InstructionsSet; import lombok.Builder; diff --git a/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/StreamLogger.java b/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/installer/StreamLogger.java similarity index 98% rename from nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/StreamLogger.java rename to nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/installer/StreamLogger.java index 101717f..1732f89 100644 --- a/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/StreamLogger.java +++ b/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/installer/StreamLogger.java @@ -7,7 +7,7 @@ * https://www.mozilla.org/en-US/MPL/2.0/ */ -package gr.iccs.imu.ems.baguette.client.install; +package gr.iccs.imu.ems.baguette.client.install.installer; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; diff --git a/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/plugin/AllowedTopicsProcessorPlugin.java b/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/plugin/AllowedTopicsProcessorPlugin.java index c82f9d7..7caffd1 100644 --- a/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/plugin/AllowedTopicsProcessorPlugin.java +++ b/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/plugin/AllowedTopicsProcessorPlugin.java @@ -9,6 +9,9 @@ package gr.iccs.imu.ems.baguette.client.install.plugin; +import com.fasterxml.jackson.annotation.JsonInclude; +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; import gr.iccs.imu.ems.baguette.client.install.ClientInstallationTask; import gr.iccs.imu.ems.baguette.client.install.InstallationContextProcessorPlugin; import gr.iccs.imu.ems.translate.model.Monitor; @@ -23,8 +26,8 @@ import java.util.*; /** * Installation context processor plugin for generating 'allowed-topics' setting - * used in baguette-client[.yml/.properties] config. file. - * It set the 'COLLECTOR_ALLOWED_TOPICS' variable in pre-registration context. + * used in baguette-client[.yml/.properties] configuration file. + * It sets the 'COLLECTOR_ALLOWED_TOPICS' variable in pre-registration context. */ @Slf4j @Data @@ -37,6 +40,7 @@ public class AllowedTopicsProcessorPlugin implements InstallationContextProcesso StringBuilder sbAllowedTopics = new StringBuilder(); Set addedTopicsSet = new HashSet<>(); + Map> collectorConfigs = new LinkedHashMap<>(); boolean first = true; for (Monitor monitor : task.getTranslationContext().getMON()) { @@ -53,7 +57,7 @@ public class AllowedTopicsProcessorPlugin implements InstallationContextProcesso } // Get sensor configuration - Map sensorConfig = monitor.getSensor().getConfiguration();; + Map sensorConfig = monitor.getSensor().getConfiguration(); // Process Destination aliases, if specified in configuration if (sensorConfig!=null) { @@ -72,6 +76,14 @@ public class AllowedTopicsProcessorPlugin implements InstallationContextProcesso } } } + + if (monitor.getSensor().isPullSensor()) { + if (sensorConfig.get("type") instanceof String type && StringUtils.isNotBlank(type)) { + collectorConfigs + .computeIfAbsent(type, key->new LinkedList<>()) + .add(monitor.getSensor()); + } + } } log.trace("AllowedTopicsProcessorPlugin: Task #{}: MONITOR: metric={}, allowed-topics={}", @@ -86,7 +98,28 @@ public class AllowedTopicsProcessorPlugin implements InstallationContextProcesso String allowedTopics = sbAllowedTopics.toString(); log.debug("AllowedTopicsProcessorPlugin: Task #{}: Allowed-Topics configuration for collectors: \n{}", taskCounter, allowedTopics); + String collectorConfigsStr = null; + try { + if (! collectorConfigs.isEmpty()) { + log.debug("AllowedTopicsProcessorPlugin: Task #{}: Pull-Sensor collector configurations: \n{}", taskCounter, collectorConfigs); + ObjectMapper mapper = new ObjectMapper(); + mapper.setSerializationInclusion(JsonInclude.Include.NON_NULL); + /*mapper.setFilterProvider(new SimpleFilterProvider().addFilter("customerFilter", + SimpleBeanPropertyFilter.serializeAllExcept("@objectClass")));*/ + collectorConfigsStr = mapper + .writerWithDefaultPrettyPrinter() + .writeValueAsString(collectorConfigs); + if (StringUtils.isBlank(collectorConfigsStr)) + collectorConfigsStr = null; + } + } catch (JsonProcessingException e) { + log.error("AllowedTopicsProcessorPlugin: Task #{}: EXCEPTION while processing sensor configs. Skipping them.\n", + taskCounter, e); + } + log.debug("AllowedTopicsProcessorPlugin: Task #{}: Pull-Sensor collector configurations String: \n{}", taskCounter, collectorConfigsStr); + task.getNodeRegistryEntry().getPreregistration().put(EmsConstant.COLLECTOR_ALLOWED_TOPICS_VAR, allowedTopics); + task.getNodeRegistryEntry().getPreregistration().put(EmsConstant.COLLECTOR_CONFIGURATIONS_VAR, collectorConfigsStr); log.debug("AllowedTopicsProcessorPlugin: Task #{}: processBeforeInstallation: END", taskCounter); } diff --git a/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/plugin/PrometheusProcessorPlugin.java b/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/plugin/PrometheusProcessorPlugin.java index 1fe58d4..8b9200e 100644 --- a/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/plugin/PrometheusProcessorPlugin.java +++ b/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/plugin/PrometheusProcessorPlugin.java @@ -83,7 +83,7 @@ public class PrometheusProcessorPlugin implements InstallationContextProcessorPl // Get monitor interval Interval interval = monitor.getSensor().pullSensor().getInterval(); if (interval != null) { - int period = interval.getPeriod(); + long period = interval.getPeriod(); TimeUnit unit = TimeUnit.SECONDS; if (interval.getUnit() != null) { unit = TimeUnit.valueOf( interval.getUnit().name() ); diff --git a/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/watch/K8sPodWatcher.java b/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/watch/K8sPodWatcher.java new file mode 100644 index 0000000..041e889 --- /dev/null +++ b/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/install/watch/K8sPodWatcher.java @@ -0,0 +1,188 @@ +/* + * Copyright (C) 2017-2025 Institute of Communication and Computer Systems (imu.iccs.gr) + * + * This Source Code Form is subject to the terms of the Mozilla Public License, v2.0, unless + * Esper library is used, in which case it is subject to the terms of General Public License v2.0. + * If a copy of the MPL was not distributed with this file, you can obtain one at + * https://www.mozilla.org/en-US/MPL/2.0/ + */ + +package gr.iccs.imu.ems.baguette.client.install.watch; + +import gr.iccs.imu.ems.baguette.server.NodeRegistry; +import gr.iccs.imu.ems.baguette.server.NodeRegistryEntry; +import gr.iccs.imu.ems.util.PasswordUtil; +import io.fabric8.kubernetes.api.model.Node; +import io.fabric8.kubernetes.api.model.Pod; +import io.fabric8.kubernetes.client.Config; +import io.fabric8.kubernetes.client.ConfigBuilder; +import io.fabric8.kubernetes.client.KubernetesClient; +import io.fabric8.kubernetes.client.KubernetesClientBuilder; +import io.fabric8.kubernetes.client.dsl.Resource; +import lombok.Data; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.scheduling.TaskScheduler; +import org.springframework.stereotype.Service; + +import java.nio.file.Files; +import java.nio.file.Paths; +import java.time.Duration; +import java.time.Instant; +import java.util.*; +import java.util.stream.Collectors; + +/** + * Kubernetes cluster pods watcher service + */ +@Slf4j +@Service +@RequiredArgsConstructor +public class K8sPodWatcher implements InitializingBean { + private static final String K8S_SERVICE_ACCOUNT_SECRETS_PATH_DEFAULT = "/var/run/secrets/kubernetes.io/serviceaccount"; + + private final TaskScheduler taskScheduler; + private final PasswordUtil passwordUtil; + private final NodeRegistry nodeRegistry; + + @Override + public void afterPropertiesSet() throws Exception { + if (Boolean.parseBoolean(getConfig("K8S_WATCHER_ENABLED", "true"))) { + taskScheduler.scheduleWithFixedDelay(this::doWatch, Instant.now().plusSeconds(20), Duration.ofSeconds(10)); + } else { + log.warn("K8sPodWatcher: Disabled (set K8S_WATCHER_ENABLED=true to enable)"); + } + } + + private String getConfig(@NonNull String key, String defaultValue) { + String value = System.getenv(key); + return value==null ? defaultValue : value; + } + + private void doWatch() { + Map addressToNodeMap; + Map> addressToPodMap; + try { + log.debug("K8sPodWatcher: BEGIN: doWatch"); + + String serviceAccountPath = getConfig("K8S_SERVICE_ACCOUNT_SECRETS_PATH", K8S_SERVICE_ACCOUNT_SECRETS_PATH_DEFAULT); + String masterUrl = getConfig("KUBERNETES_SERVICE_HOST", null); + String caCert = Files.readString(Paths.get(serviceAccountPath, "ca.crt")); + String token = Files.readString(Paths.get(serviceAccountPath, "token")); + String namespace = Files.readString(Paths.get(serviceAccountPath, "namespace")); + log.trace(""" + K8sPodWatcher: + Master URL: {} + CA cert.: + {} + Token: {} + Namespace: {}""", + masterUrl, caCert.trim(), passwordUtil.encodePassword(token), namespace); + + // Configure and start Kubernetes API client + Config config = new ConfigBuilder() + .withMasterUrl(masterUrl) + .withCaCertData(caCert) + .withOauthToken(token) + .build(); + try (KubernetesClient client = new KubernetesClientBuilder().withConfig(config).build()) { + log.debug("K8sPodWatcher: Retrieving active Kubernetes cluster nodes and pods"); + + // Get Kubernetes cluster nodes (Hosts) + addressToNodeMap = new HashMap<>(); + Map uidToNodeMap = new HashMap<>(); + client.nodes() + .resources() + .map(Resource::item) + .forEach(node -> { + NodeEntry entry = uidToNodeMap.computeIfAbsent( + node.getMetadata().getUid(), s -> new NodeEntry(node)); + node.getStatus().getAddresses().stream() + .filter(address -> ! "Hostname".equalsIgnoreCase(address.getType())) + .forEach(address -> addressToNodeMap.putIfAbsent(address.getAddress(), entry)); + }); + log.trace("K8sPodWatcher: Address-to-Nodes: {}", addressToNodeMap); + + // Get Kubernetes cluster pods + addressToPodMap = new HashMap<>(); + Map uidToPodMap = new HashMap<>(); + client.pods() + .inAnyNamespace() +// .withLabel("nebulous.application") + .resources() + .map(Resource::item) + .filter(pod-> "Running".equalsIgnoreCase(pod.getStatus().getPhase())) + .forEach(pod -> { + PodEntry entry = uidToPodMap.computeIfAbsent( + pod.getMetadata().getUid(), s -> new PodEntry(pod)); + pod.getStatus().getPodIPs() + .forEach(address -> + addressToPodMap.computeIfAbsent(address.getIp(), s -> new HashSet<>()).add(entry) + ); + }); + log.trace("K8sPodWatcher: Address-to-Pods: {}", addressToPodMap); + + } // End of try-with-resources + + // Update Node Registry + log.debug("K8sPodWatcher: Updating Node Registry"); + Map addressToNodeEntryMap = nodeRegistry.getNodes().stream() + .collect(Collectors.toMap(NodeRegistryEntry::getIpAddress, entry -> entry)); + + // New Pods + HashMap> newPods = new HashMap<>(addressToPodMap); + newPods.keySet().removeAll(addressToNodeEntryMap.keySet()); + if (! newPods.isEmpty()) { + log.trace("K8sPodWatcher: New Pods found: {}", newPods); + /*newPods.forEach((address, podSet) -> { + if (podSet.size()==1) + nodeRegistry.addNode(null, podSet.iterator().next().getPodUid()); + });*/ + } else { + log.trace("K8sPodWatcher: No new Pods"); + } + + // Node Entries to be removed + HashMap oldEntries = new HashMap<>(addressToNodeEntryMap); + oldEntries.keySet().removeAll(addressToPodMap.keySet()); + if (! oldEntries.isEmpty()) { + log.trace("K8sPodWatcher: Node entries to be removed: {}", oldEntries); + } else { + log.trace("K8sPodWatcher: No node entries to remove"); + } + + } catch (Exception e) { + log.warn("K8sPodWatcher: Error while running doWatch: ", e); + } + } + + @Data + private static class NodeEntry { + private final String nodeUid; + private final String nodeName; + + public NodeEntry(Node node) { + nodeUid = node.getMetadata().getUid(); + nodeName = node.getMetadata().getName(); + } + } + + @Data + private static class PodEntry { + private final String podUid; + private final String podIP; + private final String podName; + private final String hostIP; + private final Map labels; + + public PodEntry(Pod pod) { + podUid = pod.getMetadata().getUid(); + podIP = pod.getStatus().getPodIP(); + podName = pod.getMetadata().getName(); + hostIP = pod.getStatus().getHostIP(); + labels = Collections.unmodifiableMap(pod.getMetadata().getLabels()); + } + } +} diff --git a/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/selfhealing/ClientRecoveryPlugin.java b/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/selfhealing/ClientRecoveryPlugin.java index 4cf113b..7c314a2 100644 --- a/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/selfhealing/ClientRecoveryPlugin.java +++ b/nebulous/ems-core/baguette-client-install/src/main/java/gr/iccs/imu/ems/baguette/client/selfhealing/ClientRecoveryPlugin.java @@ -11,7 +11,7 @@ package gr.iccs.imu.ems.baguette.client.selfhealing; import gr.iccs.imu.ems.baguette.client.install.ClientInstallationProperties; import gr.iccs.imu.ems.baguette.client.install.ClientInstallationTask; -import gr.iccs.imu.ems.baguette.client.install.SshClientInstaller; +import gr.iccs.imu.ems.baguette.client.install.installer.SshClientInstaller; import gr.iccs.imu.ems.baguette.client.install.helper.InstallationHelperFactory; import gr.iccs.imu.ems.baguette.server.BaguetteServer; import gr.iccs.imu.ems.baguette.server.ClientShellCommand; diff --git a/nebulous/ems-core/baguette-client-install/src/main/resources/ems-client-daemonset.yaml b/nebulous/ems-core/baguette-client-install/src/main/resources/ems-client-daemonset.yaml new file mode 100644 index 0000000..5a65c10 --- /dev/null +++ b/nebulous/ems-core/baguette-client-install/src/main/resources/ems-client-daemonset.yaml @@ -0,0 +1,156 @@ +# +# Copyright (C) 2017-2025 Institute of Communication and Computer Systems (imu.iccs.gr) +# +# This Source Code Form is subject to the terms of the Mozilla Public License, v2.0, unless +# Esper library is used, in which case it is subject to the terms of General Public License v2.0. +# If a copy of the MPL was not distributed with this file, you can obtain one at +# https://www.mozilla.org/en-US/MPL/2.0/ +# + +apiVersion: apps/v1 +kind: DaemonSet +metadata: + name: ${EMS_CLIENT_DAEMONSET_NAME} + labels: + app.kubernetes.io/name: ${EMS_CLIENT_DAEMONSET_NAME} +spec: + selector: + matchLabels: + app.kubernetes.io/name: ${EMS_CLIENT_DAEMONSET_NAME} + template: + metadata: + labels: + app.kubernetes.io/name: ${EMS_CLIENT_DAEMONSET_NAME} + spec: + hostNetwork: true + + terminationGracePeriodSeconds: 10 + containers: + - name: "${EMS_CLIENT_DAEMONSET_NAME}" + image: "${EMS_CLIENT_DAEMONSET_IMAGE_REPOSITORY}:${EMS_CLIENT_DAEMONSET_IMAGE_TAG}" + imagePullPolicy: "${EMS_CLIENT_DAEMONSET_IMAGE_PULL_POLICY}" + env: + # + # K8S cluster node info + # + - name: K8S_NODE_NAME + valueFrom: + fieldRef: + fieldPath: spec.nodeName + - name: K8S_NODE_ADDRESS + valueFrom: + fieldRef: + fieldPath: status.hostIP + # + # Pod info + # + - name: POD_NAME + valueFrom: + fieldRef: + fieldPath: metadata.name + - name: POD_NAMESPACE + valueFrom: + fieldRef: + fieldPath: metadata.namespace + - name: POD_ADDRESS + valueFrom: + fieldRef: + fieldPath: status.podIP + # + # EMS client settings + # + - name: IP_SETTING + value: 'DEFAULT_IP' + - name: BAGUETTE_CLIENT_BASE_DIR + value: '/opt/baguette-client' + - name: BAGUETTE_CLIENT_ID + valueFrom: + fieldRef: + fieldPath: metadata.uid + - name: NODE_CLIENT_ID + valueFrom: + fieldRef: + fieldPath: metadata.uid + - name: NODE_ADDRESS + valueFrom: + fieldRef: + fieldPath: status.podIP + - name: SELF_HEALING_ENABLED + value: "false" + - name: COLLECTOR_NETDATA_ENABLE + value: "true" + - name: COLLECTOR_NETDATA_URL + value: 'http://${K8S_NODE_ADDRESS}:19999/api/v1/allmetrics?format=json' + - name: COLLECTOR_PROMETHEUS_ENABLE + value: "false" + - name: COLLECTOR_ALLOWED_TOPICS + valueFrom: + configMapKeyRef: + name: "${EMS_CLIENT_CONFIG_MAP_NAME}" + key: COLLECTOR_ALLOWED_TOPICS + optional: true + - name: EMS_KEYSTORE_PASSWORD + value: "${EMS_CLIENT_KEYSTORE_SECRET}" + - name: EMS_TRUSTSTORE_PASSWORD + value: "${EMS_CLIENT_TRUSTSTORE_SECRET}" + # + # EMS client Broker settings + # + - name: BROKER_URL_ADDRESS_INSECURE + value: "0.0.0.0" + - name: BROKERCEP_ADDITIONAL_BROKER_CREDENTIALS + value: "${EMS_CLIENT_ADDITIONAL_BROKER_CREDENTIALS}" + # + # Baguette Server connection info. Can be retrieved from EMS server: + # https://ems-server:8111/baguette/connectionInfo + # + - name: BAGUETTE_SERVER_ADDRESS + valueFrom: + configMapKeyRef: + name: "${EMS_CLIENT_CONFIG_MAP_NAME}" + key: BAGUETTE_SERVER_ADDRESS + - name: BAGUETTE_SERVER_PORT + valueFrom: + configMapKeyRef: + name: "${EMS_CLIENT_CONFIG_MAP_NAME}" + key: BAGUETTE_SERVER_PORT + - name: BAGUETTE_SERVER_PUBKEY + valueFrom: + configMapKeyRef: + name: "${EMS_CLIENT_CONFIG_MAP_NAME}" + key: BAGUETTE_SERVER_PUBKEY + - name: BAGUETTE_SERVER_PUBKEY_FINGERPRINT + valueFrom: + configMapKeyRef: + name: "${EMS_CLIENT_CONFIG_MAP_NAME}" + key: BAGUETTE_SERVER_PUBKEY_FINGERPRINT + - name: BAGUETTE_SERVER_PUBKEY_FORMAT + valueFrom: + configMapKeyRef: + name: "${EMS_CLIENT_CONFIG_MAP_NAME}" + key: BAGUETTE_SERVER_PUBKEY_FORMAT + - name: BAGUETTE_SERVER_PUBKEY_ALGORITHM + valueFrom: + configMapKeyRef: + name: "${EMS_CLIENT_CONFIG_MAP_NAME}" + key: BAGUETTE_SERVER_PUBKEY_ALGORITHM + - name: BAGUETTE_SERVER_USERNAME + valueFrom: + configMapKeyRef: + name: "${EMS_CLIENT_CONFIG_MAP_NAME}" + key: BAGUETTE_SERVER_USERNAME + - name: BAGUETTE_SERVER_PASSWORD + valueFrom: + configMapKeyRef: + name: "${EMS_CLIENT_CONFIG_MAP_NAME}" + key: BAGUETTE_SERVER_PASSWORD + ports: + - name: openwire + containerPort: 61616 + protocol: TCP + - name: openwire-tls + containerPort: 61617 + protocol: TCP + - name: stomp + containerPort: 61610 + protocol: TCP \ No newline at end of file diff --git a/nebulous/ems-core/baguette-client/Dockerfile b/nebulous/ems-core/baguette-client/Dockerfile new file mode 100644 index 0000000..4990b0c --- /dev/null +++ b/nebulous/ems-core/baguette-client/Dockerfile @@ -0,0 +1,47 @@ +# +# Copyright (C) 2017-2023 Institute of Communication and Computer Systems (imu.iccs.gr) +# +# This Source Code Form is subject to the terms of the Mozilla Public License, v2.0, unless +# Esper library is used, in which case it is subject to the terms of General Public License v2.0. +# If a copy of the MPL was not distributed with this file, you can obtain one at +# https://www.mozilla.org/en-US/MPL/2.0/ +# + +ARG RUN_IMAGE=eclipse-temurin +ARG RUN_IMAGE_TAG=21.0.1_12-jre + +# ----------------- Run image ----------------- +FROM $RUN_IMAGE:$RUN_IMAGE_TAG + +# Install required and optional packages +RUN apt-get update \ + && apt-get install -y vim iputils-ping \ + && rm -rf /var/lib/apt/lists/* + +# Add an EMS user +ARG EMS_USER=emsuser +ARG EMS_HOME=/opt/baguette-client +RUN mkdir -p ${EMS_HOME} && \ + addgroup ${EMS_USER} && \ + adduser --home ${EMS_HOME} --no-create-home --ingroup ${EMS_USER} --disabled-password ${EMS_USER} && \ + chown ${EMS_USER}:${EMS_USER} ${EMS_HOME} + +USER ${EMS_USER} +WORKDIR ${EMS_HOME} + +# Setup environment +ARG EMS_CONFIG_DIR=${EMS_HOME}/conf +ARG JAVA_HOME=/opt/java/openjdk +ARG PATH=$JAVA_HOME/bin:$PATH +ARG INSTALLATION_PACKAGE=baguette-client-installation-package.tgz + +# Copy Baguette Client files +COPY --chown=${EMS_USER}:${EMS_USER} target/$INSTALLATION_PACKAGE /tmp +RUN tar zxvf /tmp/$INSTALLATION_PACKAGE -C /opt && rm -f /tmp/$INSTALLATION_PACKAGE +COPY --chown=${EMS_USER}:${EMS_USER} conf/* ${EMS_HOME}/conf/ + +EXPOSE 61610 +EXPOSE 61616 +EXPOSE 61617 + +ENTRYPOINT ["/bin/sh", "-c", "/opt/baguette-client/bin/run.sh && tail -f /opt/baguette-client/logs/output.txt"] \ No newline at end of file diff --git a/nebulous/ems-core/baguette-client/bin/install.sh b/nebulous/ems-core/baguette-client/bin/install.sh index 9c841dd..8e4d3fd 100644 --- a/nebulous/ems-core/baguette-client/bin/install.sh +++ b/nebulous/ems-core/baguette-client/bin/install.sh @@ -41,9 +41,9 @@ date -Iseconds # Common variables DOWNLOAD_URL=$BASE_URL/baguette-client.tgz -DOWNLOAD_URL_MD5=$BASE_URL/baguette-client.tgz.md5 +DOWNLOAD_URL_SHA256=$BASE_URL/baguette-client.tgz.sha256 INSTALL_PACKAGE=/opt/baguette-client/baguette-client.tgz -INSTALL_PACKAGE_MD5=/opt/baguette-client/baguette-client.tgz.md5 +INSTALL_PACKAGE_SHA256=/opt/baguette-client/baguette-client.tgz.sha256 INSTALL_DIR=/opt/ STARTUP_SCRIPT=$BIN_DIRECTORY/baguette-client SERVICE_NAME=baguette-client @@ -86,34 +86,34 @@ fi date -Iseconds echo "Download installation package...ok" -# Download installation package MD5 checksum +# Download installation package SHA256 checksum echo "" -echo "Download installation package MD5 checksum..." +echo "Download installation package SHA256 checksum..." date -Iseconds -wget $SERVER_CERT $DOWNLOAD_URL_MD5 -O $INSTALL_PACKAGE_MD5 +wget $SERVER_CERT $DOWNLOAD_URL_SHA256 -O $INSTALL_PACKAGE_SHA256 if [ $? != 0 ]; then echo "Failed to download installation package ($?)" echo "Aborting installation..." date -Iseconds - echo "ABORT: download MD5: `date -Iseconds`" >> $INSTALL_LOG + echo "ABORT: download SHA256: `date -Iseconds`" >> $INSTALL_LOG exit 1 fi date -Iseconds -echo "Download installation package MD5 checksum...ok" +echo "Download installation package SHA256 checksum...ok" -# Check MD5 checksum -PACKAGE_MD5=`cat $INSTALL_PACKAGE_MD5` -PACKAGE_CHECKSUM=`md5sum $INSTALL_PACKAGE |cut -d " " -f 1` +# Check SHA256 checksum +PACKAGE_SHA256=`cat $INSTALL_PACKAGE_SHA256` +PACKAGE_CHECKSUM=`sha256sum $INSTALL_PACKAGE |cut -d " " -f 1` echo "" -echo "Checksum MD5: $PACKAGE_MD5" +echo "Checksum SHA256: $PACKAGE_SHA256" echo "Checksum calc: $PACKAGE_CHECKSUM" -if [ $PACKAGE_CHECKSUM == $PACKAGE_MD5 ]; then +if [ $PACKAGE_CHECKSUM == $PACKAGE_SHA256 ]; then echo "Checksum: ok" else echo "Checksum: wrong" echo "Aborting installation..." date -Iseconds - echo "ABORT: wrong MD5: `date -Iseconds`" >> $INSTALL_LOG + echo "ABORT: wrong SHA256: `date -Iseconds`" >> $INSTALL_LOG exit 1 fi diff --git a/nebulous/ems-core/baguette-client/bin/jre-install.sh b/nebulous/ems-core/baguette-client/bin/jre-install.sh new file mode 100644 index 0000000..247c1f8 --- /dev/null +++ b/nebulous/ems-core/baguette-client/bin/jre-install.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash + +arch=$(uname -m) +bits=$(getconf LONG_BIT) +echo "Architecture: $arch, Bits: $bits" + +[[ "$bits" == "32" ]] && [[ "${arch,,}" = arm* ]] && TARGET="ARM-32" +[[ "$bits" == "64" ]] && [[ "${arch,,}" = arm* ]] && TARGET="ARM-64" +[[ "$bits" == "64" ]] && [[ "${arch,,}" = amd* ]] && TARGET="x86-64" +[[ "$bits" == "64" ]] && [[ "${arch,,}" = x86* ]] && TARGET="x86-64" + +DOWNLOAD_URL="https://download.bell-sw.com/java/21.0.2+14" +[[ "$TARGET" == "ARM-32" ]] && PACKAGE="bellsoft-jre21.0.2+14-linux-arm32-vfp-hflt-full.tar.gz" +[[ "$TARGET" == "ARM-64" ]] && PACKAGE="bellsoft-jre21.0.2+14-linux-aarch64-full.tar.gz" +[[ "$TARGET" == "x86-64" ]] && PACKAGE="bellsoft-jre21.0.2+14-linux-amd64-full.tar.gz" + +if [[ "$TARGET" == "" ]]; then + echo "Target device not supported" + exit 1 +else + echo "Target device: $TARGET" + curl -k $DOWNLOAD_URL/$PACKAGE -o /tmp/jre.tar.gz + ls -l +fi diff --git a/nebulous/ems-core/baguette-client/bin/run.bat b/nebulous/ems-core/baguette-client/bin/run.bat index a38a299..fc015e4 100644 --- a/nebulous/ems-core/baguette-client/bin/run.bat +++ b/nebulous/ems-core/baguette-client/bin/run.bat @@ -12,32 +12,46 @@ setlocal set PWD=%~dp0 cd %PWD%.. set BASEDIR=%cd% + IF NOT DEFINED EMS_CONFIG_DIR set EMS_CONFIG_DIR=%BASEDIR%\conf -IF NOT DEFINED PAASAGE_CONFIG_DIR set PAASAGE_CONFIG_DIR=%BASEDIR%\conf +:: IF NOT DEFINED PAASAGE_CONFIG_DIR set PAASAGE_CONFIG_DIR=%BASEDIR%\conf IF NOT DEFINED EMS_CONFIG_LOCATION set EMS_CONFIG_LOCATION=optional:file:%EMS_CONFIG_DIR%\ems-client.yml,optional:file:%EMS_CONFIG_DIR%\ems-client.properties,optional:file:%EMS_CONFIG_DIR%\baguette-client.yml,optional:file:%EMS_CONFIG_DIR%\baguette-client.properties -IF NOT DEFINED JASYPT_PASSWORD set JASYPT_PASSWORD=password -set JAVA_HOME=%BASEDIR%/jre +set LOG_FILE=%BASEDIR%/logs/output.txt +:: IF NOT DEFINED JASYPT_PASSWORD set JASYPT_PASSWORD=password +IF NOT DEFINED JAVA_HOME IF EXIST %BASEDIR%\jre\ set JAVA_HOME=%BASEDIR%/jre :: Update path set PATH=%JAVA_HOME%\bin;%PATH% +:: Source external environment variables file +if DEFINED EMS_EXTRA_ENV_VARS_FILE CALL %EMS_EXTRA_ENV_VARS_FILE% + :: Copy dependencies if missing if exist pom.xml ( if not exist %BASEDIR%\target\dependency cmd /C "mvn dependency:copy-dependencies" ) -:: Run Baguette Client -set JAVA_OPTS= -Djavax.net.ssl.trustStore=%EMS_CONFIG_DIR%\client-broker-truststore.p12 ^ - -Djavax.net.ssl.trustStorePassword=melodic ^ - -Djavax.net.ssl.trustStoreType=pkcs12 ^ - -Djasypt.encryptor.password=%JASYPT_PASSWORD% ^ - --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED +:: Set JAVA_OPTS +::set JAVA_OPTS= -Djavax.net.ssl.trustStore=%EMS_CONFIG_DIR%\client-broker-truststore.p12 ^ +:: -Djavax.net.ssl.trustStorePassword=melodic ^ +:: -Djavax.net.ssl.trustStoreType=pkcs12 ^ ::set JAVA_OPTS=-Djavax.net.debug=all %JAVA_OPTS% ::set JAVA_OPTS=-Dlogging.level.gr.iccs.imu.ems=TRACE %JAVA_OPTS% +set JAVA_OPTS=%JAVA_OPTS% -Djasypt.encryptor.password=%JASYPT_PASSWORD% +set JAVA_OPTS=%JAVA_OPTS% --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED +:: Print settings +echo Starting baguette client... echo EMS_CONFIG_DIR=%EMS_CONFIG_DIR% echo EMS_CONFIG_LOCATION=%EMS_CONFIG_LOCATION% -echo Starting baguette client... +echo LOG_FILE=%LOG_FILE% + +echo Starting baguette client... >> %LOG_FILE% +echo EMS_CONFIG_DIR=%EMS_CONFIG_DIR% >> %LOG_FILE% +echo EMS_CONFIG_LOCATION=%EMS_CONFIG_LOCATION% >> %LOG_FILE% +echo LOG_FILE=%LOG_FILE% >> %LOG_FILE% + +:: Run Baguette Client java %JAVA_OPTS% -classpath "%EMS_CONFIG_DIR%;%BASEDIR%\jars\*;%BASEDIR%\target\classes;%BASEDIR%\target\dependency\*" gr.iccs.imu.ems.baguette.client.BaguetteClient "--spring.config.location=%EMS_CONFIG_LOCATION%" "--logging.config=file:%EMS_CONFIG_DIR%\logback-spring.xml" %* cd %PWD% diff --git a/nebulous/ems-core/baguette-client/bin/run.sh b/nebulous/ems-core/baguette-client/bin/run.sh index e9cd9fc..df84fce 100644 --- a/nebulous/ems-core/baguette-client/bin/run.sh +++ b/nebulous/ems-core/baguette-client/bin/run.sh @@ -12,18 +12,28 @@ PREVWORKDIR=`pwd` BASEDIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd .. && pwd ) cd ${BASEDIR} + EMS_CONFIG_DIR=${BASEDIR}/conf -PAASAGE_CONFIG_DIR=${BASEDIR}/conf +#PAASAGE_CONFIG_DIR=${BASEDIR}/conf EMS_CONFIG_LOCATION=optional:file:$EMS_CONFIG_DIR/ems-client.yml,optional:file:$EMS_CONFIG_DIR/ems-client.properties,optional:file:$EMS_CONFIG_DIR/baguette-client.yml,optional:file:$EMS_CONFIG_DIR/baguette-client.properties LOG_FILE=${BASEDIR}/logs/output.txt TEE_FILE=${BASEDIR}/logs/tee.txt -JASYPT_PASSWORD=password -JAVA_HOME=${BASEDIR}/jre -export EMS_CONFIG_DIR PAASAGE_CONFIG_DIR LOG_FILE JASYPT_PASSWORD JAVA_HOME +#JASYPT_PASSWORD=password +export HOST_IP=1.2.3.4 + +[ -z "${JAVA_HOME}" ] && [ -d "${BASEDIR}/jre" ] && JAVA_HOME=${BASEDIR}/jre +#export EMS_CONFIG_DIR PAASAGE_CONFIG_DIR LOG_FILE JASYPT_PASSWORD JAVA_HOME +export EMS_CONFIG_DIR LOG_FILE JAVA_HOME # Update path PATH=${JAVA_HOME}/bin:$PATH +# Source external environment variables file +if [ "$EMS_EXTRA_ENV_VARS_FILE" != "" ]; then + echo "Sourcing $EMS_EXTRA_ENV_VARS_FILE..." + source $EMS_EXTRA_ENV_VARS_FILE +fi + # Check if baguette client is already running #PID=`jps | grep BaguetteClient | cut -d " " -f 1` PID=`ps -ef |grep java |grep BaguetteClient | cut -c 10-14` @@ -40,14 +50,15 @@ if [ -f pom.xml ]; then fi fi -# Run Baguette client -JAVA_OPTS=-Djavax.net.ssl.trustStore=${EMS_CONFIG_DIR}/client-broker-truststore.p12 -JAVA_OPTS="${JAVA_OPTS} -Djavax.net.ssl.trustStorePassword=melodic -Djavax.net.ssl.trustStoreType=pkcs12" -JAVA_OPTS="${JAVA_OPTS} -Djasypt.encryptor.password=$JASYPT_PASSWORD" +# Set JAVA_OPTS +#JAVA_OPTS=-Djavax.net.ssl.trustStore=${EMS_CONFIG_DIR}/client-broker-truststore.p12 +#JAVA_OPTS="${JAVA_OPTS} -Djavax.net.ssl.trustStorePassword=melodic -Djavax.net.ssl.trustStoreType=pkcs12" #JAVA_OPTS="-Djavax.net.debug=all ${JAVA_OPTS}" #JAVA_OPTS="-Dlogging.level.gr.iccs.imu.ems=TRACE ${JAVA_OPTS}" +JAVA_OPTS="${JAVA_OPTS} -Djasypt.encryptor.password=$JASYPT_PASSWORD" JAVA_OPTS="${JAVA_OPTS} --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED" +# Print settings echo "Starting baguette client..." echo "EMS_CONFIG_DIR=${EMS_CONFIG_DIR}" echo "EMS_CONFIG_LOCATION=${EMS_CONFIG_LOCATION}" @@ -58,14 +69,18 @@ echo "EMS_CONFIG_DIR=${EMS_CONFIG_DIR}" &>> ${LOG_FILE} echo "EMS_CONFIG_LOCATION=${EMS_CONFIG_LOCATION}" &>> ${LOG_FILE} echo "LOG_FILE=${LOG_FILE}" &>> ${LOG_FILE} +# Run Baguette Client if [ "$1" == "--i" ]; then echo "Baguette client running in Interactive mode" - java ${JAVA_OPTS} -classpath "conf:jars/*:target/classes:target/dependency/*" gr.iccs.imu.ems.baguette.client.BaguetteClient "--spring.config.location=${EMS_CONFIG_LOCATION}" "--logging.config=file:${EMS_CONFIG_DIR}/logback-spring.xml" $* $* 2>&1 | tee ${TEE_FILE} + java ${JAVA_OPTS} -classpath "conf:jars/*:target/classes:target/dependency/*" gr.iccs.imu.ems.baguette.client.BaguetteClient "--spring.config.location=${EMS_CONFIG_LOCATION}" "--logging.config=file:${EMS_CONFIG_DIR}/logback-spring.xml" $* 2>&1 | tee ${TEE_FILE} else java ${JAVA_OPTS} -classpath "conf:jars/*:target/classes:target/dependency/*" gr.iccs.imu.ems.baguette.client.BaguetteClient "--spring.config.location=${EMS_CONFIG_LOCATION}" "--logging.config=file:${EMS_CONFIG_DIR}/logback-spring.xml" $* &>> ${LOG_FILE} & - PID=`jps | grep BaguetteClient | cut -d " " -f 1` - PID=`ps -ef |grep java |grep BaguetteClient | cut -c 10-14` - echo "Baguette client PID: $PID" + if command -v jps + then + PID=`jps | grep BaguetteClient | cut -d " " -f 1` + PID=`ps -ef |grep java |grep BaguetteClient | cut -c 10-14` + echo "Baguette client PID: $PID" + fi fi cd $PREVWORKDIR \ No newline at end of file diff --git a/nebulous/ems-core/baguette-client/conf/baguette-client.properties.sample b/nebulous/ems-core/baguette-client/conf/baguette-client.properties.sample index ab88d1c..1a1b7f8 100644 --- a/nebulous/ems-core/baguette-client/conf/baguette-client.properties.sample +++ b/nebulous/ems-core/baguette-client/conf/baguette-client.properties.sample @@ -11,12 +11,20 @@ ### EMS - Baguette Client properties ### ################################################################################ -#password-encoder-class = password.gr.iccs.imu.ems.util.AsterisksPasswordEncoder -#password-encoder-class = password.gr.iccs.imu.ems.util.IdentityPasswordEncoder -#password-encoder-class = password.gr.iccs.imu.ems.util.PresentPasswordEncoder +#password-encoder-class = gr.iccs.imu.ems.util.password.AsterisksPasswordEncoder +#password-encoder-class = gr.iccs.imu.ems.util.password.IdentityPasswordEncoder +#password-encoder-class = gr.iccs.imu.ems.util.password.PresentPasswordEncoder + +### Jasypt encryptor settings (using old settings until encrypted texts are updated) +jasypt.encryptor.algorithm = PBEWithMD5AndDES +jasypt.encryptor.ivGeneratorClassname = org.jasypt.iv.NoIvGenerator # Baguette Client configuration +baseDir = ${BAGUETTE_CLIENT_BASE_DIR} +connection-retry-enabled = true +connection-retry-delay = 10000 +connection-retry-limit = -1 auth-timeout = 60000 exec-timeout = 120000 #retry-period = 60000 @@ -38,7 +46,9 @@ client-id = ${BAGUETTE_CLIENT_ID} server-address = ${BAGUETTE_SERVER_ADDRESS} server-port = ${BAGUETTE_SERVER_PORT} server-pubkey = ${BAGUETTE_SERVER_PUBKEY} -server-fingerprint = ${BAGUETTE_SERVER_PUBKEY_FINGERPRINT} +server-pubkey-fingerprint = ${BAGUETTE_SERVER_PUBKEY_FINGERPRINT} +server-pubkey-algorithm = ${BAGUETTE_SERVER_PUBKEY_ALGORITHM} +server-pubkey-format = ${BAGUETTE_SERVER_PUBKEY_FORMAT} server-username = ${BAGUETTE_SERVER_USERNAME} server-password = ${BAGUETTE_SERVER_PASSWORD} @@ -58,7 +68,7 @@ server-password = ${BAGUETTE_SERVER_PASSWORD} # Collectors settings # ----------------------------------------------------------------------------- -#collector-classes = netdata.collector.gr.iccs.imu.ems.baguette.client.NetdataCollector +#collector-classes = gr.iccs.imu.ems.baguette.client.collector.netdata.NetdataCollector collector.netdata.enable = true collector.netdata.delay = 10000 @@ -141,8 +151,8 @@ brokercep.broker-protocol = ssl BROKER_URL_PROPERTIES = transport.daemon=true&transport.trace=false&transport.useKeepAlive=true&transport.useInactivityMonitor=false&transport.needClientAuth=${CLIENT_AUTH_REQUIRED}&transport.verifyHostName=true&transport.connectionTimeout=0&transport.keepAlive=true CLIENT_AUTH_REQUIRED = false brokercep.broker-url[0] = ${brokercep.broker-protocol}://0.0.0.0:${brokercep.broker-port}?${BROKER_URL_PROPERTIES} -brokercep.broker-url[1] = tcp://127.0.0.1:61616?${BROKER_URL_PROPERTIES} -brokercep.broker-url[2] = +brokercep.broker-url[1] = tcp://${BROKER_URL_ADDRESS_INSECURE:127.0.0.1}:61616?${BROKER_URL_PROPERTIES} +brokercep.broker-url[2] = stomp://${BROKER_URL_ADDRESS_INSECURE:127.0.0.1}:61610?${BROKER_URL_PROPERTIES} CLIENT_URL_PROPERTIES=daemon=true&trace=false&useInactivityMonitor=false&connectionTimeout=0&keepAlive=true brokercep.broker-url-for-consumer = tcp://127.0.0.1:61616?${CLIENT_URL_PROPERTIES} @@ -153,12 +163,14 @@ brokercep.broker-url-for-clients = ${brokercep.broker-protocol}://${EMS_CLIENT_A brokercep.ssl.keystore-file = ${EMS_CONFIG_DIR}/client-broker-keystore.p12 brokercep.ssl.keystore-type = PKCS12 #brokercep.ssl.keystore-password = melodic -brokercep.ssl.keystore-password = ENC(ISMbn01HVPbtRPkqm2Lslg==) +#brokercep.ssl.keystore-password = ENC(ISMbn01HVPbtRPkqm2Lslg==) +brokercep.ssl.keystore-password = ${EMS_KEYSTORE_PASSWORD} # Trust store brokercep.ssl.truststore-file = ${EMS_CONFIG_DIR}/client-broker-truststore.p12 brokercep.ssl.truststore-type = PKCS12 #brokercep.ssl.truststore-password = melodic -brokercep.ssl.truststore-password = ENC(ISMbn01HVPbtRPkqm2Lslg==) +#brokercep.ssl.truststore-password = ENC(ISMbn01HVPbtRPkqm2Lslg==) +brokercep.ssl.truststore-password = ${EMS_TRUSTSTORE_PASSWORD} # Certificate brokercep.ssl.certificate-file = ${EMS_CONFIG_DIR}/client-broker.crt # Key-and-Cert data @@ -170,7 +182,8 @@ brokercep.ssl.key-entry-ext-san = dns:localhost,ip:127.0.0.1,ip:${DEFAULT_IP},ip # Authentication and Authorization settings brokercep.authentication-enabled = true #brokercep.additional-broker-credentials = aaa/111, bbb/222, morphemic/morphemic -brokercep.additional-broker-credentials = ENC(axeJUxNHajYfBffUwvuT3kwTgLTpRliDMz/ZQ9hROZ3BNOv0Idw72NJsawzIZRuZ) +#brokercep.additional-broker-credentials = ENC(axeJUxNHajYfBffUwvuT3kwTgLTpRliDMz/ZQ9hROZ3BNOv0Idw72NJsawzIZRuZ) +brokercep.additional-broker-credentials = ${EMS_CLIENT_ADDITIONAL_BROKER_CREDENTIALS} brokercep.authorization-enabled = false # Broker instance settings @@ -184,14 +197,14 @@ brokercep.broker-using-shutdown-hook = false # Message interceptors brokercep.message-interceptors[0].destination = > -brokercep.message-interceptors[0].className = interceptor.broker.gr.iccs.imu.ems.brokercep.SequentialCompositeInterceptor +brokercep.message-interceptors[0].className = gr.iccs.imu.ems.brokercep.broker.interceptor.SequentialCompositeInterceptor brokercep.message-interceptors[0].params[0] = #SourceAddressMessageUpdateInterceptor brokercep.message-interceptors[0].params[1] = #MessageForwarderInterceptor brokercep.message-interceptors[0].params[2] = #NodePropertiesMessageUpdateInterceptor -brokercep.message-interceptors-specs.SourceAddressMessageUpdateInterceptor.className = interceptor.broker.gr.iccs.imu.ems.brokercep.SourceAddressMessageUpdateInterceptor -brokercep.message-interceptors-specs.MessageForwarderInterceptor.className = interceptor.broker.gr.iccs.imu.ems.brokercep.MessageForwarderInterceptor -brokercep.message-interceptors-specs.NodePropertiesMessageUpdateInterceptor.className = interceptor.broker.gr.iccs.imu.ems.brokercep.NodePropertiesMessageUpdateInterceptor +brokercep.message-interceptors-specs.SourceAddressMessageUpdateInterceptor.className = gr.iccs.imu.ems.brokercep.broker.interceptor.SourceAddressMessageUpdateInterceptor +brokercep.message-interceptors-specs.MessageForwarderInterceptor.className = gr.iccs.imu.ems.brokercep.broker.interceptor.MessageForwarderInterceptor +brokercep.message-interceptors-specs.NodePropertiesMessageUpdateInterceptor.className = gr.iccs.imu.ems.brokercep.broker.interceptor.NodePropertiesMessageUpdateInterceptor # Message forward destinations (MessageForwarderInterceptor must be included in 'message-interceptors' property) #brokercep.message-forward-destinations[0].connection-string = tcp://localhost:51515 diff --git a/nebulous/ems-core/baguette-client/conf/baguette-client.yml b/nebulous/ems-core/baguette-client/conf/baguette-client.yml index 0920f57..76602ef 100644 --- a/nebulous/ems-core/baguette-client/conf/baguette-client.yml +++ b/nebulous/ems-core/baguette-client/conf/baguette-client.yml @@ -11,12 +11,22 @@ ### EMS - Baguette Client properties ### ################################################################################ -#password-encoder-class: password.gr.iccs.imu.ems.util.AsterisksPasswordEncoder -#password-encoder-class: password.gr.iccs.imu.ems.util.IdentityPasswordEncoder -#password-encoder-class: password.gr.iccs.imu.ems.util.PresentPasswordEncoder +#password-encoder-class: gr.iccs.imu.ems.util.password.AsterisksPasswordEncoder +#password-encoder-class: gr.iccs.imu.ems.util.password.IdentityPasswordEncoder +#password-encoder-class: gr.iccs.imu.ems.util.password.PresentPasswordEncoder + +### Jasypt encryptor settings (using old settings until encrypted texts are updated) +jasypt: + encryptor: + algorithm: PBEWithMD5AndDES + ivGeneratorClassname: org.jasypt.iv.NoIvGenerator # Baguette Client configuration +baseDir: ${BAGUETTE_CLIENT_BASE_DIR} +connection-retry-enabled: true +connection-retry-delay: 10000 +connection-retry-limit: -1 auth-timeout: 60000 exec-timeout: 120000 #retry-period: 60000 @@ -46,7 +56,9 @@ client-id: ${BAGUETTE_CLIENT_ID} server-address: ${BAGUETTE_SERVER_ADDRESS} server-port: ${BAGUETTE_SERVER_PORT} server-pubkey: ${BAGUETTE_SERVER_PUBKEY} -server-fingerprint: ${BAGUETTE_SERVER_PUBKEY_FINGERPRINT} +server-pubkey-fingerprint: ${BAGUETTE_SERVER_PUBKEY_FINGERPRINT} +server-pubkey-algorithm: ${BAGUETTE_SERVER_PUBKEY_ALGORITHM} +server-pubkey-format: ${BAGUETTE_SERVER_PUBKEY_FORMAT} server-username: ${BAGUETTE_SERVER_USERNAME} server-password: ${BAGUETTE_SERVER_PASSWORD} @@ -69,7 +81,9 @@ server-password: ${BAGUETTE_SERVER_PASSWORD} # Collectors settings # ----------------------------------------------------------------------------- -#collector-classes: netdata.collector.gr.iccs.imu.ems.baguette.client.NetdataCollector +#collector-classes: gr.iccs.imu.ems.baguette.client.collector.netdata.NetdataCollector + +collector-configurations: ${COLLECTOR_CONFIGURATIONS} collector: netdata: @@ -162,7 +176,8 @@ brokercep: # Broker connectors broker-url: - ${brokercep.broker-protocol}://0.0.0.0:${brokercep.broker-port}?${BROKER_URL_PROPERTIES} - - tcp://127.0.0.1:61616?${BROKER_URL_PROPERTIES} + - tcp://${BROKER_URL_ADDRESS_INSECURE:127.0.0.1}:61616?${BROKER_URL_PROPERTIES} + - stomp://${BROKER_URL_ADDRESS_INSECURE:127.0.0.1}:61610?${BROKER_URL_PROPERTIES} # Broker URLs for (EMS) consumer and clients broker-url-for-consumer: tcp://127.0.0.1:61616?${CLIENT_URL_PROPERTIES} @@ -173,12 +188,14 @@ brokercep: # Key store settings keystore-file: ${EMS_CONFIG_DIR}/client-broker-keystore.p12 keystore-type: PKCS12 - keystore-password: 'ENC(ISMbn01HVPbtRPkqm2Lslg==)' # melodic + #keystore-password: 'ENC(ISMbn01HVPbtRPkqm2Lslg==)' # melodic + keystore-password: ${EMS_KEYSTORE_PASSWORD:${random.value}} # Trust store settings truststore-file: ${EMS_CONFIG_DIR}/client-broker-truststore.p12 truststore-type: PKCS12 - truststore-password: 'ENC(ISMbn01HVPbtRPkqm2Lslg==)' # melodic + #truststore-password: 'ENC(ISMbn01HVPbtRPkqm2Lslg==)' # melodic + truststore-password: ${EMS_TRUSTSTORE_PASSWORD:${random.value}} # Certificate settings certificate-file: ${EMS_CONFIG_DIR}/client-broker.crt @@ -192,7 +209,8 @@ brokercep: # Authentication and Authorization settings authentication-enabled: true #additional-broker-credentials: aaa/111, bbb/222, morphemic/morphemic - additional-broker-credentials: 'ENC(axeJUxNHajYfBffUwvuT3kwTgLTpRliDMz/ZQ9hROZ3BNOv0Idw72NJsawzIZRuZ)' + #additional-broker-credentials: 'ENC(axeJUxNHajYfBffUwvuT3kwTgLTpRliDMz/ZQ9hROZ3BNOv0Idw72NJsawzIZRuZ)' + additional-broker-credentials: ${EMS_CLIENT_ADDITIONAL_BROKER_CREDENTIALS} authorization-enabled: false # Broker instance settings @@ -207,7 +225,7 @@ brokercep: # Message interceptors message-interceptors: - destination: '>' - className: 'interceptor.broker.gr.iccs.imu.ems.brokercep.SequentialCompositeInterceptor' + className: 'gr.iccs.imu.ems.brokercep.broker.interceptor.SequentialCompositeInterceptor' params: - '#SourceAddressMessageUpdateInterceptor' - '#MessageForwarderInterceptor' @@ -215,11 +233,11 @@ brokercep: message-interceptors-specs: SourceAddressMessageUpdateInterceptor: - className: interceptor.broker.gr.iccs.imu.ems.brokercep.SourceAddressMessageUpdateInterceptor + className: gr.iccs.imu.ems.brokercep.broker.interceptor.SourceAddressMessageUpdateInterceptor MessageForwarderInterceptor: - className: interceptor.broker.gr.iccs.imu.ems.brokercep.MessageForwarderInterceptor + className: gr.iccs.imu.ems.brokercep.broker.interceptor.MessageForwarderInterceptor NodePropertiesMessageUpdateInterceptor: - className: interceptor.broker.gr.iccs.imu.ems.brokercep.NodePropertiesMessageUpdateInterceptor + className: gr.iccs.imu.ems.brokercep.broker.interceptor.NodePropertiesMessageUpdateInterceptor # Message forward destinations (MessageForwarderInterceptor must be included in 'message-interceptors' property) #message-forward-destinations: diff --git a/nebulous/ems-core/baguette-client/docker-compose.yml b/nebulous/ems-core/baguette-client/docker-compose.yml new file mode 100644 index 0000000..3cb0348 --- /dev/null +++ b/nebulous/ems-core/baguette-client/docker-compose.yml @@ -0,0 +1,56 @@ +# +# Copyright (C) 2017-2025 Institute of Communication and Computer Systems (imu.iccs.gr) +# +# This Source Code Form is subject to the terms of the Mozilla Public License, v2.0. +# If a copy of the MPL was not distributed with this file, You can obtain one at +# https://www.mozilla.org/en-US/MPL/2.0/ +# + +services: + ems-client: + image: ems-client:2024-feb-nebulous + environment: + # Logging settings +# - LOGGING_LEVEL_GR_ICCS_IMU_EMS_BAGUETTE_CLIENT=TRACE + + # Mode of operation - Common settings + - IP_SETTING=DEFAULT_IP + - SELF_HEALING_ENABLED=false + #- JASYPT_PASSWORD= + #- BAGUETTE_CLIENT_BASE_DIR=/opt/baguette-client + + # This Node info + - NODE_CLIENT_ID=localhost + - BAGUETTE_CLIENT_ID=localhost + + - EMS_CLIENT_ADDRESS=localhost + - NODE_ADDRESS=localhost + - NODE_ADDRESS_PUBLIC=localhost + - NODE_ADDRESS_PRIVATE=localhost + - zone-id= + - provider= + + # EMS server SSH info and credentials + - BAGUETTE_SERVER_ADDRESS=host.docker.internal + - BAGUETTE_SERVER_PORT=2222 + - BAGUETTE_SERVER_PUBKEY=-----BEGIN PUBLIC KEY-----\nMIICRjCCAbkGByqGSM49AgEwggGsAgEBME0GByqGSM49AQECQgH/////////////\n////////////////////////////////////////////////////////////////\n/////////zCBiARCAf//////////////////////////////////////////////\n///////////////////////////////////////8BEIAUZU+uWGOHJofkpohoLaF\nQO6i2nJbmbMV87i0iZGO8QnhVhk5Uex+k3sWUsC9O7G/BzVz34g9LDTx70Uf1GtQ\nPwAEgYUEAMaFjga3BATpzZ4+y2YjlbRCnGSBOQU/tSH4KK9ga009uqFLXnfv51ko\n/h3BJ6L/qN4zSLPBhWpCm/l+fjHC5b1mARg5KWp4mjvABFyKX7QsfRvZmPVESVeb\nRGgXr70XJz5mLJfucple9CZAxVC5AT+tB2E1PHCGonLCQIi+lHaf0WZQAkIB////\n///////////////////////////////////////6UYaHg78vlmt/zAFI9wml0Du1\nybiJnEeuu2+3HpE4ZAkCAQEDgYYABAHedQ6pZnbUFjw+/5B2MQGl/WQsUcRFS0Dy\n7RmSTOsfBlRnEaga7KWPT/lGaSwJfi3OdEMwZYdAN/I7A1HsvqemBgEYUuN96ENP\nGskWCX2qNmfDjxsDpPPXr7lsR9pzafXFpP7PLIvQSFb5UnlzI7edbF6UNVjfjkSY\nY8KnManEvzaMeQ\=\=\n-----END PUBLIC KEY----- + - BAGUETTE_SERVER_PUBKEY_FINGERPRINT=SHA256\:GPn9rx9WWPr+JXlUw0cq8I8tvYLiyadVZswfCevzpN0 + - BAGUETTE_SERVER_PUBKEY_ALGORITHM=EC + - BAGUETTE_SERVER_PUBKEY_FORMAT=X.509 + - BAGUETTE_SERVER_USERNAME=user-45cb8e46-c6bf-4a4b-8e7a-354f7b6d8fc1 + - BAGUETTE_SERVER_PASSWORD=tTUjicVJfrfCurbjecGfBOTxdshA9dOLLjIEo + + # Collectors settings + - COLLECTOR_NETDATA_ENABLE=false + - COLLECTOR_PROMETHEUS_ENABLE=false + - COLLECTOR_ALLOWED_TOPICS= + + # AMQ broker settings + - EMS_KEYSTORE_PASSWORD= + - EMS_TRUSTSTORE_PASSWORD= + - EMS_CLIENT_ADDITIONAL_BROKER_CREDENTIALS= + ports: + - 11016:61616 + - 11017:61617 + - 11010:61610 +# - 1099:19999 \ No newline at end of file diff --git a/nebulous/ems-core/baguette-client/pom.xml b/nebulous/ems-core/baguette-client/pom.xml index 0127385..a08c2ce 100644 --- a/nebulous/ems-core/baguette-client/pom.xml +++ b/nebulous/ems-core/baguette-client/pom.xml @@ -21,6 +21,12 @@ 3.1.12 + + + 0.43.2 + ems-client + emsuser + /opt/baguette-client @@ -74,6 +80,10 @@ org.springframework * + + org.eclipse.jgit + org.eclipse.jgit + @@ -171,5 +181,121 @@ - + + + + dev-local-docker-image-build + + + ../.dev-local-docker-image-build + + + + + + + org.codehaus.mojo + properties-maven-plugin + 1.2.0 + + + read-docker-image-properties + validate + + read-project-properties + + + + + + ../.dev-local-docker-image-build + + + + + + + + + + + build-docker-image + + . + + + + + + maven-antrun-plugin + 3.1.0 + + + set-docker-properties + validate + + run + + + true + + + + + + + + + + + + + + + + + + diff --git a/nebulous/ems-core/baguette-client/src/main/java/gr/iccs/imu/ems/baguette/client/BaguetteClient.java b/nebulous/ems-core/baguette-client/src/main/java/gr/iccs/imu/ems/baguette/client/BaguetteClient.java index 3856ab5..c8e2e90 100644 --- a/nebulous/ems-core/baguette-client/src/main/java/gr/iccs/imu/ems/baguette/client/BaguetteClient.java +++ b/nebulous/ems-core/baguette-client/src/main/java/gr/iccs/imu/ems/baguette/client/BaguetteClient.java @@ -140,6 +140,13 @@ public class BaguetteClient implements ApplicationRunner { try { log.debug("BaguetteClient: Starting collector: {}...", collectorClass.getName()); Collector collector = applicationContext.getBean(collectorClass); + log.debug("BaguetteClient: Starting collector: {}: instance={}", collectorClass.getName(), collector); + if (baguetteClientProperties.getCollectorConfigurations()!=null) { + Object config = baguetteClientProperties.getCollectorConfigurations().get(collector.getName()); + log.debug("BaguetteClient: Starting collector: {}: collector-config={}", collectorClass.getName(), collector); + if (config!=null) + collector.setConfiguration(config); + } collector.start(); collectorsList.add(collector); log.debug("BaguetteClient: Starting collector: {}...ok", collectorClass.getName()); diff --git a/nebulous/ems-core/baguette-client/src/main/java/gr/iccs/imu/ems/baguette/client/BaguetteClientProperties.java b/nebulous/ems-core/baguette-client/src/main/java/gr/iccs/imu/ems/baguette/client/BaguetteClientProperties.java index b2f6037..38ef8bb 100644 --- a/nebulous/ems-core/baguette-client/src/main/java/gr/iccs/imu/ems/baguette/client/BaguetteClientProperties.java +++ b/nebulous/ems-core/baguette-client/src/main/java/gr/iccs/imu/ems/baguette/client/BaguetteClientProperties.java @@ -18,6 +18,7 @@ import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.PropertySource; import java.util.List; +import java.util.Map; @Data @ToString(callSuper = true) @@ -41,6 +42,7 @@ public class BaguetteClientProperties extends SshClientProperties { private int killDelay = 5; private List> collectorClasses; + private Map>> collectorConfigurations; private String debugFakeIpAddress; diff --git a/nebulous/ems-core/baguette-client/src/main/java/gr/iccs/imu/ems/baguette/client/Collector.java b/nebulous/ems-core/baguette-client/src/main/java/gr/iccs/imu/ems/baguette/client/Collector.java index a1a5cbd..84e655d 100644 --- a/nebulous/ems-core/baguette-client/src/main/java/gr/iccs/imu/ems/baguette/client/Collector.java +++ b/nebulous/ems-core/baguette-client/src/main/java/gr/iccs/imu/ems/baguette/client/Collector.java @@ -12,5 +12,7 @@ package gr.iccs.imu.ems.baguette.client; import gr.iccs.imu.ems.util.Plugin; public interface Collector extends Plugin { + String getName(); + void setConfiguration(Object config); void activeGroupingChanged(String oldGrouping, String newGrouping); } diff --git a/nebulous/ems-core/baguette-client/src/main/java/gr/iccs/imu/ems/baguette/client/CommandExecutor.java b/nebulous/ems-core/baguette-client/src/main/java/gr/iccs/imu/ems/baguette/client/CommandExecutor.java index 3f64ed3..9831472 100644 --- a/nebulous/ems-core/baguette-client/src/main/java/gr/iccs/imu/ems/baguette/client/CommandExecutor.java +++ b/nebulous/ems-core/baguette-client/src/main/java/gr/iccs/imu/ems/baguette/client/CommandExecutor.java @@ -371,10 +371,14 @@ public class CommandExecutor { double upper = Double.parseDouble(args[4].trim()); if (eventGenerators.get(destination) == null) { + /* EventGenerator generator = applicationContext.getBean(EventGenerator.class); generator.setBrokerUrl(brokerCepService.getBrokerCepProperties().getBrokerUrlForClients()); generator.setBrokerUsername(brokerCepService.getBrokerUsername()); generator.setBrokerPassword(brokerCepService.getBrokerPassword()); + */ + EventGenerator generator = new EventGenerator((destinationName, event) -> + sendEvent(null, destinationName, event)==CollectorContext.PUBLISH_RESULT.SENT); generator.setDestinationName(destination); generator.setLevel(1); generator.setInterval(interval); diff --git a/nebulous/ems-core/baguette-client/src/main/java/gr/iccs/imu/ems/baguette/client/collector/netdata/K8sNetdataCollector.java b/nebulous/ems-core/baguette-client/src/main/java/gr/iccs/imu/ems/baguette/client/collector/netdata/K8sNetdataCollector.java new file mode 100644 index 0000000..0c0cfb1 --- /dev/null +++ b/nebulous/ems-core/baguette-client/src/main/java/gr/iccs/imu/ems/baguette/client/collector/netdata/K8sNetdataCollector.java @@ -0,0 +1,382 @@ +/* + * Copyright (C) 2017-2023 Institute of Communication and Computer Systems (imu.iccs.gr) + * + * This Source Code Form is subject to the terms of the Mozilla Public License, v2.0, unless + * Esper library is used, in which case it is subject to the terms of General Public License v2.0. + * If a copy of the MPL was not distributed with this file, you can obtain one at + * https://www.mozilla.org/en-US/MPL/2.0/ + */ + +package gr.iccs.imu.ems.baguette.client.collector.netdata; + +import gr.iccs.imu.ems.baguette.client.Collector; +import gr.iccs.imu.ems.baguette.client.collector.ClientCollectorContext; +import gr.iccs.imu.ems.brokercep.event.EventMap; +import gr.iccs.imu.ems.common.collector.CollectorContext; +import gr.iccs.imu.ems.common.collector.netdata.NetdataCollectorProperties; +import gr.iccs.imu.ems.util.EmsConstant; +import gr.iccs.imu.ems.util.EventBus; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.StringUtils; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.scheduling.TaskScheduler; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestClient; + +import java.time.Duration; +import java.util.*; +import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.atomic.AtomicBoolean; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * Collects measurements from Netdata agents in a Kubernetes cluster + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class K8sNetdataCollector implements Collector, InitializingBean { + protected final static Set SENSOR_CONFIG_KEYS_EXCLUDED = Set.of("endpoint", "type", "_containerName"); + protected final static String NETDATA_DATA_API_V1_PATH = "/api/v1/data"; + protected final static String NETDATA_DATA_API_V2_PATH = "/api/v2/data"; + protected final static String DEFAULT_NETDATA_DATA_API_PATH = NETDATA_DATA_API_V2_PATH; + + private final NetdataCollectorProperties properties; + private final CollectorContext collectorContext; + private final TaskScheduler taskScheduler; + private final EventBus eventBus; + private final RestClient restClient = RestClient.create(); + private final List> scheduledFuturesList = new LinkedList<>(); + private boolean started; + private List> configuration; + + @Override + public void afterPropertiesSet() throws Exception { + if (!(collectorContext instanceof ClientCollectorContext)) + throw new IllegalArgumentException("Invalid CollectorContext provided. Expected: ClientCollectorContext, but got "+collectorContext.getClass().getName()); + } + + @Override + public String getName() { + return "netdata"; + } + + @Override + public void setConfiguration(Object config) { + if (config instanceof List sensorConfigList) { + configuration = sensorConfigList.stream() + .filter(o -> o instanceof Map) + .filter(map -> ((Map)map).keySet().stream().allMatch(k->k instanceof String)) + .toList(); + log.debug("K8sNetdataCollector: setConfiguration: {}", configuration); + + // If configuration changes while collector running we need to restart it + if (started) { + log.debug("K8sNetdataCollector: setConfiguration: Restarting collector"); + stop(); + start(); + log.info("K8sNetdataCollector: setConfiguration: Restarted collector"); + } + } else + log.warn("K8sNetdataCollector: setConfiguration: Ignoring unsupported configuration object: {}", config); + } + + @Override + public void start() { + if (started) return; + if (configuration!=null) + doStart(); + started = true; + log.debug("K8sNetdataCollector: Started"); + } + + @Override + public void stop() { + if (!started) return; + started = false; + doStop(); + log.debug("K8sNetdataCollector: Stopped"); + } + + private synchronized void doStart() { + log.debug("K8sNetdataCollector: doStart(): BEGIN: configuration={}", configuration); + log.trace("K8sNetdataCollector: doStart(): BEGIN: scheduledFuturesList={}", scheduledFuturesList); + + // Get Netdata agent address and port from env. vars + String netdataAddress = null; + if (StringUtils.isBlank(netdataAddress)) netdataAddress = System.getenv("NETDATA_ADDRESS"); + if (StringUtils.isBlank(netdataAddress)) netdataAddress = System.getenv("NETDATA_IP"); + if (StringUtils.isBlank(netdataAddress)) netdataAddress = System.getenv("HOST_IP"); + if (StringUtils.isBlank(netdataAddress)) netdataAddress = "127.0.0.1"; + log.trace("K8sNetdataCollector: doStart(): netdataAddress={}", netdataAddress); + + int netdataPort = Integer.parseInt( + StringUtils.defaultIfBlank(System.getenv("NETDATA_PORT"), "19999").trim()); + final String baseUrl = String.format("http://%s:%d", netdataAddress.trim(), netdataPort); + log.trace("K8sNetdataCollector: doStart(): baseUrl={}", baseUrl); + + // Process each sensor configuration + AtomicInteger sensorNum = new AtomicInteger(0); + configuration.forEach(map -> { + log.debug("K8sNetdataCollector: doStart(): Sensor-{}: map={}", sensorNum.incrementAndGet(), map); + + // Check if it is a Pull sensor. (Push sensors are ignored) + if ("true".equalsIgnoreCase( get(map, "pushSensor", "false") )) { + log.debug("K8sNetdataCollector: doStart(): Sensor-{}: It is a Push sensor. Ignoring this sensor", sensorNum.get()); + return; + } + // else it is a Pull sensor + + // Get destination (topic) and component name + String destinationName = get(map, "name", null); + log.trace("K8sNetdataCollector: doStart(): Sensor-{}: destination={}", sensorNum.get(), destinationName); + if (StringUtils.isBlank(destinationName)) { + log.warn("K8sNetdataCollector: doStart(): Sensor-{}: No destination found in sensor config: {}", sensorNum.get(), map); + return; + } + + // Get metric URL + int apiVer; + String url = null; + String component = null; + String context = null; + String dimensions = "*"; + if (map.get("configuration") instanceof Map cfgMap) { + log.trace("K8sNetdataCollector: doStart(): Sensor-{}: cfgMap={}", sensorNum.get(), cfgMap); + + // Get component name + component = get(cfgMap, "_containerName", null); + log.trace("K8sNetdataCollector: doStart(): Sensor-{}: component={}", sensorNum.get(), component); + + // Process 'configuration' map entries, to build metric URL + Map sensorConfig = (Map) cfgMap; + String endpoint = get(sensorConfig, "endpoint", DEFAULT_NETDATA_DATA_API_PATH); + log.trace("K8sNetdataCollector: doStart(): Sensor-{}: endpoint={}", sensorNum.get(), endpoint); + + if (NETDATA_DATA_API_V1_PATH.equalsIgnoreCase(endpoint)) { + apiVer = 1; + + // If expanded by a shorthand expression + context = get(sensorConfig, EmsConstant.NETDATA_METRIC_KEY, null); + if (StringUtils.isNotBlank(context)) + addEntryIfMissingOrBlank(sensorConfig, "context", context); + + // Else check sensor config for 'context' key + context = get(sensorConfig, "context", null); + + addEntryIfMissingOrBlank(sensorConfig, "dimension", "*"); + addEntryIfMissingOrBlank(sensorConfig, "after", "-1"); + addEntryIfMissingOrBlank(sensorConfig, "group", "average"); + addEntryIfMissingOrBlank(sensorConfig, "format", "json2"); + } else + if (NETDATA_DATA_API_V2_PATH.equalsIgnoreCase(endpoint)) { + apiVer = 2; + + // If expanded by a shorthand expression + context = get(sensorConfig, EmsConstant.NETDATA_METRIC_KEY, null); + if (StringUtils.isNotBlank(context)) + addEntryIfMissingOrBlank(sensorConfig, "scope_contexts", context); + + // Else check sensor config for 'scope_contexts' or 'context' key + context = get(sensorConfig, "scope_contexts", null); + if (StringUtils.isBlank(context)) + context = get(sensorConfig, "context", null); + + boolean isK8s = StringUtils.startsWithIgnoreCase(context, "k8s"); + if (isK8s) { + addEntryIfMissingOrBlank(sensorConfig, "group_by", "label"); + addEntryIfMissingOrBlank(sensorConfig, "group_by_label", "k8s_pod_name"); + } + addEntryIfMissingOrBlank(sensorConfig, "dimension", "*"); + addEntryIfMissingOrBlank(sensorConfig, "after", "-1"); + addEntryIfMissingOrBlank(sensorConfig, "time_group", "average"); + addEntryIfMissingOrBlank(sensorConfig, "format", "ssv"); + } else { + log.warn("K8sNetdataCollector: doStart(): Sensor-{}: Invalid Netdata endpoint found in sensor config: {}", sensorNum.get(), map); + return; + } + dimensions = get(sensorConfig, "dimension", dimensions); + + StringBuilder sb = new StringBuilder(endpoint); + final AtomicBoolean first = new AtomicBoolean(true); + sensorConfig.forEach((key, value) -> { + if (StringUtils.isNotBlank(key) && ! SENSOR_CONFIG_KEYS_EXCLUDED.contains(key)) { + if (value instanceof String valueStr) { + sb.append(first.get() ? "?" : "&").append(key).append("=").append(valueStr); + first.set(false); + } + } + }); + + if (StringUtils.isNotBlank(context)) { + url = baseUrl + sb; + } else { + log.warn("K8sNetdataCollector: doStart(): Sensor-{}: No 'context' found in sensor configuration: {}", sensorNum.get(), map); + return; + } + } else { + log.warn("K8sNetdataCollector: doStart(): Sensor-{}: No sensor configuration found is spec: {}", sensorNum.get(), map); + return; + } + log.trace("K8sNetdataCollector: doStart(): Sensor-{}: Metric url={}", sensorNum.get(), url); + + // Get interval and configure scheduler + long period = 60; + TimeUnit unit = TimeUnit.SECONDS; + Duration duration = null; + if (map.get("interval") instanceof Map intervalMap) { + log.trace("K8sNetdataCollector: doStart(): Sensor-{}: intervalMap={}", sensorNum.get(), intervalMap); + period = Long.parseLong(get(intervalMap, "period", Long.toString(period))); + if (period>0) { + String unitStr = get(intervalMap, "unit", unit.name()); + unit = StringUtils.isNotBlank(unitStr) + ? TimeUnit.valueOf(unitStr.toUpperCase().trim()) : TimeUnit.SECONDS; + duration = Duration.of(period, unit.toChronoUnit()); + } + } + log.trace("K8sNetdataCollector: doStart(): Sensor-{}: duration-from-spec={}", sensorNum.get(), duration); + if (duration==null) { + duration = Duration.of(period, unit.toChronoUnit()); + } + log.trace("K8sNetdataCollector: doStart(): Sensor-{}: duration={}", sensorNum.get(), duration); + + final int apiVer1 = apiVer; + final String url1 = url; + final String component1 = component; + scheduledFuturesList.add( taskScheduler.scheduleAtFixedRate(() -> { + collectData(apiVer1, url1, destinationName, component1); + }, duration) ); + log.debug("K8sNetdataCollector: doStart(): Sensor-{}: destination={}, component={}, interval={}, url={}", + sensorNum.get(), destinationName, component, duration, url); + log.info("K8sNetdataCollector: Collecting Netdata metric '{}.{}' into '{}', every {} {}", + context, dimensions, destinationName, period, unit.name().toLowerCase()); + }); + log.trace("K8sNetdataCollector: doStart(): scheduledFuturesList={}", scheduledFuturesList); + log.debug("K8sNetdataCollector: doStart(): END"); + } + + private String get(Map map, String key, String defaultValue) { + Object valObj; + if (!map.containsKey(key) || (valObj = map.get(key))==null) return defaultValue; + String value = valObj.toString(); + if (StringUtils.isBlank(value)) return defaultValue; + return value; + } + + private void addEntryIfMissingOrBlank(Map map, String key, Object value) { + if (map.containsKey(key) && map.get(key)!=null) + if (map.get(key) instanceof String s && StringUtils.isNotBlank(s)) + return; + map.put(key, value); + } + + private void collectData(int apiVer, String url, String destination, String component) { + long startTm = System.currentTimeMillis(); + log.debug("K8sNetdataCollector: collectData(): BEGIN: apiVer={}, url={}, destination={}, component={}", + apiVer, url, destination, component); + + Map resultsMap = new HashMap<>(); + long timestamp = -1L; + if (apiVer==1) { + Map response = restClient.get() + .uri(url) + .retrieve() + .body(Map.class); + // Need to call for each pod separately + // or get the total + timestamp = Long.parseLong( response.get("before").toString() ); + List chart_ids = (List) response.get("chart_ids"); + List dimension_ids = (List) response.get("dimension_ids"); + List latest_values = (List) response.get("view_latest_value"); + for (int i=0, n=chart_ids.size(); i ids = (List) dimensions.get("ids"); + List units = (List) dimensions.get("units"); + List values = (List) ((Map)dimensions.get("sts")).get("avg"); + log.trace("K8sNetdataCollector: collectData(): ids={}", ids); + log.trace("K8sNetdataCollector: collectData(): units={}", units); + log.trace("K8sNetdataCollector: collectData(): values={}", values); + for (int i=0, n=ids.size(); i publishResults = new LinkedHashMap<>(); + resultsMap.forEach((k,v) -> { + publishResults.put( k+"="+v, publishMetricEvent(destination, k, v, timestamp1, null) ); + }); + log.debug("K8sNetdataCollector: collectData(): Events published: results={}", publishResults); + + long endTm = System.currentTimeMillis(); + log.warn("K8sNetdataCollector: collectData(): END: duration={}ms", endTm-startTm); + } + + private synchronized void doStop() { + log.debug("K8sNetdataCollector: doStop(): BEGIN"); + log.trace("K8sNetdataCollector: doStop(): BEGIN: scheduledFuturesList={}", scheduledFuturesList); + // Cancel all task scheduler futures + scheduledFuturesList.forEach(future -> future.cancel(true)); + scheduledFuturesList.clear(); + log.debug("K8sNetdataCollector: doStop(): END"); + } + + public synchronized void activeGroupingChanged(String oldGrouping, String newGrouping) { + log.debug("K8sNetdataCollector: activeGroupingChanged: {} --> {}", oldGrouping, newGrouping); + } + + protected CollectorContext.PUBLISH_RESULT publishMetricEvent(String metricName, String key, double metricValue, long timestamp, String nodeAddress) { + EventMap event = new EventMap(metricValue, 1, timestamp); + return sendEvent(metricName, metricName, key, event, null, true); + } + + private CollectorContext.PUBLISH_RESULT sendEvent(String metricName, String originalTopic, String key, EventMap event, String nodeAddress, boolean createDestination) { + event.setEventProperty(EmsConstant.EVENT_PROPERTY_SOURCE_ADDRESS, nodeAddress); + event.getEventProperties().put(EmsConstant.EVENT_PROPERTY_EFFECTIVE_DESTINATION, metricName); + event.getEventProperties().put(EmsConstant.EVENT_PROPERTY_ORIGINAL_DESTINATION, originalTopic); + event.getEventProperties().put(EmsConstant.EVENT_PROPERTY_KEY, key); + log.debug("K8sNetdataCollector: Publishing metric: {}: {}", metricName, event.getMetricValue()); + CollectorContext.PUBLISH_RESULT result = collectorContext.sendEvent(null, metricName, event, createDestination); + log.trace("K8sNetdataCollector: Publishing metric: {}: {} -> result: {}", metricName, event.getMetricValue(), result); + return result; + } +} diff --git a/nebulous/ems-core/baguette-client/src/main/java/gr/iccs/imu/ems/baguette/client/collector/netdata/NetdataCollector.java b/nebulous/ems-core/baguette-client/src/main/java/gr/iccs/imu/ems/baguette/client/collector/netdata/NetdataCollector.java index 8d3db8a..ef7be39 100644 --- a/nebulous/ems-core/baguette-client/src/main/java/gr/iccs/imu/ems/baguette/client/collector/netdata/NetdataCollector.java +++ b/nebulous/ems-core/baguette-client/src/main/java/gr/iccs/imu/ems/baguette/client/collector/netdata/NetdataCollector.java @@ -21,11 +21,9 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.TaskScheduler; import org.springframework.stereotype.Component; -import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; /** * Collects measurements from Netdata http server @@ -33,6 +31,8 @@ import java.util.stream.Collectors; @Slf4j @Component public class NetdataCollector extends gr.iccs.imu.ems.common.collector.netdata.NetdataCollector implements Collector { + private List> configuration; + public NetdataCollector(@NonNull NetdataCollectorProperties properties, @NonNull CollectorContext collectorContext, @NonNull TaskScheduler taskScheduler, @@ -43,6 +43,22 @@ public class NetdataCollector extends gr.iccs.imu.ems.common.collector.netdata.N throw new IllegalArgumentException("Invalid CollectorContext provided. Expected: ClientCollectorContext, but got "+collectorContext.getClass().getName()); } + @Override + public String getName() { + return "netdata"; + } + + @Override + public void setConfiguration(Object config) { + if (config instanceof List sensorConfigList) { + configuration = sensorConfigList.stream() + .filter(o -> o instanceof Map) + .filter(map -> ((Map)map).keySet().stream().allMatch(k->k instanceof String)) + .toList(); + log.info("Collectors::Netdata: setConfiguration: {}", configuration); + } + } + public synchronized void activeGroupingChanged(String oldGrouping, String newGrouping) { HashSet topics = new HashSet<>(); for (String g : GROUPING.getNames()) { diff --git a/nebulous/ems-core/baguette-client/src/main/java/gr/iccs/imu/ems/baguette/client/collector/prometheus/PrometheusCollector.java b/nebulous/ems-core/baguette-client/src/main/java/gr/iccs/imu/ems/baguette/client/collector/prometheus/PrometheusCollector.java index cea61df..1a088e8 100644 --- a/nebulous/ems-core/baguette-client/src/main/java/gr/iccs/imu/ems/baguette/client/collector/prometheus/PrometheusCollector.java +++ b/nebulous/ems-core/baguette-client/src/main/java/gr/iccs/imu/ems/baguette/client/collector/prometheus/PrometheusCollector.java @@ -21,11 +21,9 @@ import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.TaskScheduler; import org.springframework.stereotype.Component; -import java.util.ArrayList; import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.stream.Collectors; /** * Collects measurements from Prometheus exporter @@ -33,6 +31,8 @@ import java.util.stream.Collectors; @Slf4j @Component public class PrometheusCollector extends gr.iccs.imu.ems.common.collector.prometheus.PrometheusCollector implements Collector { + private List> configuration; + public PrometheusCollector(@NonNull PrometheusCollectorProperties properties, @NonNull CollectorContext collectorContext, @NonNull TaskScheduler taskScheduler, @@ -43,6 +43,22 @@ public class PrometheusCollector extends gr.iccs.imu.ems.common.collector.promet throw new IllegalArgumentException("Invalid CollectorContext provided. Expected: ClientCollectorContext, but got "+collectorContext.getClass().getName()); } + @Override + public String getName() { + return "prometheus"; + } + + @Override + public void setConfiguration(Object config) { + if (config instanceof List sensorConfigList) { + configuration = sensorConfigList.stream() + .filter(o -> o instanceof Map) + .filter(map -> ((Map)map).keySet().stream().allMatch(k->k instanceof String)) + .toList(); + log.info("Collectors::Prometheus: setConfiguration: {}", configuration); + } + } + public synchronized void activeGroupingChanged(String oldGrouping, String newGrouping) { HashSet topics = new HashSet<>(); for (String g : GROUPING.getNames()) { diff --git a/nebulous/ems-core/baguette-server/pom.xml b/nebulous/ems-core/baguette-server/pom.xml index 12a70aa..fe03368 100644 --- a/nebulous/ems-core/baguette-server/pom.xml +++ b/nebulous/ems-core/baguette-server/pom.xml @@ -63,6 +63,10 @@ org.springframework * + + org.eclipse.jgit + org.eclipse.jgit + diff --git a/nebulous/ems-core/baguette-server/src/main/java/gr/iccs/imu/ems/baguette/server/BaguetteServer.java b/nebulous/ems-core/baguette-server/src/main/java/gr/iccs/imu/ems/baguette/server/BaguetteServer.java index 510d636..1efb26b 100644 --- a/nebulous/ems-core/baguette-server/src/main/java/gr/iccs/imu/ems/baguette/server/BaguetteServer.java +++ b/nebulous/ems-core/baguette-server/src/main/java/gr/iccs/imu/ems/baguette/server/BaguetteServer.java @@ -49,6 +49,7 @@ public class BaguetteServer implements InitializingBean, EventBus.EventConsumer< @Getter private final SelfHealingManager selfHealingManager; private final TaskScheduler taskScheduler; + private final ConfigWriteService configWriteService; private Sshd server; @@ -147,6 +148,8 @@ public class BaguetteServer implements InitializingBean, EventBus.EventConsumer< public BrokerCepService getBrokerCepService() { return brokerCepService; } + public Map getServerConnectionInfo() { return server.getServerConnectionInfo(); } + public String getServerPubkey() { return server.getPublicKey(); } public String getServerPubkeyFingerprint() { return server.getPublicKeyFingerprint(); } @@ -162,13 +165,26 @@ public class BaguetteServer implements InitializingBean, EventBus.EventConsumer< if (server == null) { eventBus.subscribe(RecoveryConstant.SELF_HEALING_RECOVERY_GIVE_UP, this); + // Start SSH server log.info("BaguetteServer.startServer(): Starting SSH server..."); nodeRegistry.setCoordinator(coordinator); Sshd server = new Sshd(); server.start(config, coordinator, eventBus, nodeRegistry); - server.setNodeRegistry(getNodeRegistry()); + //server.setNodeRegistry(getNodeRegistry()); this.server = server; log.info("BaguetteServer.startServer(): Starting SSH server... done"); + + // Store Baguette Server connection info in a properties file + try { + configWriteService + .getOrCreateConfigFile( + EmsConstant.EMS_CLIENT_K8S_CONFIG_MAP_FILE, + EmsConstant.EMS_CLIENT_K8S_CONFIG_MAP_FORMAT) + .putAll(server.getServerConnectionInfo()); + } catch (Exception e) { + log.error("BaguetteServer.startServer(): Failed to store connection info in ems-client-config-map: {}, Exception: ", + EmsConstant.EMS_CLIENT_K8S_CONFIG_MAP_FILE, e); + } } else { log.info("BaguetteServer.startServer(): SSH server is already running"); } diff --git a/nebulous/ems-core/baguette-server/src/main/java/gr/iccs/imu/ems/baguette/server/ClientShellCommand.java b/nebulous/ems-core/baguette-server/src/main/java/gr/iccs/imu/ems/baguette/server/ClientShellCommand.java index 7a9de39..672f112 100644 --- a/nebulous/ems-core/baguette-server/src/main/java/gr/iccs/imu/ems/baguette/server/ClientShellCommand.java +++ b/nebulous/ems-core/baguette-server/src/main/java/gr/iccs/imu/ems/baguette/server/ClientShellCommand.java @@ -164,9 +164,23 @@ public class ClientShellCommand implements Command, Runnable, ServerSessionAware if (entry!=null) { setNodeRegistryEntry(entry); } else { - log.error("{}--> initNodeRegistryEntry: No node registry entry found for client: address={}", id, address); - log.error("{}--> initNodeRegistryEntry: Marked client session for immediate close: address={}", id, address); - setCloseConnection(true); + log.info("{}--> initNodeRegistryEntry: No node registry entry found for client: address={}", id, address); + if (coordinator.allowNotPreregisteredNode(this)) { + try { + log.info("{}--> initNodeRegistryEntry: Preregistering new client: address={}", id, address); + HashMap nodeInfo = new HashMap<>(); + nodeInfo.put("address", address); + entry = coordinator.getServer().registerClient(nodeInfo); + setNodeRegistryEntry(entry); + } catch (Exception e) { + log.error("{}--> initNodeRegistryEntry: Exception while registering new client: address={}, EXCEPTION: ", id, address, e); + log.error("{}--> initNodeRegistryEntry: Marked client session for immediate close: address={}", id, address); + setCloseConnection(true); + } + } else { + log.error("{}--> initNodeRegistryEntry: Marked client session for immediate close: address={}", id, address); + setCloseConnection(true); + } } } diff --git a/nebulous/ems-core/baguette-server/src/main/java/gr/iccs/imu/ems/baguette/server/NodeRegistry.java b/nebulous/ems-core/baguette-server/src/main/java/gr/iccs/imu/ems/baguette/server/NodeRegistry.java index 05a7b8e..b7bc1f4 100644 --- a/nebulous/ems-core/baguette-server/src/main/java/gr/iccs/imu/ems/baguette/server/NodeRegistry.java +++ b/nebulous/ems-core/baguette-server/src/main/java/gr/iccs/imu/ems/baguette/server/NodeRegistry.java @@ -38,17 +38,22 @@ public class NodeRegistry { // Get IP address from provided hostname or address Throwable errorObj = null; - try { - log.debug("NodeRegistry.addNode(): Resolving IP address from provided hostname/address: {}", hostnameOrAddress); - InetAddress host = InetAddress.getByName(hostnameOrAddress); - log.trace("NodeRegistry.addNode(): InetAddress for provided hostname/address: {}, InetAddress: {}", hostnameOrAddress, host); - String resolvedIpAddress = host.getHostAddress(); - log.info("NodeRegistry.addNode(): Provided-Address={}, Resolved-IP-Address={}", hostnameOrAddress, resolvedIpAddress); - ipAddress = resolvedIpAddress; - } catch (UnknownHostException e) { - log.error("NodeRegistry.addNode(): EXCEPTION while resolving IP address from provided hostname/address: {}\n", ipAddress, e); - errorObj = e; - //throw e; + if (StringUtils.isNotBlank(ipAddress)) { + try { + log.debug("NodeRegistry.addNode(): Resolving IP address from provided hostname/address: {}", hostnameOrAddress); + InetAddress host = InetAddress.getByName(hostnameOrAddress); + log.trace("NodeRegistry.addNode(): InetAddress for provided hostname/address: {}, InetAddress: {}", hostnameOrAddress, host); + String resolvedIpAddress = host.getHostAddress(); + log.info("NodeRegistry.addNode(): Provided-Address={}, Resolved-IP-Address={}", hostnameOrAddress, resolvedIpAddress); + ipAddress = resolvedIpAddress; + } catch (UnknownHostException e) { + log.error("NodeRegistry.addNode(): EXCEPTION while resolving IP address from provided hostname/address: {}\n", ipAddress, e); + errorObj = e; + //throw e; + } + } else { + ipAddress = "unknown-address-"+System.currentTimeMillis(); // We don't know POD address + nodeInfo.put("address", ipAddress); } nodeInfo.put("original-address", hostnameOrAddress); nodeInfo.put("address", ipAddress); diff --git a/nebulous/ems-core/baguette-server/src/main/java/gr/iccs/imu/ems/baguette/server/Sshd.java b/nebulous/ems-core/baguette-server/src/main/java/gr/iccs/imu/ems/baguette/server/Sshd.java index 5c5da67..a5bccb6 100644 --- a/nebulous/ems-core/baguette-server/src/main/java/gr/iccs/imu/ems/baguette/server/Sshd.java +++ b/nebulous/ems-core/baguette-server/src/main/java/gr/iccs/imu/ems/baguette/server/Sshd.java @@ -142,6 +142,20 @@ public class Sshd { log.info("SSH server: Stopped"); } + public Map getServerConnectionInfo() { + Map.Entry credentials = configuration.getCredentials().getPreferredPair(); + return Map.of( + "BAGUETTE_SERVER_ADDRESS", configuration.getServerAddress(), + "BAGUETTE_SERVER_PORT", Integer.toString(configuration.getServerPort()), + "BAGUETTE_SERVER_PUBKEY", StringEscapeUtils.unescapeJson(serverPubkey), + "BAGUETTE_SERVER_PUBKEY_FINGERPRINT", serverPubkeyFingerprint, + "BAGUETTE_SERVER_PUBKEY_ALGORITHM", serverPubkeyAlgorithm, + "BAGUETTE_SERVER_PUBKEY_FORMAT", serverPubkeyFormat, + "BAGUETTE_SERVER_USERNAME", credentials.getKey(), + "BAGUETTE_SERVER_PASSWORD", credentials.getValue() + ); + } + public void startHeartbeat(long period) { heartbeatOn = true; Thread heartbeat = new Thread( diff --git a/nebulous/ems-core/baguette-server/src/main/java/gr/iccs/imu/ems/baguette/server/coordinator/NoopCoordinator.java b/nebulous/ems-core/baguette-server/src/main/java/gr/iccs/imu/ems/baguette/server/coordinator/NoopCoordinator.java index 4f78422..a070d35 100644 --- a/nebulous/ems-core/baguette-server/src/main/java/gr/iccs/imu/ems/baguette/server/coordinator/NoopCoordinator.java +++ b/nebulous/ems-core/baguette-server/src/main/java/gr/iccs/imu/ems/baguette/server/coordinator/NoopCoordinator.java @@ -97,7 +97,7 @@ public class NoopCoordinator implements ServerCoordinator { if (!checkStarted && started) { log.warn("{}: {}(): Coordinator is already running{}", className, methodName, str); } else { - log.info("{}: {}(): Method invoked{}", className, methodName, str); + log.debug("{}: {}(): Method invoked{}", className, methodName, str); } return started; } diff --git a/nebulous/ems-core/baguette-server/src/main/java/gr/iccs/imu/ems/baguette/server/coordinator/cluster/ClusterSelfHealing.java b/nebulous/ems-core/baguette-server/src/main/java/gr/iccs/imu/ems/baguette/server/coordinator/cluster/ClusterSelfHealing.java index 5480a3d..d845d8a 100644 --- a/nebulous/ems-core/baguette-server/src/main/java/gr/iccs/imu/ems/baguette/server/coordinator/cluster/ClusterSelfHealing.java +++ b/nebulous/ems-core/baguette-server/src/main/java/gr/iccs/imu/ems/baguette/server/coordinator/cluster/ClusterSelfHealing.java @@ -27,6 +27,10 @@ public class ClusterSelfHealing { // Server-side self-healing methods // ------------------------------------------------------------------------ + public boolean isEnabled() { + return selfHealingManager.isEnabled(); + } + List getAggregatorCapableNodesInZone(IClusterZone zone) { // Get the normal nodes in the zone that can be Aggregators (i.e. Aggregator and candidates) List aggregatorCapableNodes = zone.findAggregatorCapableNodes(); diff --git a/nebulous/ems-core/baguette-server/src/main/java/gr/iccs/imu/ems/baguette/server/coordinator/cluster/ClusteringCoordinator.java b/nebulous/ems-core/baguette-server/src/main/java/gr/iccs/imu/ems/baguette/server/coordinator/cluster/ClusteringCoordinator.java index 7490e4b..0354b10 100644 --- a/nebulous/ems-core/baguette-server/src/main/java/gr/iccs/imu/ems/baguette/server/coordinator/cluster/ClusteringCoordinator.java +++ b/nebulous/ems-core/baguette-server/src/main/java/gr/iccs/imu/ems/baguette/server/coordinator/cluster/ClusteringCoordinator.java @@ -89,8 +89,12 @@ public class ClusteringCoordinator extends NoopCoordinator { topLevelGrouping, aggregatorGrouping, lastLevelGrouping); // Configure Self-Healing manager - clusterSelfHealing = new ClusterSelfHealing(server.getSelfHealingManager()); - server.getSelfHealingManager().setMode(SelfHealingManager.MODE.INCLUDED); + if (server.getSelfHealingManager().isEnabled()) { + clusterSelfHealing = new ClusterSelfHealing(server.getSelfHealingManager()); + server.getSelfHealingManager().setMode(SelfHealingManager.MODE.INCLUDED); + } else { + clusterSelfHealing = null; + } } @SneakyThrows @@ -336,9 +340,11 @@ public class ClusteringCoordinator extends NoopCoordinator { log.trace("addNodeInTopology: CSC is in zone: client={}, address={}, zone={}", csc.getId(), csc.getClientIpAddress(), zone.getId()); // Self-healing-related actions - List aggregatorCapableNodes = clusterSelfHealing.getAggregatorCapableNodesInZone(zone); - clusterSelfHealing.updateNodesSelfHealingMonitoring(zone, aggregatorCapableNodes); - clusterSelfHealing.removeResourceLimitedNodeSelfHealingMonitoring(zone, aggregatorCapableNodes); + if (clusterSelfHealing!=null && clusterSelfHealing.isEnabled()) { + List aggregatorCapableNodes = clusterSelfHealing.getAggregatorCapableNodesInZone(zone); + clusterSelfHealing.updateNodesSelfHealingMonitoring(zone, aggregatorCapableNodes); + clusterSelfHealing.removeResourceLimitedNodeSelfHealingMonitoring(zone, aggregatorCapableNodes); + } } } @@ -371,11 +377,13 @@ public class ClusteringCoordinator extends NoopCoordinator { } // Self-healing-related actions - List aggregatorCapableNodes = clusterSelfHealing.getAggregatorCapableNodesInZone(zone); - clusterSelfHealing.updateNodesSelfHealingMonitoring(zone, aggregatorCapableNodes); - if (aggregatorCapableNodes.isEmpty()) - ; //XXX: TODO: ??Reconfigure non-candidate nodes to forward their events to EMS server?? - clusterSelfHealing.addResourceLimitedNodeSelfHealingMonitoring(zone, aggregatorCapableNodes); + if (clusterSelfHealing!=null && clusterSelfHealing.isEnabled()) { + List aggregatorCapableNodes = clusterSelfHealing.getAggregatorCapableNodesInZone(zone); + clusterSelfHealing.updateNodesSelfHealingMonitoring(zone, aggregatorCapableNodes); + if (aggregatorCapableNodes.isEmpty()) + ; //XXX: TODO: ??Reconfigure non-candidate nodes to forward their events to EMS server?? + clusterSelfHealing.addResourceLimitedNodeSelfHealingMonitoring(zone, aggregatorCapableNodes); + } } } diff --git a/nebulous/ems-core/baguette-server/src/main/java/gr/iccs/imu/ems/baguette/server/properties/BaguetteServerProperties.java b/nebulous/ems-core/baguette-server/src/main/java/gr/iccs/imu/ems/baguette/server/properties/BaguetteServerProperties.java index d8dd180..a42ddb9 100644 --- a/nebulous/ems-core/baguette-server/src/main/java/gr/iccs/imu/ems/baguette/server/properties/BaguetteServerProperties.java +++ b/nebulous/ems-core/baguette-server/src/main/java/gr/iccs/imu/ems/baguette/server/properties/BaguetteServerProperties.java @@ -76,6 +76,8 @@ public class BaguetteServerProperties implements InitializingBean { private String keyFile = "hostkey.pem"; public String getServerKeyFile() { return keyFile; } + private String connectionInfoFile = "baguette-server-connection-info.json"; + private boolean heartbeatEnabled; @Min(-1) private long heartbeatPeriod = 60000; diff --git a/nebulous/ems-core/bin/k8s-helper.sh b/nebulous/ems-core/bin/k8s-helper.sh new file mode 100644 index 0000000..ea6ed0f --- /dev/null +++ b/nebulous/ems-core/bin/k8s-helper.sh @@ -0,0 +1,34 @@ +#!/usr/bin/env bash +# +# Copyright (C) 2017-2025 Institute of Communication and Computer Systems (imu.iccs.gr) +# +# This Source Code Form is subject to the terms of the Mozilla Public License, v2.0, unless +# Esper library is used, in which case it is subject to the terms of General Public License v2.0. +# If a copy of the MPL was not distributed with this file, you can obtain one at +# https://www.mozilla.org/en-US/MPL/2.0/ +# + +EMS_CLIENT_K8S_CONFIG_MAP_NAME=ems-client-configmap +EMS_CLIENT_K8S_CONFIG_MAP_FILE=ems-client-configmap.json +K8S_OUTPUT_DIR=$EMS_CONFIG_DIR/k8s +K8S_OUTPUT_FILE=$K8S_OUTPUT_DIR/cfgmap_output.json + +# Check if baguette server connection info file exists +[ ! -f $EMS_CLIENT_K8S_CONFIG_MAP_FILE ] && exit 1 + +# Read baguette server connection info file into a variable +BSCI=$( tr -d "\r\n" < $EMS_CLIENT_K8S_CONFIG_MAP_FILE ) +#echo $BSCI + +# Write baguette server connection info into ems-client-configmap +mkdir -p $K8S_OUTPUT_DIR/ +echo "/* Date: $(date) */" > $K8S_OUTPUT_FILE +sec=/var/run/secrets/kubernetes.io/serviceaccount +curl -sS \ + -H "Authorization: Bearer $(cat $sec/token)" \ + -H "Content-Type: application/json-patch+json" \ + --cacert $sec/ca.crt \ + --request PATCH \ + --data "[{\"op\": \"replace\", \"path\": \"/data\", \"value\": $BSCI}]" \ + https://$KUBERNETES_SERVICE_HOST/api/v1/namespaces/$(cat $sec/namespace)/configmaps/$EMS_CLIENT_K8S_CONFIG_MAP_NAME \ + &>> $K8S_OUTPUT_FILE diff --git a/nebulous/ems-core/bin/run.bat b/nebulous/ems-core/bin/run.bat index a65bb98..ebebd1d 100644 --- a/nebulous/ems-core/bin/run.bat +++ b/nebulous/ems-core/bin/run.bat @@ -13,7 +13,7 @@ set PWD=%~dp0 cd %PWD%.. set BASEDIR=%cd% IF NOT DEFINED EMS_CONFIG_DIR set EMS_CONFIG_DIR=%BASEDIR%\config-files -IF NOT DEFINED PAASAGE_CONFIG_DIR set PAASAGE_CONFIG_DIR=%BASEDIR%\config-files +:: IF NOT DEFINED PAASAGE_CONFIG_DIR set PAASAGE_CONFIG_DIR=%BASEDIR%\config-files IF NOT DEFINED JARS_DIR set JARS_DIR=%BASEDIR%\control-service\target IF NOT DEFINED LOGS_DIR set LOGS_DIR=%BASEDIR%\logs IF NOT DEFINED PUBLIC_DIR set PUBLIC_DIR=%BASEDIR%\public_resources @@ -31,7 +31,7 @@ if "%EMS_SECRETS_FILE%"=="" ( set EMS_SECRETS_FILE=%EMS_CONFIG_DIR%\secrets.properties ) if "%EMS_CONFIG_LOCATION%"=="" ( - set EMS_CONFIG_LOCATION=classpath:rule-templates.yml,optional:file:%EMS_CONFIG_DIR%\ems-server.yml,optional:file:%EMS_CONFIG_DIR%\ems-server.properties,optional:file:%EMS_CONFIG_DIR%\ems.yml,optional:file:%EMS_CONFIG_DIR%\ems.properties,optional:file:%EMS_SECRETS_FILE% + set EMS_CONFIG_LOCATION=optional:classpath:rule-templates.yml,optional:file:%EMS_CONFIG_DIR%\ems-server.yml,optional:file:%EMS_CONFIG_DIR%\ems-server.properties,optional:file:%EMS_CONFIG_DIR%\ems.yml,optional:file:%EMS_CONFIG_DIR%\ems.properties,optional:file:%EMS_SECRETS_FILE% ) :: Check logger configuration @@ -46,6 +46,9 @@ if "%LOG_FILE%"=="" ( :: Set shell encoding to UTF-8 (in order to display banner correctly) chcp 65001 +:: Create default models directory +mkdir %BASEDIR%\models + :: Run EMS server rem Uncomment next line to set JAVA runtime options rem set JAVA_OPTS=-Djavax.net.debug=all diff --git a/nebulous/ems-core/bin/run.sh b/nebulous/ems-core/bin/run.sh index 9ded56e..4715598 100644 --- a/nebulous/ems-core/bin/run.sh +++ b/nebulous/ems-core/bin/run.sh @@ -13,7 +13,7 @@ PREVWORKDIR=`pwd` BASEDIR=$( cd "$( dirname "${BASH_SOURCE[0]}" )" && cd .. && pwd ) cd ${BASEDIR} if [[ -z $EMS_CONFIG_DIR ]]; then EMS_CONFIG_DIR=$BASEDIR/config-files; export EMS_CONFIG_DIR; fi -if [[ -z $PAASAGE_CONFIG_DIR ]]; then PAASAGE_CONFIG_DIR=$BASEDIR/config-files; export PAASAGE_CONFIG_DIR; fi +#if [[ -z $PAASAGE_CONFIG_DIR ]]; then PAASAGE_CONFIG_DIR=$BASEDIR/config-files; export PAASAGE_CONFIG_DIR; fi if [[ -z $JARS_DIR ]]; then JARS_DIR=$BASEDIR/control-service/target; export JARS_DIR; fi if [[ -z $LOGS_DIR ]]; then LOGS_DIR=$BASEDIR/logs; export LOGS_DIR; fi if [[ -z $PUBLIC_DIR ]]; then PUBLIC_DIR=$BASEDIR/public_resources; export PUBLIC_DIR; fi @@ -34,7 +34,7 @@ if [[ -z "$EMS_SECRETS_FILE" ]]; then EMS_SECRETS_FILE=$EMS_CONFIG_DIR/secrets.properties fi if [[ -z "$EMS_CONFIG_LOCATION" ]]; then - EMS_CONFIG_LOCATION=classpath:rule-templates.yml,optional:file:$EMS_CONFIG_DIR/ems-server.yml,optional:file:$EMS_CONFIG_DIR/ems-server.properties,optional:file:$EMS_CONFIG_DIR/ems.yml,optional:file:$EMS_CONFIG_DIR/ems.properties,optional:file:$EMS_SECRETS_FILE + EMS_CONFIG_LOCATION=optional:classpath:rule-templates.yml,optional:file:$EMS_CONFIG_DIR/ems-server.yml,optional:file:$EMS_CONFIG_DIR/ems-server.properties,optional:file:$EMS_CONFIG_DIR/ems.yml,optional:file:$EMS_CONFIG_DIR/ems.properties,optional:file:$EMS_SECRETS_FILE fi # Check logger configuration @@ -52,6 +52,9 @@ export LANG=C.UTF-8 # Setup TERM & INT signal handler trap 'echo "Signaling EMS to exit"; kill -TERM "${emsPid}"; wait "${emsPid}"; ' SIGTERM SIGINT +# Create default models directory +mkdir -p ${BASEDIR}/models + # Run EMS server # Uncomment next line to set JAVA runtime options #JAVA_OPTS=-Djavax.net.debug=all diff --git a/nebulous/ems-core/broker-cep/src/main/java/gr/iccs/imu/ems/brokercep/BrokerCepConsumer.java b/nebulous/ems-core/broker-cep/src/main/java/gr/iccs/imu/ems/brokercep/BrokerCepConsumer.java index 8608ef1..b5e1081 100644 --- a/nebulous/ems-core/broker-cep/src/main/java/gr/iccs/imu/ems/brokercep/BrokerCepConsumer.java +++ b/nebulous/ems-core/broker-cep/src/main/java/gr/iccs/imu/ems/brokercep/BrokerCepConsumer.java @@ -14,6 +14,8 @@ import gr.iccs.imu.ems.brokercep.cep.CepService; import gr.iccs.imu.ems.brokercep.event.EventMap; import gr.iccs.imu.ems.brokercep.properties.BrokerCepProperties; import gr.iccs.imu.ems.util.StrUtil; +import jakarta.jms.*; +import lombok.Getter; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.apache.activemq.broker.BrokerService; @@ -27,11 +29,12 @@ import org.springframework.context.event.ContextClosedEvent; import org.springframework.scheduling.TaskScheduler; import org.springframework.stereotype.Service; -import jakarta.jms.*; import java.time.Instant; import java.util.Collections; import java.util.Enumeration; import java.util.HashMap; +import java.util.LinkedList; +import java.util.List; import java.util.Map; import java.util.concurrent.atomic.AtomicLong; @@ -59,6 +62,9 @@ public class BrokerCepConsumer implements MessageListener, InitializingBean, App private final EventCache eventCache; + @Getter + private final List listeners = new LinkedList<>(); + @Override public void afterPropertiesSet() { initialize(); @@ -123,36 +129,28 @@ public class BrokerCepConsumer implements MessageListener, InitializingBean, App } public synchronized void addQueue(String queueName) { - log.debug("BrokerCepConsumer.addQueue(): Adding queue: {}", queueName); - if (addedDestinations.containsKey(queueName)) { - log.debug("BrokerCepConsumer.addQueue(): Queue already added: {}", queueName); - return; - } - try { - Queue queue = session.createQueue(queueName); - MessageConsumer consumer = session.createConsumer(queue); - consumer.setMessageListener(this); - addedDestinations.put(queueName, consumer); - log.debug("BrokerCepConsumer.addQueue(): Added queue: {}", queueName); - } catch (Exception ex) { - log.error("BrokerCepConsumer.addQueue(): EXCEPTION: ", ex); - } + addDestinationAndListener(queueName, false, this); } public synchronized void addTopic(String topicName) { - log.debug("BrokerCepConsumer.addTopic(): Adding topic: {}", topicName); - if (addedDestinations.containsKey(topicName)) { - log.debug("BrokerCepConsumer.addTopic(): Topic already added: {}", topicName); + addDestinationAndListener(topicName, true, this); + } + + private synchronized void addDestinationAndListener(String destinationName, boolean isTopic, MessageListener listener) { + log.debug("BrokerCepConsumer.addDestinationAndListener(): Adding destination: {}", destinationName); + if (addedDestinations.containsKey(destinationName)) { + log.debug("BrokerCepConsumer.addDestinationAndListener(): Destination already added: {}", destinationName); return; } try { - Topic topic = session.createTopic(topicName); - MessageConsumer consumer = session.createConsumer(topic); - consumer.setMessageListener(this); - addedDestinations.put(topicName, consumer); - log.debug("BrokerCepConsumer.addTopic(): Added topic: {}", topicName); + Destination destination = isTopic + ? session.createTopic(destinationName) : session.createQueue(destinationName); + MessageConsumer consumer = session.createConsumer(destination); + consumer.setMessageListener(listener); + addedDestinations.put(destinationName, consumer); + log.debug("BrokerCepConsumer.addDestinationAndListener(): Added destination: {}", destinationName); } catch (Exception ex) { - log.error("BrokerCepConsumer.addTopic(): EXCEPTION: ", ex); + log.error("BrokerCepConsumer.addDestinationAndListener(): EXCEPTION: ", ex); } } @@ -224,6 +222,10 @@ public class BrokerCepConsumer implements MessageListener, InitializingBean, App log.warn("BrokerCepConsumer.onMessage(): Message ignored: type={}", message.getClass().getName()); } eventCounter.incrementAndGet(); + + // Notify listeners + listeners.forEach(l -> l.onMessage(message)); + } catch (Exception ex) { log.error("BrokerCepConsumer.onMessage(): EXCEPTION: ", ex); eventFailuresCounter.incrementAndGet(); diff --git a/nebulous/ems-core/broker-cep/src/main/java/gr/iccs/imu/ems/brokercep/broker/BrokerConfig.java b/nebulous/ems-core/broker-cep/src/main/java/gr/iccs/imu/ems/brokercep/broker/BrokerConfig.java index 98e089c..287b0f7 100644 --- a/nebulous/ems-core/broker-cep/src/main/java/gr/iccs/imu/ems/brokercep/broker/BrokerConfig.java +++ b/nebulous/ems-core/broker-cep/src/main/java/gr/iccs/imu/ems/brokercep/broker/BrokerConfig.java @@ -300,7 +300,7 @@ public class BrokerConfig implements InitializingBean { List plugins = new ArrayList<>(); if (getBrokerAuthenticationPlugin()!=null) plugins.add(getBrokerAuthenticationPlugin()); if (getBrokerAuthorizationPlugin()!=null) plugins.add(getBrokerAuthorizationPlugin()); - if (plugins.size() > 0) { + if (!plugins.isEmpty()) { brokerService.setPlugins(plugins.toArray(new BrokerPlugin[0])); } diff --git a/nebulous/ems-core/broker-cep/src/main/java/gr/iccs/imu/ems/brokercep/broker/interceptor/SourceAddressMessageUpdateInterceptor.java b/nebulous/ems-core/broker-cep/src/main/java/gr/iccs/imu/ems/brokercep/broker/interceptor/SourceAddressMessageUpdateInterceptor.java index 0005d12..0418ede 100644 --- a/nebulous/ems-core/broker-cep/src/main/java/gr/iccs/imu/ems/brokercep/broker/interceptor/SourceAddressMessageUpdateInterceptor.java +++ b/nebulous/ems-core/broker-cep/src/main/java/gr/iccs/imu/ems/brokercep/broker/interceptor/SourceAddressMessageUpdateInterceptor.java @@ -46,8 +46,9 @@ public class SourceAddressMessageUpdateInterceptor extends AbstractMessageInterc boolean isLocal = StringUtils.isBlank(address) || NetUtil.isLocalAddress(address.trim()); if (isLocal) { log.trace("SourceAddressMessageUpdateInterceptor: Producer host is local. Getting our public IP address"); - address = NetUtil.getPublicIpAddress(); - log.trace("SourceAddressMessageUpdateInterceptor: Producer host (public): {}", address); + address = NetUtil.getIpSettingAddress(); + log.trace("SourceAddressMessageUpdateInterceptor: Producer host ({}): {}", + NetUtil.isUsePublic() ? "public" : "default", address); } else { log.trace("SourceAddressMessageUpdateInterceptor: Producer host is not local. Ok"); } diff --git a/nebulous/ems-core/broker-cep/src/main/java/gr/iccs/imu/ems/brokercep/cep/MathUtil.java b/nebulous/ems-core/broker-cep/src/main/java/gr/iccs/imu/ems/brokercep/cep/MathUtil.java index 09e777d..17dd828 100644 --- a/nebulous/ems-core/broker-cep/src/main/java/gr/iccs/imu/ems/brokercep/cep/MathUtil.java +++ b/nebulous/ems-core/broker-cep/src/main/java/gr/iccs/imu/ems/brokercep/cep/MathUtil.java @@ -26,6 +26,7 @@ import java.util.stream.Collectors; public class MathUtil { static { License.iConfirmNonCommercialUse("EMS-"+ NetUtil.getIpAddress()); + mXparser.disableImpliedMultiplicationMode(); } private static final Map functions = new HashMap<>(); private static final Map constants = new HashMap<>(); @@ -118,8 +119,20 @@ public class MathUtil { // Get token names List initTokens = e.getCopyOfInitialTokens(); - log.debug("MathUtil: initial-tokens={}", initTokens); if (log.isTraceEnabled()) { + log.trace("MathUtil: initial-tokens={}", initTokens.stream() + .map(token -> Map.of( + "tokenId", token.tokenId, + "tokenType", token.tokenTypeId, + "tokenStr", token.tokenStr, + "tokenValue", token.tokenValue, + "looksLike", token.looksLike, + "keyWord", token.keyWord, + "tokenLevel", token.tokenLevel, + "is-identifier", token.isIdentifier() + ) + ).toList() + ); mXparser.consolePrintTokens(initTokens); } diff --git a/nebulous/ems-core/broker-cep/src/main/java/gr/iccs/imu/ems/brokercep/event/EventMap.java b/nebulous/ems-core/broker-cep/src/main/java/gr/iccs/imu/ems/brokercep/event/EventMap.java index cdb680b..eec4bdc 100644 --- a/nebulous/ems-core/broker-cep/src/main/java/gr/iccs/imu/ems/brokercep/event/EventMap.java +++ b/nebulous/ems-core/broker-cep/src/main/java/gr/iccs/imu/ems/brokercep/event/EventMap.java @@ -10,6 +10,7 @@ package gr.iccs.imu.ems.brokercep.event; import com.google.gson.Gson; +import com.google.gson.GsonBuilder; import gr.iccs.imu.ems.util.StrUtil; import lombok.Data; import lombok.EqualsAndHashCode; @@ -29,7 +30,7 @@ import java.util.stream.Collectors; @EqualsAndHashCode(callSuper = false) public class EventMap extends LinkedHashMap implements Serializable { - private static Gson gson; + private static Gson gson = new GsonBuilder().create(); private static AtomicLong eventIdSequence = new AtomicLong(0); // Standard/Known Event fields configuration diff --git a/nebulous/ems-core/broker-client/src/main/java/gr/iccs/imu/ems/brokerclient/BrokerClientApp.java b/nebulous/ems-core/broker-client/src/main/java/gr/iccs/imu/ems/brokerclient/BrokerClientApp.java index a5545fd..f78bd25 100644 --- a/nebulous/ems-core/broker-client/src/main/java/gr/iccs/imu/ems/brokerclient/BrokerClientApp.java +++ b/nebulous/ems-core/broker-client/src/main/java/gr/iccs/imu/ems/brokerclient/BrokerClientApp.java @@ -31,6 +31,7 @@ import javax.script.ScriptEngine; import javax.script.ScriptEngineManager; import javax.script.ScriptException; import java.io.*; +import java.nio.file.Files; import java.nio.file.Paths; import java.util.*; import java.util.concurrent.atomic.AtomicLong; @@ -98,9 +99,9 @@ public class BrokerClientApp { String url = processUrlArg( args[aa++] ); String topic = args[aa++]; String type = args[aa].startsWith("-T") ? args[aa++].substring(2) : "text"; + boolean processPlaceholders = !args[aa].startsWith("-PP") || Boolean.parseBoolean(args[aa++].substring(3)); String payload = args[aa++]; - payload = payload - .replaceAll("%TIMESTAMP%|%TS%", ""+System.currentTimeMillis()); + payload = getPayload(payload, processPlaceholders); EventMap event = gson.fromJson(payload, EventMap.class); sendEvent(url, username, password, topic, type, event, collectProperties(args, aa)); } else @@ -108,9 +109,9 @@ public class BrokerClientApp { String url = processUrlArg( args[aa++] ); String topic = args[aa++]; String type = args[aa].startsWith("-T") ? args[aa++].substring(2) : "text"; + boolean processPlaceholders = !args[aa].startsWith("-PP") || Boolean.parseBoolean(args[aa++].substring(3)); String payload = args[aa++]; - payload = payload - .replaceAll("%TIMESTAMP%|%TS%", ""+System.currentTimeMillis()); + payload = getPayload(payload, processPlaceholders); Map properties = collectProperties(args, aa); if ("map".equalsIgnoreCase(type)) { EventMap event = gson.fromJson(payload, EventMap.class); @@ -224,6 +225,24 @@ public class BrokerClientApp { } } + private static String getPayload(String payload, boolean processPlaceholders) throws IOException { + if (payload==null) return null; + String payloadTrim = payload.trim(); + if (StringUtils.startsWith(payloadTrim, "@")) { + payload = Files.readString(Paths.get(StringUtils.substring(payloadTrim, 1))); + } + if ("-".equals(payloadTrim)) { + BufferedReader reader = new BufferedReader(new InputStreamReader(System.in)); + payload = reader.lines().collect(Collectors.joining("\n")); + } + if (processPlaceholders) { + payload = payload + .replaceAll("%TIMESTAMP%|%TS%", ""+System.currentTimeMillis()); + + } + return payload; + } + private static Map collectProperties(String[] args, int aa) { return Arrays.stream(args, aa, args.length) .map(s->s.split("[=:]",2)) @@ -741,9 +760,11 @@ public class BrokerClientApp { log.info("BrokerClientApp: Usage: "); log.info("BrokerClientApp: client list [-U [-P "); log.info("BrokerClientApp: client publish [ -U [-P [-T] []*"); - log.info("BrokerClientApp: client publish2 [-U [-P [-T] []*"); - log.info("BrokerClientApp: client publish3 [-U [-P [-T] []*"); + log.info("BrokerClientApp: client publish2 [-U [-P [-T] [-PP] []*"); + log.info("BrokerClientApp: client publish3 [-U [-P [-T] [-PP] []*"); log.info("BrokerClientApp: : text, object, bytes, map"); + log.info("BrokerClientApp: -PP: Process placeholders (default 'true')"); + log.info("BrokerClientApp: '-' | '@file': Read payload from STDIN | Read payload from file 'file' "); log.info("BrokerClientApp: : = (use quotes if needed)"); log.info("BrokerClientApp: client receive [-U [-P "); log.info("BrokerClientApp: client subscribe [-U [-P "); diff --git a/nebulous/ems-core/broker-client/src/main/java/gr/iccs/imu/ems/brokerclient/event/EventGenerator.java b/nebulous/ems-core/broker-client/src/main/java/gr/iccs/imu/ems/brokerclient/event/EventGenerator.java index 48f384c..18c33ec 100644 --- a/nebulous/ems-core/broker-client/src/main/java/gr/iccs/imu/ems/brokerclient/event/EventGenerator.java +++ b/nebulous/ems-core/broker-client/src/main/java/gr/iccs/imu/ems/brokerclient/event/EventGenerator.java @@ -12,12 +12,14 @@ package gr.iccs.imu.ems.brokerclient.event; import gr.iccs.imu.ems.brokerclient.BrokerClient; import jakarta.annotation.PostConstruct; import lombok.Data; +import lombok.NonNull; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Component; import java.util.concurrent.atomic.AtomicLong; +import java.util.function.BiFunction; @Slf4j @Data @@ -25,6 +27,7 @@ import java.util.concurrent.atomic.AtomicLong; @Scope(value = ConfigurableBeanFactory.SCOPE_PROTOTYPE) public class EventGenerator implements Runnable { private final static AtomicLong counter = new AtomicLong(); + private final BiFunction eventPublisher; private final BrokerClient client; private String brokerUrl; private String brokerUsername; @@ -38,6 +41,16 @@ public class EventGenerator implements Runnable { private transient boolean keepRunning; + public EventGenerator(@NonNull BiFunction eventPublisher) { + this.eventPublisher = eventPublisher; + this.client = null; + } + + public EventGenerator(@NonNull BrokerClient brokerClient) { + this.eventPublisher = null; + this.client = brokerClient; + } + @PostConstruct public void printCounter() { log.info("New EventGenerator with instance number: {}", counter.getAndIncrement()); @@ -65,7 +78,10 @@ public class EventGenerator implements Runnable { double newValue = Math.random() * valueRangeWidth + lowerValue; EventMap event = new EventMap(newValue, level, System.currentTimeMillis()); log.info("EventGenerator.run(): Sending event #{}: {}", countSent + 1, event); - client.publishEventWithCredentials(brokerUrl, brokerUsername, brokerPassword, destinationName, event); + if (eventPublisher!=null) + eventPublisher.apply(destinationName, event); + else + client.publishEventWithCredentials(brokerUrl, brokerUsername, brokerPassword, destinationName, event); countSent++; if (countSent == howMany) keepRunning = false; log.info("EventGenerator.run(): Event sent #{}: {}", countSent, event); diff --git a/nebulous/ems-core/common/src/main/java/gr/iccs/imu/ems/common/collector/AbstractEndpointCollector.java b/nebulous/ems-core/common/src/main/java/gr/iccs/imu/ems/common/collector/AbstractEndpointCollector.java index 106bec1..12ce9fd 100644 --- a/nebulous/ems-core/common/src/main/java/gr/iccs/imu/ems/common/collector/AbstractEndpointCollector.java +++ b/nebulous/ems-core/common/src/main/java/gr/iccs/imu/ems/common/collector/AbstractEndpointCollector.java @@ -181,7 +181,7 @@ public abstract class AbstractEndpointCollector implements InitializingBean, // collect data from local node if (! properties.isSkipLocal()) { - log.debug/*info*/("Collectors::{}: Collecting metrics from local node...", collectorId); + log.debug("Collectors::{}: Collecting metrics from local node...", collectorId); collectAndPublishData(""); } else { log.debug("Collectors::{}: Collection from local node is disabled", collectorId); @@ -191,8 +191,8 @@ public abstract class AbstractEndpointCollector implements InitializingBean, log.trace("Collectors::{}: Nodes without clients in Zone: {}", collectorId, collectorContext.getNodesWithoutClient()); log.trace("Collectors::{}: Is Aggregator: {}", collectorId, collectorContext.isAggregator()); if (collectorContext.isAggregator()) { - if (collectorContext.getNodesWithoutClient().size()>0) { - log.debug/*info*/("Collectors::{}: Collecting metrics from remote nodes (without EMS client): {}", collectorId, + if (! collectorContext.getNodesWithoutClient().isEmpty()) { + log.debug("Collectors::{}: Collecting metrics from remote nodes (without EMS client): {}", collectorId, collectorContext.getNodesWithoutClient()); for (Object nodeAddress : collectorContext.getNodesWithoutClient()) { // collect data from remote node @@ -253,7 +253,7 @@ public abstract class AbstractEndpointCollector implements InitializingBean, private COLLECTION_RESULT collectAndPublishData(@NonNull String nodeAddress) { if (ignoredNodes.containsKey(nodeAddress)) { - log.debug/*info*/("Collectors::{}: Node is in ignore list: {}", collectorId, nodeAddress); + log.debug("Collectors::{}: Node is in ignore list: {}", collectorId, nodeAddress); return COLLECTION_RESULT.IGNORED; } @@ -322,7 +322,7 @@ public abstract class AbstractEndpointCollector implements InitializingBean, // Remote node data collection URL url = String.format(properties.getUrlOfNodesWithoutClient(), nodeAddress); } - log.debug/*info*/("Collectors::{}: Collecting data from url: {}", collectorId, url); + log.debug("Collectors::{}: Collecting data from url: {}", collectorId, url); log.debug("Collectors::{}: Collecting data: {}...", collectorId, url); long startTm = System.currentTimeMillis(); @@ -342,8 +342,8 @@ public abstract class AbstractEndpointCollector implements InitializingBean, log.debug("Collectors::{}: Collecting data...ok", collectorId); //log.info("Collectors::{}: Metrics: extracted={}, published={}, failed={}", collectorId, // stats.countSuccess + stats.countErrors, stats.countSuccess, stats.countErrors); - if (log.isInfoEnabled()) - log.debug/*info*/("Collectors::{}: Publish statistics: {}", collectorId, stats); + if (log.isDebugEnabled()) + log.debug("Collectors::{}: Publish statistics: {}", collectorId, stats); log.debug("Collectors::{}: Durations: rest-call={}, extract+publish={}, total={}", collectorId, callEndTm-startTm, endTm-callEndTm, endTm-startTm); } else { diff --git a/nebulous/ems-core/config-files/baguette-client-install/linux-yaml/baguette.yml b/nebulous/ems-core/config-files/baguette-client-install/linux-yaml/baguette.yml index 0caa8cc..0d740be 100644 --- a/nebulous/ems-core/config-files/baguette-client-install/linux-yaml/baguette.yml +++ b/nebulous/ems-core/config-files/baguette-client-install/linux-yaml/baguette.yml @@ -53,17 +53,17 @@ instructions: executable: false exitCode: 0 match: false - - description: Upload installation package MD5 checksum + - description: Upload installation package SHA256 checksum taskType: COPY - fileName: /tmp/baguette-client.tgz.md5 - localFileName: '${EMS_PUBLIC_DIR}/resources/baguette-client.tgz.md5' + fileName: /tmp/baguette-client.tgz.sha256 + localFileName: '${EMS_PUBLIC_DIR}/resources/baguette-client.tgz.sha256' executable: false exitCode: 0 match: false - - description: Check MD5 checksum of installation package + - description: Check SHA256 checksum of installation package taskType: CHECK command: >- - [[ `cat /tmp/baguette-client.tgz.md5` != `md5sum /tmp/baguette-client.tgz | cut -d ' ' -f 1 ` ]] && exit 99 + [[ `cat /tmp/baguette-client.tgz.sha256` != `sha256sum /tmp/baguette-client.tgz | cut -d ' ' -f 1 ` ]] && exit 99 executable: false exitCode: 99 match: true diff --git a/nebulous/ems-core/config-files/baguette-client-install/linux/baguette.json b/nebulous/ems-core/config-files/baguette-client-install/linux/baguette.json index 5c1905d..b650193 100644 --- a/nebulous/ems-core/config-files/baguette-client-install/linux/baguette.json +++ b/nebulous/ems-core/config-files/baguette-client-install/linux/baguette.json @@ -48,18 +48,18 @@ "match": false }, { - "description": "Upload installation package MD5 checksum", + "description": "Upload installation package SHA256 checksum", "taskType": "COPY", - "fileName": "/tmp/baguette-client.tgz.md5", - "localFileName": "${EMS_PUBLIC_DIR}/resources/baguette-client.tgz.md5", + "fileName": "/tmp/baguette-client.tgz.sha256", + "localFileName": "${EMS_PUBLIC_DIR}/resources/baguette-client.tgz.sha256", "executable": false, "exitCode": 0, "match": false }, { - "description": "Check MD5 checksum of installation package", + "description": "Check SHA256 checksum of installation package", "taskType": "CHECK", - "command": "[[ `cat /tmp/baguette-client.tgz.md5` != `md5sum /tmp/baguette-client.tgz | cut -d ' ' -f 1 ` ]] && exit 99", + "command": "[[ `cat /tmp/baguette-client.tgz.sha256` != `sha256sum /tmp/baguette-client.tgz | cut -d ' ' -f 1 ` ]] && exit 99", "executable": false, "exitCode": 99, "match": true diff --git a/nebulous/ems-core/config-files/baguette-client/conf/baguette-client.properties.sample b/nebulous/ems-core/config-files/baguette-client/conf/baguette-client.properties.sample index b493650..a70398c 100644 --- a/nebulous/ems-core/config-files/baguette-client/conf/baguette-client.properties.sample +++ b/nebulous/ems-core/config-files/baguette-client/conf/baguette-client.properties.sample @@ -11,9 +11,9 @@ ### EMS - Baguette Client properties ### ################################################################################ -#password-encoder-class = password.gr.iccs.imu.ems.util.AsterisksPasswordEncoder -#password-encoder-class = password.gr.iccs.imu.ems.util.IdentityPasswordEncoder -#password-encoder-class = password.gr.iccs.imu.ems.util.PresentPasswordEncoder +#password-encoder-class = gr.iccs.imu.ems.util.password.AsterisksPasswordEncoder +#password-encoder-class = gr.iccs.imu.ems.util.password.IdentityPasswordEncoder +#password-encoder-class = gr.iccs.imu.ems.util.password.PresentPasswordEncoder ### Jasypt encryptor settings (using old settings until encrypted texts are updated) jasypt.encryptor.algorithm = PBEWithMD5AndDES @@ -68,7 +68,7 @@ server-password = ${BAGUETTE_SERVER_PASSWORD} # Collectors settings # ----------------------------------------------------------------------------- -#collector-classes = netdata.collector.gr.iccs.imu.ems.baguette.client.NetdataCollector +#collector-classes = gr.iccs.imu.ems.baguette.client.collector.netdata.NetdataCollector collector.netdata.enable = true collector.netdata.delay = 10000 @@ -80,7 +80,7 @@ collector.netdata.allowed-topics = ${COLLECTOR_ALLOWED_TOPICS} collector.netdata.error-limit = 3 collector.netdata.pause-period = 60 -collector.prometheus.enable = false +collector.prometheus.enable = true collector.prometheus.delay = 10000 collector.prometheus.url = http://127.0.0.1:9090/metrics collector.prometheus.urlOfNodesWithoutClient = http://%s:9090/metrics @@ -151,8 +151,8 @@ brokercep.broker-protocol = ssl BROKER_URL_PROPERTIES = transport.daemon=true&transport.trace=false&transport.useKeepAlive=true&transport.useInactivityMonitor=false&transport.needClientAuth=${CLIENT_AUTH_REQUIRED}&transport.verifyHostName=true&transport.connectionTimeout=0&transport.keepAlive=true CLIENT_AUTH_REQUIRED = false brokercep.broker-url[0] = ${brokercep.broker-protocol}://0.0.0.0:${brokercep.broker-port}?${BROKER_URL_PROPERTIES} -brokercep.broker-url[1] = tcp://127.0.0.1:61616?${BROKER_URL_PROPERTIES} -brokercep.broker-url[2] = +brokercep.broker-url[1] = tcp://${BROKER_URL_ADDRESS_INSECURE:127.0.0.1}:61616?${BROKER_URL_PROPERTIES} +brokercep.broker-url[2] = stomp://${BROKER_URL_ADDRESS_INSECURE:127.0.0.1}:61610?${BROKER_URL_PROPERTIES} CLIENT_URL_PROPERTIES=daemon=true&trace=false&useInactivityMonitor=false&connectionTimeout=0&keepAlive=true brokercep.broker-url-for-consumer = tcp://127.0.0.1:61616?${CLIENT_URL_PROPERTIES} @@ -163,16 +163,18 @@ brokercep.broker-url-for-clients = ${brokercep.broker-protocol}://${EMS_CLIENT_A brokercep.ssl.keystore-file = ${EMS_CONFIG_DIR}/client-broker-keystore.p12 brokercep.ssl.keystore-type = PKCS12 #brokercep.ssl.keystore-password = melodic -brokercep.ssl.keystore-password = ENC(ISMbn01HVPbtRPkqm2Lslg==) +#brokercep.ssl.keystore-password = ENC(ISMbn01HVPbtRPkqm2Lslg==) +brokercep.ssl.keystore-password = ${EMS_KEYSTORE_PASSWORD} # Trust store brokercep.ssl.truststore-file = ${EMS_CONFIG_DIR}/client-broker-truststore.p12 brokercep.ssl.truststore-type = PKCS12 #brokercep.ssl.truststore-password = melodic -brokercep.ssl.truststore-password = ENC(ISMbn01HVPbtRPkqm2Lslg==) +#brokercep.ssl.truststore-password = ENC(ISMbn01HVPbtRPkqm2Lslg==) +brokercep.ssl.truststore-password = ${EMS_TRUSTSTORE_PASSWORD} # Certificate brokercep.ssl.certificate-file = ${EMS_CONFIG_DIR}/client-broker.crt # Key-and-Cert data -brokercep.ssl.key-entry-generate = IF-IP-CHANGED +brokercep.ssl.key-entry-generate = ALWAYS brokercep.ssl.key-entry-name = ${EMS_CLIENT_ADDRESS} brokercep.ssl.key-entry-dname = CN=${EMS_CLIENT_ADDRESS},OU=Information Management Unit (IMU),O=Institute of Communication and Computer Systems (ICCS),L=Athens,ST=Attika,C=GR brokercep.ssl.key-entry-ext-san = dns:localhost,ip:127.0.0.1,ip:${DEFAULT_IP},ip:${PUBLIC_IP} @@ -180,7 +182,8 @@ brokercep.ssl.key-entry-ext-san = dns:localhost,ip:127.0.0.1,ip:${DEFAULT_IP},ip # Authentication and Authorization settings brokercep.authentication-enabled = true #brokercep.additional-broker-credentials = aaa/111, bbb/222, morphemic/morphemic -brokercep.additional-broker-credentials = ENC(axeJUxNHajYfBffUwvuT3kwTgLTpRliDMz/ZQ9hROZ3BNOv0Idw72NJsawzIZRuZ) +#brokercep.additional-broker-credentials = ENC(axeJUxNHajYfBffUwvuT3kwTgLTpRliDMz/ZQ9hROZ3BNOv0Idw72NJsawzIZRuZ) +brokercep.additional-broker-credentials = ${EMS_CLIENT_ADDITIONAL_BROKER_CREDENTIALS} brokercep.authorization-enabled = false # Broker instance settings @@ -194,14 +197,14 @@ brokercep.broker-using-shutdown-hook = false # Message interceptors brokercep.message-interceptors[0].destination = > -brokercep.message-interceptors[0].className = interceptor.broker.gr.iccs.imu.ems.brokercep.SequentialCompositeInterceptor +brokercep.message-interceptors[0].className = gr.iccs.imu.ems.brokercep.broker.interceptor.SequentialCompositeInterceptor brokercep.message-interceptors[0].params[0] = #SourceAddressMessageUpdateInterceptor brokercep.message-interceptors[0].params[1] = #MessageForwarderInterceptor brokercep.message-interceptors[0].params[2] = #NodePropertiesMessageUpdateInterceptor -brokercep.message-interceptors-specs.SourceAddressMessageUpdateInterceptor.className = interceptor.broker.gr.iccs.imu.ems.brokercep.SourceAddressMessageUpdateInterceptor -brokercep.message-interceptors-specs.MessageForwarderInterceptor.className = interceptor.broker.gr.iccs.imu.ems.brokercep.MessageForwarderInterceptor -brokercep.message-interceptors-specs.NodePropertiesMessageUpdateInterceptor.className = interceptor.broker.gr.iccs.imu.ems.brokercep.NodePropertiesMessageUpdateInterceptor +brokercep.message-interceptors-specs.SourceAddressMessageUpdateInterceptor.className = gr.iccs.imu.ems.brokercep.broker.interceptor.SourceAddressMessageUpdateInterceptor +brokercep.message-interceptors-specs.MessageForwarderInterceptor.className = gr.iccs.imu.ems.brokercep.broker.interceptor.MessageForwarderInterceptor +brokercep.message-interceptors-specs.NodePropertiesMessageUpdateInterceptor.className = gr.iccs.imu.ems.brokercep.broker.interceptor.NodePropertiesMessageUpdateInterceptor # Message forward destinations (MessageForwarderInterceptor must be included in 'message-interceptors' property) #brokercep.message-forward-destinations[0].connection-string = tcp://localhost:51515 diff --git a/nebulous/ems-core/config-files/baguette-client/conf/baguette-client.yml b/nebulous/ems-core/config-files/baguette-client/conf/baguette-client.yml index d9d4237..62aa123 100644 --- a/nebulous/ems-core/config-files/baguette-client/conf/baguette-client.yml +++ b/nebulous/ems-core/config-files/baguette-client/conf/baguette-client.yml @@ -83,6 +83,8 @@ server-password: ${BAGUETTE_SERVER_PASSWORD} #collector-classes: gr.iccs.imu.ems.baguette.client.collector.netdata.NetdataCollector +collector-configurations: ${COLLECTOR_CONFIGURATIONS} + collector: netdata: enable: true @@ -95,7 +97,7 @@ collector: error-limit: 3 pause-period: 60 prometheus: - enable: false + enable: true delay: 10000 url: http://127.0.0.1:9090/metrics urlOfNodesWithoutClient: http://%s:9090/metrics @@ -174,8 +176,8 @@ brokercep: # Broker connectors broker-url: - ${brokercep.broker-protocol}://0.0.0.0:${brokercep.broker-port}?${BROKER_URL_PROPERTIES} - - tcp://127.0.0.1:61616?${BROKER_URL_PROPERTIES} - - stomp://127.0.0.1:61610?${BROKER_URL_PROPERTIES} + - tcp://${BROKER_URL_ADDRESS_INSECURE:127.0.0.1}:61616?${BROKER_URL_PROPERTIES} + - stomp://${BROKER_URL_ADDRESS_INSECURE:127.0.0.1}:61610?${BROKER_URL_PROPERTIES} # Broker URLs for (EMS) consumer and clients broker-url-for-consumer: tcp://127.0.0.1:61616?${CLIENT_URL_PROPERTIES} @@ -186,18 +188,20 @@ brokercep: # Key store settings keystore-file: ${EMS_CONFIG_DIR}/client-broker-keystore.p12 keystore-type: PKCS12 - keystore-password: 'ENC(ISMbn01HVPbtRPkqm2Lslg==)' # melodic + #keystore-password: 'ENC(ISMbn01HVPbtRPkqm2Lslg==)' # melodic + keystore-password: ${EMS_KEYSTORE_PASSWORD:${random.value}} # Trust store settings truststore-file: ${EMS_CONFIG_DIR}/client-broker-truststore.p12 truststore-type: PKCS12 - truststore-password: 'ENC(ISMbn01HVPbtRPkqm2Lslg==)' # melodic + #truststore-password: 'ENC(ISMbn01HVPbtRPkqm2Lslg==)' # melodic + truststore-password: ${EMS_TRUSTSTORE_PASSWORD:${random.value}} # Certificate settings certificate-file: ${EMS_CONFIG_DIR}/client-broker.crt # key generation settings - key-entry-generate: IF-IP-CHANGED + key-entry-generate: ALWAYS key-entry-name: ${EMS_CLIENT_ADDRESS} key-entry-dname: 'CN=${EMS_CLIENT_ADDRESS},OU=Information Management Unit (IMU),O=Institute of Communication and Computer Systems (ICCS),L=Athens,ST=Attika,C=GR' key-entry-ext-san: 'dns:localhost,ip:127.0.0.1,ip:${DEFAULT_IP},ip:${PUBLIC_IP}' @@ -205,7 +209,8 @@ brokercep: # Authentication and Authorization settings authentication-enabled: true #additional-broker-credentials: aaa/111, bbb/222, morphemic/morphemic - additional-broker-credentials: 'ENC(axeJUxNHajYfBffUwvuT3kwTgLTpRliDMz/ZQ9hROZ3BNOv0Idw72NJsawzIZRuZ)' + #additional-broker-credentials: 'ENC(axeJUxNHajYfBffUwvuT3kwTgLTpRliDMz/ZQ9hROZ3BNOv0Idw72NJsawzIZRuZ)' + additional-broker-credentials: ${EMS_CLIENT_ADDITIONAL_BROKER_CREDENTIALS} authorization-enabled: false # Broker instance settings diff --git a/nebulous/ems-core/config-files/ems-server.properties.sample b/nebulous/ems-core/config-files/ems-server.properties.sample index e3b0bec..625819a 100644 --- a/nebulous/ems-core/config-files/ems-server.properties.sample +++ b/nebulous/ems-core/config-files/ems-server.properties.sample @@ -11,8 +11,9 @@ ### Global settings ################################################################################ -### Don't touch the next line!! -EMS_SERVER_ADDRESS=${${control.IP_SETTING}} +### Don't touch the next lines!! +EMS_IP_SETTING=${P_EMS_IP_SETTING:PUBLIC_IP} +EMS_SERVER_ADDRESS=${${EMS_IP_SETTING}} DOLLAR=$ ### Password Encoder settings @@ -111,9 +112,9 @@ control.log-requests = ${EMS_LOG_REQUESTS:false} #control.skip-broker-cep = true #control.skip-baguette = true #control.skip-collectors = true -#control.skip-metasolver = true -#control.skip-notification = true -control.upperware-grouping = GLOBAL +control.skip-metasolver = true +control.skip-notification = true +#control.upperware-grouping = GLOBAL ### Debug settings - Load/Save translation results control.tc-load-file = ${EMS_TC_LOAD_FILE:${EMS_TC_FILE:${LOGS_DIR:${EMS_CONFIG_DIR}/../logs}/_TC.json}} @@ -319,7 +320,7 @@ brokercep.broker-url[1] = tcp://0.0.0.0:61616?${BROKER_URL_PROPERTIES} brokercep.broker-url[2] = stomp://0.0.0.0:61610 # Broker URLs for (EMS) consumer and clients -brokercep.broker-url-for-consumer = tcp://${EMS_SERVER_ADDRESS}:61616?${CLIENT_URL_PROPERTIES} +brokercep.broker-url-for-consumer = tcp://127.0.0.1:61616?${CLIENT_URL_PROPERTIES} brokercep.broker-url-for-clients = ${brokercep.broker-protocol}://${EMS_SERVER_ADDRESS}:${brokercep.broker-port}?${CLIENT_URL_PROPERTIES} # Must be a public IP address @@ -527,9 +528,9 @@ baguette.client.install.sessionRecordingDir = ${LOGS_DIR:${EMS_CONFIG_DIR}/../lo #baguette.client.install.parameters.SKIP_JRE_INSTALLATION=true #baguette.client.install.parameters.SKIP_START=true -baguette.client.install.parameters.BAGUETTE_INSTALLATION_MIN_PROCESSORS=2 -baguette.client.install.parameters.BAGUETTE_INSTALLATION_MIN_RAM=2*1024*1024 -baguette.client.install.parameters.BAGUETTE_INSTALLATION_MIN_DISK_FREE=1024*1024 +#baguette.client.install.parameters.BAGUETTE_INSTALLATION_MIN_PROCESSORS=2 +#baguette.client.install.parameters.BAGUETTE_INSTALLATION_MIN_RAM=2*1024*1024 +#baguette.client.install.parameters.BAGUETTE_INSTALLATION_MIN_DISK_FREE=1024*1024 ### Settings for resolving Node state after baguette client installation #baguette.client.install.clientInstallVarName=__EMS_CLIENT_INSTALL__ diff --git a/nebulous/ems-core/config-files/ems-server.yml b/nebulous/ems-core/config-files/ems-server.yml index c8f3503..bd1d427 100644 --- a/nebulous/ems-core/config-files/ems-server.yml +++ b/nebulous/ems-core/config-files/ems-server.yml @@ -11,8 +11,9 @@ ### Global settings ################################################################################ -### Don't touch the next line!! -EMS_SERVER_ADDRESS: ${${control.IP_SETTING}} +### Don't touch the next lines!! +EMS_IP_SETTING: ${P_EMS_IP_SETTING:PUBLIC_IP} +EMS_SERVER_ADDRESS: ${${EMS_IP_SETTING}} DOLLAR: '$' ### Password Encoder settings @@ -114,9 +115,9 @@ control: #skip-broker-cep: true #skip-baguette: true #skip-collectors: true - #skip-metasolver: true - #skip-notification: true - upperware-grouping: GLOBAL + skip-metasolver: true + skip-notification: true + #upperware-grouping: GLOBAL ### Debug settings - Load/Save translation results tc-load-file: ${EMS_TC_LOAD_FILE:${EMS_TC_FILE:${LOGS_DIR:${EMS_CONFIG_DIR}/../logs}/_TC.json}} @@ -329,7 +330,7 @@ brokercep: - stomp://0.0.0.0:61610 # Broker URLs for (EMS) consumer and clients - broker-url-for-consumer: tcp://${EMS_SERVER_ADDRESS}:61616?${CLIENT_URL_PROPERTIES} + broker-url-for-consumer: tcp://127.0.0.1:61616?${CLIENT_URL_PROPERTIES} broker-url-for-clients: ${brokercep.broker-protocol}://${EMS_SERVER_ADDRESS}:${brokercep.broker-port}?${CLIENT_URL_PROPERTIES} # Must be a public IP address @@ -569,7 +570,7 @@ baguette.client.install: sessionRecordingDir: ${LOGS_DIR:${EMS_CONFIG_DIR}/../logs} ### Baguette and Netdata installation parameters (for condition checking) - parameters: + #parameters: #SKIP_IGNORE_CHECK: true #SKIP_DETECTION: true @@ -578,9 +579,9 @@ baguette.client.install: #SKIP_JRE_INSTALLATION: true #SKIP_START: true - BAGUETTE_INSTALLATION_MIN_PROCESSORS: 2 - BAGUETTE_INSTALLATION_MIN_RAM: 2*1024*1024 - BAGUETTE_INSTALLATION_MIN_DISK_FREE: 1024*1024 + #BAGUETTE_INSTALLATION_MIN_PROCESSORS: 2 + #BAGUETTE_INSTALLATION_MIN_RAM: 2*1024*1024 + #BAGUETTE_INSTALLATION_MIN_DISK_FREE: 1024*1024 ### Settings for resolving Node state after baguette client installation #clientInstallVarName: '__EMS_CLIENT_INSTALL__' diff --git a/nebulous/ems-core/config-files/secrets.properties b/nebulous/ems-core/config-files/secrets.properties index 1184da4..aac86f6 100644 --- a/nebulous/ems-core/config-files/secrets.properties +++ b/nebulous/ems-core/config-files/secrets.properties @@ -21,8 +21,3 @@ control.ssl.truststore-password=ENC(ISMbn01HVPbtRPkqm2Lslg==) ### Additional Baguette Server SSH username/passwords: aa/xx, bb/yy #baguette.server.credentials.aa=xx #baguette.server.credentials.bb=yy - -### Other settings -control.IP_SETTING=DEFAULT_IP -control.esb-url= -control.metasolver-configuration-url= diff --git a/nebulous/ems-core/control-service/pom.xml b/nebulous/ems-core/control-service/pom.xml index 22bad01..6e581fe 100644 --- a/nebulous/ems-core/control-service/pom.xml +++ b/nebulous/ems-core/control-service/pom.xml @@ -20,11 +20,11 @@ EMS - Control Service - - 3.1.3 - 1.11.2 - 2.1.0 - 0.11.5 + 4.0-M4 + 3.2.2 + 1.12.4 + 2.3.0 + 0.12.5 ${maven.build.timestamp} yyyy-MM-dd HH:mm:ss @@ -33,10 +33,12 @@ esper-${esper.version}.jar - 0.43.2 + 0.44.0 ems-server + emsuser + /opt/ems-server gr.iccs.imu.ems.control.ControlServiceApplication @@ -176,13 +178,11 @@ --> - - + @@ -208,43 +208,6 @@ - - - - - net.nicoulaj.maven.plugins - checksum-maven-plugin - 1.11 - - - org.bouncycastle - * - - - org.codehaus.plexus - plexus-utils - - - com.google.guava - guava - - - commons-io - commons-io - - - - - org.codehaus.plexus - plexus-utils - 4.0.0 - - - commons-io - commons-io - 2.15.1 - - @@ -291,7 +254,7 @@ io.github.git-commit-id git-commit-id-maven-plugin - 6.0.0 + 7.0.0 user: {}", user); log.debug("jwtAuthorizationFilter: Authorization header --> audience: {}", audience); if (user!=null && audience!=null) { - if (JwtTokenService.AUDIENCE_UPPERWARE.equals(audience)) { + if (audience.contains(JwtTokenService.AUDIENCE_UPPERWARE)) { log.debug("jwtAuthorizationFilter: JWT token is valid"); UsernamePasswordAuthenticationToken authentication = new UsernamePasswordAuthenticationToken(user, null, diff --git a/nebulous/ems-core/control-service/src/main/resources/banner.txt b/nebulous/ems-core/control-service/src/main/resources/banner.txt index d246d52..0f8f260 100644 --- a/nebulous/ems-core/control-service/src/main/resources/banner.txt +++ b/nebulous/ems-core/control-service/src/main/resources/banner.txt @@ -12,6 +12,6 @@ ${AnsiColor.046} :: Spring Boot :: ${AnsiColor.87} ${spring ${AnsiColor.046} :: Java (TM) :: ${AnsiColor.87} (${java.version}) ${AnsiColor.046} :: Build Num. :: ${AnsiColor.226}@buildNumber@ ${AnsiColor.046} :: Build Date :: ${AnsiColor.226}@timestamp@ -${AnsiColor.046} :: SCM Branch :: ${AnsiColor.226}@git.branch@ +${AnsiColor.046} :: SCM Branch :: ${AnsiColor.226}@git.branch@, at Repos.: @git.remote.origin.url@ ${AnsiColor.046} :: Image Tag :: ${AnsiColor.226}@docker.image.name@:@docker.image.tag@ ${AnsiColor.046} :: Description :: ${AnsiColor.226}@build.description@ ${AnsiColor.DEFAULT}${AnsiStyle.NORMAL} \ No newline at end of file diff --git a/nebulous/ems-core/control-service/src/main/resources/public/index.html b/nebulous/ems-core/control-service/src/main/resources/public/index.html index 8e74e5f..921fc8c 100644 --- a/nebulous/ems-core/control-service/src/main/resources/public/index.html +++ b/nebulous/ems-core/control-service/src/main/resources/public/index.html @@ -432,7 +432,7 @@
  • Baguette Client TGZ
  • -
  • Baguette Client MD5
  • +
  • Baguette Client SHA256
  • diff --git a/nebulous/ems-core/pom.xml b/nebulous/ems-core/pom.xml index 4f4a6a1..eb1b6e7 100644 --- a/nebulous/ems-core/pom.xml +++ b/nebulous/ems-core/pom.xml @@ -14,7 +14,7 @@ org.springframework.boot spring-boot-starter-parent - 3.2.1 + 3.2.3 @@ -36,10 +36,10 @@ 21 - 2.4 - 3.11.0 - 2.9.1 - 2.5.3 + 3.3.0 + 3.12.1 + 3.6.3 + 3.7.0 2.10.1 @@ -58,18 +58,18 @@ 3.0.5 - 2.11.0 + 2.12.1 1.77 - 32.1.3-jre + 33.0.0-jre 1.10.0 1.2.6 - 2.16.0 + 2.16.2 2.2 diff --git a/nebulous/ems-core/translator/src/main/java/gr/iccs/imu/ems/translate/Translator.java b/nebulous/ems-core/translator/src/main/java/gr/iccs/imu/ems/translate/Translator.java index 159646e..6b7d814 100644 --- a/nebulous/ems-core/translator/src/main/java/gr/iccs/imu/ems/translate/Translator.java +++ b/nebulous/ems-core/translator/src/main/java/gr/iccs/imu/ems/translate/Translator.java @@ -11,4 +11,7 @@ package gr.iccs.imu.ems.translate; public interface Translator { TranslationContext translate(String modelPath); + default TranslationContext translate(String modelPath, String applicationId) { + return translate(modelPath); + } } \ No newline at end of file diff --git a/nebulous/ems-core/translator/src/main/java/gr/iccs/imu/ems/translate/model/Interval.java b/nebulous/ems-core/translator/src/main/java/gr/iccs/imu/ems/translate/model/Interval.java index cb88ddc..b62aced 100644 --- a/nebulous/ems-core/translator/src/main/java/gr/iccs/imu/ems/translate/model/Interval.java +++ b/nebulous/ems-core/translator/src/main/java/gr/iccs/imu/ems/translate/model/Interval.java @@ -25,5 +25,5 @@ public class Interval extends AbstractInterfaceRootObject { public enum UnitType { DAYS, HOURS, MINUTES, SECONDS, MILLISECONDS, MICROSECONDS, NANOSECONDS } private UnitType unit; - private int period; + private long period; } diff --git a/nebulous/ems-core/util/src/main/java/gr/iccs/imu/ems/util/ConfigWriteService.java b/nebulous/ems-core/util/src/main/java/gr/iccs/imu/ems/util/ConfigWriteService.java new file mode 100644 index 0000000..3d28166 --- /dev/null +++ b/nebulous/ems-core/util/src/main/java/gr/iccs/imu/ems/util/ConfigWriteService.java @@ -0,0 +1,105 @@ +/* + * Copyright (C) 2017-2023 Institute of Communication and Computer Systems (imu.iccs.gr) + * + * This Source Code Form is subject to the terms of the Mozilla Public License, v2.0, unless + * Esper library is used, in which case it is subject to the terms of General Public License v2.0. + * If a copy of the MPL was not distributed with this file, you can obtain one at + * https://www.mozilla.org/en-US/MPL/2.0/ + */ + +package gr.iccs.imu.ems.util; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; +import lombok.Data; +import lombok.NonNull; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.EnumUtils; +import org.apache.commons.lang3.StringUtils; +import org.springframework.stereotype.Service; + +import java.io.IOException; +import java.io.StringWriter; +import java.nio.file.Files; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.LinkedHashMap; +import java.util.Map; +import java.util.Properties; + +@Slf4j +@Service +@RequiredArgsConstructor +public class ConfigWriteService { + private final Map configurations = new HashMap<>(); + private final Gson gson = new GsonBuilder().setPrettyPrinting().create(); + + public Configuration createConfigFile(@NonNull String fileName, String format) { + if (configurations.containsKey(fileName)) + throw new IllegalArgumentException("Config. file already exists: "+fileName); + return getOrCreateConfigFile(fileName, format); + } + + public Configuration getConfigFile(@NonNull String fileName) { + return configurations.get(fileName); + } + + public Configuration getOrCreateConfigFile(@NonNull String fileName, String format) { + if (StringUtils.isBlank(format) && StringUtils.endsWithIgnoreCase(fileName, ".json")) format = "json"; + final Format fmt = EnumUtils.getEnumIgnoreCase(Format.class, format, Format.PROPERTIES); + return configurations.computeIfAbsent(fileName, s -> new Configuration(Paths.get(fileName), fmt)); + } + + public boolean removeConfigFile(@NonNull String fileName, boolean alsoRemoveFile) { + Configuration c = configurations.remove(fileName); + if (c!=null) { + if (! c.getConfigPath().toFile().delete()) { + log.warn("removeConfigFile: Failed to remove config. file from the disk: {}", c.getConfigPath()); + } + return true; + } + return false; + } + + enum Format { PROPERTIES, JSON } + + @Data + @RequiredArgsConstructor + public class Configuration { + @NonNull private final Path configPath; + private final Format format; + private final Map contentMap = new LinkedHashMap<>(); + + public void put(@NonNull String key, String value) throws IOException { + contentMap.put(key, value); + write(); + } + + public void putAll(@NonNull Map map) throws IOException { + contentMap.putAll(map); + write(); + } + + public void write() throws IOException { + String content; + if (format==Format.JSON) content = asJson(); + else content = asProperties(); + Files.writeString(configPath, content); + } + + private String asProperties() throws IOException { + try (StringWriter writer = new StringWriter()) { + Properties p = new Properties(); + p.putAll(contentMap); + p.store(writer, null); + return writer.toString(); + } + } + + private String asJson() { + return gson.toJson(contentMap); + } + } +} diff --git a/nebulous/ems-core/util/src/main/java/gr/iccs/imu/ems/util/EmsConstant.java b/nebulous/ems-core/util/src/main/java/gr/iccs/imu/ems/util/EmsConstant.java index af6f6b6..9323434 100644 --- a/nebulous/ems-core/util/src/main/java/gr/iccs/imu/ems/util/EmsConstant.java +++ b/nebulous/ems-core/util/src/main/java/gr/iccs/imu/ems/util/EmsConstant.java @@ -13,12 +13,18 @@ package gr.iccs.imu.ems.util; * EMS constant */ public class EmsConstant { - public final static String EMS_PROPERTIES_PREFIX = ""; //""ems."; + public final static String EMS_PROPERTIES_PREFIX = ""; //"ems."; public final static String EVENT_PROPERTY_SOURCE_ADDRESS = "producer-host"; public final static String EVENT_PROPERTY_ORIGINAL_DESTINATION = "original-destination"; public final static String EVENT_PROPERTY_EFFECTIVE_DESTINATION = "effective-destination"; + public final static String EVENT_PROPERTY_KEY = "destination-key"; + public final static String NETDATA_METRIC_KEY = "_netdata_metric"; public final static String COLLECTOR_DESTINATION_ALIASES = "destination-aliases"; public final static String COLLECTOR_DESTINATION_ALIASES_DELIMITERS = "[,;: \t\r\n]+"; public final static String COLLECTOR_ALLOWED_TOPICS_VAR = "COLLECTOR_ALLOWED_TOPICS"; + public static final String COLLECTOR_CONFIGURATIONS_VAR = "COLLECTOR_CONFIGURATIONS"; + + public final static String EMS_CLIENT_K8S_CONFIG_MAP_FILE = "ems-client-configmap.json"; + public final static String EMS_CLIENT_K8S_CONFIG_MAP_FORMAT = "json"; } \ No newline at end of file diff --git a/nebulous/ems-core/util/src/main/java/gr/iccs/imu/ems/util/EmsRelease.java b/nebulous/ems-core/util/src/main/java/gr/iccs/imu/ems/util/EmsRelease.java new file mode 100644 index 0000000..8f25910 --- /dev/null +++ b/nebulous/ems-core/util/src/main/java/gr/iccs/imu/ems/util/EmsRelease.java @@ -0,0 +1,24 @@ +/* + * Copyright (C) 2017-2023 Institute of Communication and Computer Systems (imu.iccs.gr) + * + * This Source Code Form is subject to the terms of the Mozilla Public License, v2.0, unless + * Esper library is used, in which case it is subject to the terms of General Public License v2.0. + * If a copy of the MPL was not distributed with this file, you can obtain one at + * https://www.mozilla.org/en-US/MPL/2.0/ + */ + +package gr.iccs.imu.ems.util; + +/** + * EMS Release info + */ +public class EmsRelease { + public final static String EMS_ID = "ems"; + public final static String EMS_NAME = "Event Management System"; + public final static String EMS_VERSION = EmsRelease.class.getPackage().getImplementationVersion(); + public final static String EMS_COPYRIGHT = + "Copyright (C) 2017-2025 Institute of Communication and Computer Systems (imu.iccs.gr)"; + public final static String EMS_LICENSE = "Mozilla Public License, v2.0"; + public final static String EMS_DESCRIPTION = String.format("\n%s (%s), v.%s, %s\n%s\n", + EMS_NAME, EMS_ID, EMS_VERSION, EMS_LICENSE, EMS_COPYRIGHT); +} \ No newline at end of file diff --git a/nebulous/ems-core/util/src/main/java/gr/iccs/imu/ems/util/KeystoreUtil.java b/nebulous/ems-core/util/src/main/java/gr/iccs/imu/ems/util/KeystoreUtil.java index d812686..a894418 100644 --- a/nebulous/ems-core/util/src/main/java/gr/iccs/imu/ems/util/KeystoreUtil.java +++ b/nebulous/ems-core/util/src/main/java/gr/iccs/imu/ems/util/KeystoreUtil.java @@ -30,6 +30,7 @@ import java.math.BigInteger; import java.net.InetAddress; import java.nio.charset.StandardCharsets; import java.nio.file.Files; +import java.nio.file.Path; import java.nio.file.Paths; import java.security.*; import java.security.cert.Certificate; @@ -473,11 +474,26 @@ public class KeystoreUtil { log.debug(" Entry SAN: {}", properties.getKeyEntryExtSAN()); log.debug(" Entry Gen.: {}", properties.getKeyEntryGenerate()); - IKeystoreAndCertificateProperties.KEY_ENTRY_GENERATE keyGen = properties.getKeyEntryGenerate(); - boolean gen = (keyGen==IKeystoreAndCertificateProperties.KEY_ENTRY_GENERATE.YES || keyGen==IKeystoreAndCertificateProperties.KEY_ENTRY_GENERATE.ALWAYS); + IKeystoreAndCertificateProperties.KEY_ENTRY_GENERATE keyGenerationStrategy = properties.getKeyEntryGenerate(); + boolean generateKey = (keyGenerationStrategy==IKeystoreAndCertificateProperties.KEY_ENTRY_GENERATE.YES + || keyGenerationStrategy==IKeystoreAndCertificateProperties.KEY_ENTRY_GENERATE.ALWAYS); + + // If ALWAYS then remove previous keystore and truststore files + if (generateKey) { + Path oldKeystoreFile = Path.of(properties.getKeystoreFile()); + if (Files.exists(oldKeystoreFile)) + Files.deleteIfExists(oldKeystoreFile); + Path oldTruststoreFile = Path.of(properties.getTruststoreFile()); + if (Files.exists(oldTruststoreFile)) + Files.deleteIfExists(oldTruststoreFile); + Path oldCertificateFile = Path.of(properties.getCertificateFile()); + if (Files.exists(oldCertificateFile)) + Files.deleteIfExists(oldCertificateFile); + log.debug(" Removed old Keystore, Truststore and Certificate files"); + } // Check if key entry is missing - if (keyGen==IKeystoreAndCertificateProperties.KEY_ENTRY_GENERATE.IF_MISSING) { + if (keyGenerationStrategy==IKeystoreAndCertificateProperties.KEY_ENTRY_GENERATE.IF_MISSING) { // Check if keystore and truststore files exist (and create if they don't) KeystoreUtil .getKeystore(properties.getKeystoreFile(), properties.getKeystoreType(), properties.getKeystorePassword()) @@ -497,12 +513,12 @@ public class KeystoreUtil { log.debug(" Keystore already contains entry: {}", properties.getKeyEntryName()); } else { log.debug(" Keystore does not contain entry: {}", properties.getKeyEntryName()); - gen = true; + generateKey = true; } } // Check if IP address is in subject CN or SAN list - if (keyGen==IKeystoreAndCertificateProperties.KEY_ENTRY_GENERATE.IF_IP_CHANGED) { + if (keyGenerationStrategy==IKeystoreAndCertificateProperties.KEY_ENTRY_GENERATE.IF_IP_CHANGED) { // Check if keystore and truststore files exist (and create if they don't) KeystoreUtil .getKeystore(properties.getKeystoreFile(), properties.getKeystoreType(), properties.getKeystorePassword()) @@ -527,13 +543,13 @@ public class KeystoreUtil { // check if Default and Public IP addresses are contained in 'addrList' boolean defaultFound = addrList.stream().anyMatch(s -> s.equals(defaultIp)); boolean publicFound = addrList.stream().anyMatch(s -> s.equals(publicIp)); - gen = !defaultFound || !publicFound; + generateKey = !defaultFound || !publicFound; log.debug(" Address has changed: {} (default-ip-found={}, public-ip-found={})", - gen, defaultFound, publicFound); + generateKey, defaultFound, publicFound); } // Generate new key pair and certificate, and update keystore and trust store - if (gen) { + if (generateKey) { log.debug(" Generating new Key pair and Certificate for: {}", properties.getKeyEntryName()); KeystoreUtil ksUtil = KeystoreUtil diff --git a/nebulous/ems-core/util/src/main/java/gr/iccs/imu/ems/util/NetUtil.java b/nebulous/ems-core/util/src/main/java/gr/iccs/imu/ems/util/NetUtil.java index 73d0564..ebda88b 100644 --- a/nebulous/ems-core/util/src/main/java/gr/iccs/imu/ems/util/NetUtil.java +++ b/nebulous/ems-core/util/src/main/java/gr/iccs/imu/ems/util/NetUtil.java @@ -9,6 +9,7 @@ package gr.iccs.imu.ems.util; +import lombok.Getter; import lombok.extern.slf4j.Slf4j; import org.apache.commons.lang3.StringUtils; @@ -30,6 +31,11 @@ public class NetUtil { private final static String[][] PUBLIC_ADDRESS_DISCOVERY_SERVICES; + @Getter + private final static boolean usePublic; + @Getter + private final static boolean useDefault; + static { // Configure Address Filters String filtersStr = System.getenv("NET_UTIL_ADDRESS_FILTERS"); @@ -69,6 +75,12 @@ public class NetUtil { servicesList.add(Arrays.asList("WhatIsMyIpAddress", "http://bot.whatismyipaddress.com/").toArray(new String[0])); } PUBLIC_ADDRESS_DISCOVERY_SERVICES = servicesList.toArray(new String[0][]); + + // Configure IP address setting + String s = System.getenv("IP_SETTING"); + s = s!=null ? s.trim() : ""; + useDefault = "DEFAULT_IP".equalsIgnoreCase(s); + usePublic = ! useDefault; } // ------------------------------------------------------------------------ @@ -272,6 +284,12 @@ public class NetUtil { // ------------------------------------------------------------------------ + public static String getIpSettingAddress() { + return usePublic ? getPublicIpAddress() : getDefaultIpAddress(); + } + + // ------------------------------------------------------------------------ + public static boolean isLocalAddress(String addr) throws UnknownHostException { return isLocalAddress(InetAddress.getByName(addr)); } diff --git a/nebulous/ems-core/web-admin/src/views/admin/widgets/rest-call-forms.js b/nebulous/ems-core/web-admin/src/views/admin/widgets/rest-call-forms.js index e322996..9867977 100644 --- a/nebulous/ems-core/web-admin/src/views/admin/widgets/rest-call-forms.js +++ b/nebulous/ems-core/web-admin/src/views/admin/widgets/rest-call-forms.js @@ -45,10 +45,11 @@ export const FORM_TYPE_OPTIONS = [ 'text': 'Credentials', 'priority': 1001, 'options': [ - { 'id': 'get-cred', 'text': 'EMS server Broker credentials', 'url': '/broker/credentials', 'method': 'GET', 'form': '', 'priority': 1 }, - { 'id': 'get-ref', 'text': 'VM credentials by Ref', 'url': '/baguette/ref/{ref}', 'method': 'GET', 'form': 'ref-form', 'priority': 2 }, - { 'id': 'new-otp', 'text': 'New OTP', 'url': '/ems/otp/new', 'method': 'GET', 'form': '', 'priority': 3 }, - { 'id': 'del-otp', 'text': 'Delete OTP', 'url': '/ems/otp/remove/{otp}', 'method': 'GET', 'form': 'otp-form', 'priority': 4 }, + { 'id': 'get-bc-cred', 'text': 'EMS server Broker credentials', 'url': '/broker/credentials', 'method': 'GET', 'form': '', 'priority': 1 }, + { 'id': 'get-bs-cred', 'text': 'Baguette Server connection info', 'url': '/baguette/connectionInfo', 'method': 'GET', 'form': '', 'priority': 2 }, + { 'id': 'get-ref', 'text': 'VM credentials by Ref', 'url': '/baguette/ref/{ref}', 'method': 'GET', 'form': 'ref-form', 'priority': 3 }, + { 'id': 'new-otp', 'text': 'New OTP', 'url': '/ems/otp/new', 'method': 'GET', 'form': '', 'priority': 4 }, + { 'id': 'del-otp', 'text': 'Delete OTP', 'url': '/ems/otp/remove/{otp}', 'method': 'GET', 'form': 'otp-form', 'priority': 5 }, ] }, diff --git a/nebulous/ems-core/web-admin/src/views/common/header/header.html b/nebulous/ems-core/web-admin/src/views/common/header/header.html index 982acc5..31714e5 100644 --- a/nebulous/ems-core/web-admin/src/views/common/header/header.html +++ b/nebulous/ems-core/web-admin/src/views/common/header/header.html @@ -54,7 +54,7 @@ Broker Client .sh Baguette Client - Baguette Client MD5 + Baguette Client SHA256 Details... diff --git a/nebulous/ems-core/web-admin/src/views/common/menu-sidebar/menu-sidebar.html b/nebulous/ems-core/web-admin/src/views/common/menu-sidebar/menu-sidebar.html index 8cf700e..25e1d9c 100644 --- a/nebulous/ems-core/web-admin/src/views/common/menu-sidebar/menu-sidebar.html +++ b/nebulous/ems-core/web-admin/src/views/common/menu-sidebar/menu-sidebar.html @@ -77,10 +77,10 @@