From 517bccd5d77539457766b42504d4b4d460c03125 Mon Sep 17 00:00:00 2001 From: kbeyro <121854496+kbeyro@users.noreply.github.com> Date: Wed, 14 May 2025 15:00:02 +0200 Subject: [PATCH 01/10] add mail type add creation of file in case of missing --- .../kubernetes/ClusterMonitoringJob.java | 2 + .../kubernetes/RemoteClusterManager.java | 83 +++++++++++++++---- .../kubernetes/StringMultipartFile.java | 67 +++++++++++++++ .../api/model/RemoteClusterView.java | 2 + .../kubernetes/entities/KCluster.java | 4 + .../notifications/templates/MailType.java | 3 +- .../shell/data/mails/clusterUnavailable.json | 55 ++++++++++++ 7 files changed, 198 insertions(+), 18 deletions(-) create mode 100644 src/main/java/net/geant/nmaas/externalservices/kubernetes/StringMultipartFile.java create mode 100644 src/test/shell/data/mails/clusterUnavailable.json diff --git a/src/main/java/net/geant/nmaas/externalservices/kubernetes/ClusterMonitoringJob.java b/src/main/java/net/geant/nmaas/externalservices/kubernetes/ClusterMonitoringJob.java index bc88e1f2c..784fa5100 100644 --- a/src/main/java/net/geant/nmaas/externalservices/kubernetes/ClusterMonitoringJob.java +++ b/src/main/java/net/geant/nmaas/externalservices/kubernetes/ClusterMonitoringJob.java @@ -17,6 +17,8 @@ public class ClusterMonitoringJob implements Job { @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { log.error("Triggering cluster health check..."); + remoteClusterManager.restoreFileIfMissing(); + log.warn("File checked, everything looks fine. Next time: Update clusters state."); remoteClusterManager.updateAllClusterState(); } } diff --git a/src/main/java/net/geant/nmaas/externalservices/kubernetes/RemoteClusterManager.java b/src/main/java/net/geant/nmaas/externalservices/kubernetes/RemoteClusterManager.java index f9ddb9cf4..d0bdf10e5 100644 --- a/src/main/java/net/geant/nmaas/externalservices/kubernetes/RemoteClusterManager.java +++ b/src/main/java/net/geant/nmaas/externalservices/kubernetes/RemoteClusterManager.java @@ -3,30 +3,31 @@ package net.geant.nmaas.externalservices.kubernetes; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; import io.fabric8.kubernetes.client.Config; -import io.fabric8.kubernetes.client.DefaultKubernetesClient; import io.fabric8.kubernetes.client.KubernetesClient; import io.fabric8.kubernetes.client.KubernetesClientBuilder; import io.fabric8.kubernetes.client.KubernetesClientException; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import net.geant.nmaas.externalservices.kubernetes.entities.KCluster; import net.geant.nmaas.externalservices.kubernetes.api.model.RemoteClusterView; +import net.geant.nmaas.externalservices.kubernetes.entities.KCluster; import net.geant.nmaas.externalservices.kubernetes.entities.KClusterDeployment; import net.geant.nmaas.externalservices.kubernetes.entities.KClusterIngress; import net.geant.nmaas.externalservices.kubernetes.entities.KClusterState; import net.geant.nmaas.externalservices.kubernetes.repositories.KClusterRepository; +import net.geant.nmaas.notifications.MailAttributes; +import net.geant.nmaas.notifications.NotificationEvent; +import net.geant.nmaas.notifications.templates.MailType; +import net.geant.nmaas.portal.api.domain.UserView; import net.geant.nmaas.portal.persistent.entity.Domain; import net.geant.nmaas.portal.service.DomainService; import org.modelmapper.ModelMapper; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; import org.springframework.web.multipart.MultipartFile; import java.io.File; import java.io.IOException; import java.io.InputStream; -import java.io.InterruptedIOException; -import java.net.SocketTimeoutException; -import java.nio.charset.StandardCharsets; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; @@ -36,7 +37,10 @@ import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.time.OffsetDateTime; import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.stream.Collectors; @@ -48,13 +52,15 @@ public class RemoteClusterManager { private final static ModelMapper modelMapper = new ModelMapper(); - private final KClusterRepository KClusterRepository; + private final KClusterRepository clusterRepository; private final KubernetesClusterIngressManager kClusterIngressManager; private final KubernetesClusterDeploymentManager kClusterDeploymentManager; private final DomainService domainService; + private final ApplicationEventPublisher eventPublisher; + public RemoteClusterView getClusterView(Long id) { - Optional<KCluster> cluster = KClusterRepository.findById(id); + Optional<KCluster> cluster = clusterRepository.findById(id); if (cluster.isPresent()) { return toView(cluster.get()); } else { @@ -63,12 +69,12 @@ public class RemoteClusterManager { } public List<RemoteClusterView> getAllClusterView() { - List<KCluster> clusters = KClusterRepository.findAll(); + List<KCluster> clusters = clusterRepository.findAll(); return clusters.stream().map(RemoteClusterManager::toView).collect(Collectors.toList()); } public KCluster getCluster(Long id) { - Optional<KCluster> cluster = KClusterRepository.findById(id); + Optional<KCluster> cluster = clusterRepository.findById(id); if (cluster.isPresent()) { return cluster.get(); } else { @@ -99,7 +105,7 @@ public class RemoteClusterManager { log.debug("Filed saved in: {}", savedPath); entity.setPathConfigFile(savedPath); - KCluster cluster = this.KClusterRepository.save(entity); + KCluster cluster = this.clusterRepository.save(entity); log.debug("Cluster saved: {}", cluster.toString()); return toView(cluster); } @@ -126,10 +132,11 @@ public class RemoteClusterManager { .creationDate(OffsetDateTime.now()) .modificationDate(OffsetDateTime.now()) .codename(configView.getClusters().stream().findFirst().get().getName()) - .clusterConfigFile( new String(file.getBytes())) + .clusterConfigFile(new String(file.getBytes())) .deployment(deployment) .ingress(ingress) .state(KClusterState.UNKNOWN) + .contactEmail(view.getContactEmail()) .currentStateSince(OffsetDateTime.now()) .domains(view.getDomainNames().stream().map(d -> { Optional<Domain> dom = domainService.findDomain(d); @@ -153,7 +160,7 @@ public class RemoteClusterManager { } public RemoteClusterView updateCluster(RemoteClusterView cluster, Long id) { - Optional<KCluster> entity = KClusterRepository.findById(id); + Optional<KCluster> entity = clusterRepository.findById(id); if (entity.isPresent()) { checkRequest(entity.get(), cluster, id); @@ -176,7 +183,7 @@ public class RemoteClusterManager { updated.setDeployment(modelMapper.map(cluster.getDeployment(), KClusterDeployment.class)); - updated = KClusterRepository.save(updated); + updated = clusterRepository.save(updated); //TODO : implement file update logic return toView(updated); @@ -239,7 +246,7 @@ public class RemoteClusterManager { } public void updateAllClusterState() { - List<KCluster> kClusters = KClusterRepository.findAll(); + List<KCluster> kClusters = clusterRepository.findAll(); kClusters.forEach(cluster -> { Config config = null; try { @@ -249,7 +256,7 @@ public class RemoteClusterManager { } try { KubernetesClient client = new KubernetesClientBuilder().withConfig(config).build(); - log.debug("Get kubernetes version , something works {}",client.getKubernetesVersion().getPlatform()); + log.debug("Get kubernetes version , something works {}", client.getKubernetesVersion().getPlatform()); //try to download kubernetes version to make sure connection to cluster is working updateStateIfNeeded(cluster, KClusterState.UP); @@ -257,17 +264,19 @@ public class RemoteClusterManager { log.error("Can not connect to cluster {}", cluster.getCodename()); log.error(e.getMessage()); updateStateIfNeeded(cluster, KClusterState.DOWN); +// sendMail(cluster); - } catch (RuntimeException ex ) { + } catch (RuntimeException ex) { log.error("Runtime error while checking health of cluster {}", ex.getMessage()); updateStateIfNeeded(cluster, KClusterState.UNKNOWN); +// sendMail(cluster); } catch (Exception ex) { log.error("Caught unexpected exception: {}", ex.getMessage(), ex); } }); - KClusterRepository.saveAll(kClusters); + clusterRepository.saveAll(kClusters); } @@ -278,4 +287,44 @@ public class RemoteClusterManager { } } + private void sendMail(KCluster kCluster) { + UserView recipient = UserView.builder().email(kCluster.getContactEmail()).username(kCluster.getContactEmail()).selectedLanguage("EN").build(); + Map<String, Object> attr = new HashMap<>(); + attr.put("clusterId", kCluster.getId()); + attr.put("clusterCodename", kCluster.getCodename()); + attr.put("clusterName", kCluster.getName()); + MailAttributes mailAttributes = MailAttributes.builder() + .mailType(MailType.CLUSTER_UNAVAILABLE) + .otherAttributes(attr) + .addressees(Collections.singletonList(recipient)) + .build(); + + this.eventPublisher.publishEvent(new NotificationEvent(this, mailAttributes)); + + } + + public void restoreFileIfMissing() { + List<KCluster> clusters = clusterRepository.findAll(); + clusters.forEach(cluster -> { + if (!isFileAvailable(cluster.getPathConfigFile())) { + MultipartFile file = new StringMultipartFile("file", + "config.yaml", + "application/x-yaml", + cluster.getClusterConfigFile()); + try { + String savedPath = saveFileToTmp(file); + cluster.setPathConfigFile(savedPath); + this.clusterRepository.save(cluster); + } catch (IOException | NoSuchAlgorithmException e) { + log.error("Problem with resaved kubernetes config file from string to TMP folder. {}", e.getMessage()); + } + } + }); + } + + + public boolean isFileAvailable(String pathStr) { + Path path = Paths.get(pathStr); + return Files.exists(path) && Files.isRegularFile(path) && Files.isReadable(path); + } } \ No newline at end of file diff --git a/src/main/java/net/geant/nmaas/externalservices/kubernetes/StringMultipartFile.java b/src/main/java/net/geant/nmaas/externalservices/kubernetes/StringMultipartFile.java new file mode 100644 index 000000000..42b7fea06 --- /dev/null +++ b/src/main/java/net/geant/nmaas/externalservices/kubernetes/StringMultipartFile.java @@ -0,0 +1,67 @@ +package net.geant.nmaas.externalservices.kubernetes; + +import org.springframework.web.multipart.MultipartFile; + +import java.io.ByteArrayInputStream; +import java.io.File; +import java.io.FileOutputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.StandardCharsets; + +public class StringMultipartFile implements MultipartFile { + + private final byte[] content; + private final String name; + private final String originalFilename; + private final String contentType; + + public StringMultipartFile(String name, String originalFilename, String contentType, String contentStr) { + this.name = name; + this.originalFilename = originalFilename; + this.contentType = contentType; + this.content = contentStr.getBytes(StandardCharsets.UTF_8); + } + + @Override + public String getName() { + return name; + } + + @Override + public String getOriginalFilename() { + return originalFilename; + } + + @Override + public String getContentType() { + return contentType; + } + + @Override + public boolean isEmpty() { + return content.length == 0; + } + + @Override + public long getSize() { + return content.length; + } + + @Override + public byte[] getBytes() { + return content; + } + + @Override + public InputStream getInputStream() { + return new ByteArrayInputStream(content); + } + + @Override + public void transferTo(File dest) throws IOException, IllegalStateException { + try (FileOutputStream out = new FileOutputStream(dest)) { + out.write(content); + } + } +} \ No newline at end of file diff --git a/src/main/java/net/geant/nmaas/externalservices/kubernetes/api/model/RemoteClusterView.java b/src/main/java/net/geant/nmaas/externalservices/kubernetes/api/model/RemoteClusterView.java index 563190078..9404460b9 100644 --- a/src/main/java/net/geant/nmaas/externalservices/kubernetes/api/model/RemoteClusterView.java +++ b/src/main/java/net/geant/nmaas/externalservices/kubernetes/api/model/RemoteClusterView.java @@ -43,4 +43,6 @@ public class RemoteClusterView { private OffsetDateTime currentStateSince; + private String contactEmail; + } \ No newline at end of file diff --git a/src/main/java/net/geant/nmaas/externalservices/kubernetes/entities/KCluster.java b/src/main/java/net/geant/nmaas/externalservices/kubernetes/entities/KCluster.java index e5e81ce51..a8aab72d9 100644 --- a/src/main/java/net/geant/nmaas/externalservices/kubernetes/entities/KCluster.java +++ b/src/main/java/net/geant/nmaas/externalservices/kubernetes/entities/KCluster.java @@ -80,6 +80,9 @@ public class KCluster { @Column(nullable = false) private OffsetDateTime currentStateSince; + @Column(nullable = false) + private String contactEmail; + public List<Domain> getDomains() { return domains != null ? domains : new ArrayList<>(); } @@ -97,6 +100,7 @@ public class KCluster { ", clusterConfigFile='" + clusterConfigFile + '\'' + ", pathConfigFile='" + pathConfigFile + '\'' + ", state='" + state + '\'' + + ", email='" + contactEmail + '\'' + ", ingress=" + (ingress != null ? ingress.getId() : "null") + ", deployment=" + (deployment != null ? deployment.getId() : "null") + '}'; diff --git a/src/main/java/net/geant/nmaas/notifications/templates/MailType.java b/src/main/java/net/geant/nmaas/notifications/templates/MailType.java index 9291f9814..afa0a6167 100644 --- a/src/main/java/net/geant/nmaas/notifications/templates/MailType.java +++ b/src/main/java/net/geant/nmaas/notifications/templates/MailType.java @@ -25,5 +25,6 @@ public enum MailType { NEW_BULK_SSO_LOGIN, NEW_BULK_LOGIN, - VLAB_REQUEST + VLAB_REQUEST, + CLUSTER_UNAVAILABLE } diff --git a/src/test/shell/data/mails/clusterUnavailable.json b/src/test/shell/data/mails/clusterUnavailable.json new file mode 100644 index 000000000..7930a84d8 --- /dev/null +++ b/src/test/shell/data/mails/clusterUnavailable.json @@ -0,0 +1,55 @@ +{ + "mailType": "CLUSTER_UNAVAILABLE", + "globalInformation": { + "LOGO_ALT": "GÉANT logo", + "PORTAL_LOGO_ALT": "nmaas logo", + "SENDER_INFO": "Ⓒ GÉANT Association Hoekenrode 3 1102 BR - Amsterdam – Zuidoost- The Netherlands" + }, + "templates": [ + { + "language": "en", + "subject": "nmaas: Cluster is unavailable", + "template": { + "HEADER": "Dear ${username}", + "CONTENT": "<p> Cluster <b>{clusterCodename}</b> is unavailable.</p> <p><b> Cluster ID:<b> {clusterId}</p> <p><b> Cluster CodeName:<b> {clusterCodename}</p> <p>Please contact the administration for further information.</p>", + "SENDER": "Best regards,<br />nmaas Team", + "NOREPLY": "This is an automatically generated message, please do not reply.", + "SENDER_POLICY": "" + } + }, + { + "language": "fr", + "subject": "nmaas: Cluster is unavailable", + "template": { + "HEADER": "Dear ${username}", + "CONTENT": "<p> Cluster <b>{clusterCodename}</b> is unavailable.</p> <p><b> Cluster ID:<b> {clusterId}</p> <p><b> Cluster CodeName:<b> {clusterCodename}</p> <p>Please contact the administration for further information.</p>", + + "SENDER": "Best regards,<br />nmaas Team", + "NOREPLY": "This is an automatically generated message, please do not reply.", + "SENDER_POLICY": "" + } + }, + { + "language": "de", + "subject": "nmaas: Cluster is unavailable", + "template": { + "HEADER": "Dear ${username}", + "CONTENT": "<p> Cluster <b>{clusterCodename}</b> is unavailable.</p> <p><b> Cluster ID:<b> {clusterId}</p> <p><b> Cluster CodeName:<b> {clusterCodename}</p> <p>Please contact the administration for further information.</p>", + "SENDER": "Best regards,<br />nmaas Team", + "NOREPLY": "This is an automatically generated message, please do not reply.", + "SENDER_POLICY": "" + } + }, + { + "language": "pl", + "subject": "nmaas: Cluster jest niedostępny", + "template": { + "HEADER": "Drogi ${username}", + "CONTENT": "<p> Cluster <b>{clusterCodename}</b> is unavailable.</p> <p><b> Cluster ID:<b> {clusterId}</p> <p><b> Cluster CodeName:<b> {clusterCodename}</p> <p>Please contact the administration for further information.</p>", + "SENDER": "Z pozdrowieniami,<br />Zespół nmaas", + "NOREPLY": "Ta wiadomość została wygenerowana automatycznie.", + "SENDER_POLICY": "" + } + } + ] +} \ No newline at end of file -- GitLab From 33fdeb2bef6b8acbdbc37478cf4cce1044a87a9b Mon Sep 17 00:00:00 2001 From: kbeyro <121854496+kbeyro@users.noreply.github.com> Date: Wed, 14 May 2025 15:59:48 +0200 Subject: [PATCH 02/10] catch IO exception and set state --- .../externalservices/kubernetes/RemoteClusterManager.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/main/java/net/geant/nmaas/externalservices/kubernetes/RemoteClusterManager.java b/src/main/java/net/geant/nmaas/externalservices/kubernetes/RemoteClusterManager.java index d0bdf10e5..c18671f50 100644 --- a/src/main/java/net/geant/nmaas/externalservices/kubernetes/RemoteClusterManager.java +++ b/src/main/java/net/geant/nmaas/externalservices/kubernetes/RemoteClusterManager.java @@ -29,6 +29,7 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; +import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; @@ -251,8 +252,9 @@ public class RemoteClusterManager { Config config = null; try { config = Config.fromKubeconfig(Files.readString(Path.of(cluster.getPathConfigFile()))); - } catch (IOException e) { - throw new RuntimeException(e); + } catch (IOException e) { + log.error("IO error with accesing the file {}", e.getMessage()); + updateStateIfNeeded(cluster, KClusterState.UNKNOWN); } try { KubernetesClient client = new KubernetesClientBuilder().withConfig(config).build(); @@ -266,7 +268,7 @@ public class RemoteClusterManager { updateStateIfNeeded(cluster, KClusterState.DOWN); // sendMail(cluster); - } catch (RuntimeException ex) { + } catch (RuntimeException ex) { log.error("Runtime error while checking health of cluster {}", ex.getMessage()); updateStateIfNeeded(cluster, KClusterState.UNKNOWN); // sendMail(cluster); -- GitLab From cb5339fd3ae032727dcabbe4dd055ba3081f088a Mon Sep 17 00:00:00 2001 From: kbeyro <121854496+kbeyro@users.noreply.github.com> Date: Wed, 14 May 2025 16:00:04 +0200 Subject: [PATCH 03/10] add labels --- src/test/shell/data/i18n/de.json | 3 ++- src/test/shell/data/i18n/en.json | 3 ++- src/test/shell/data/i18n/fr.json | 3 ++- src/test/shell/data/i18n/pl.json | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/test/shell/data/i18n/de.json b/src/test/shell/data/i18n/de.json index a70f151f6..3e5586f4b 100644 --- a/src/test/shell/data/i18n/de.json +++ b/src/test/shell/data/i18n/de.json @@ -190,7 +190,8 @@ "STATE" : "State", "UP" : "Up", "DOWN" : "Down", - "UNKNOWN" : "Unknown" + "UNKNOWN" : "Unknown", + "DETAILS" : "Details" }, "GITLAB": { "TITLE": "GitLab Konfiguration", diff --git a/src/test/shell/data/i18n/en.json b/src/test/shell/data/i18n/en.json index 7588befe2..f8f4354e2 100644 --- a/src/test/shell/data/i18n/en.json +++ b/src/test/shell/data/i18n/en.json @@ -191,7 +191,8 @@ "STATE" : "State", "UP" : "Up", "DOWN" : "Down", - "UNKNOWN" : "Unknown" + "UNKNOWN" : "Unknown", + "DETAILS" : "Details" }, "GITLAB": { "TITLE": "GitLab configuration", diff --git a/src/test/shell/data/i18n/fr.json b/src/test/shell/data/i18n/fr.json index 7d83e9fca..fcdb70a03 100644 --- a/src/test/shell/data/i18n/fr.json +++ b/src/test/shell/data/i18n/fr.json @@ -192,7 +192,8 @@ "STATE" : "State", "UP" : "Up", "DOWN" : "Down", - "UNKNOWN" : "Unknown" + "UNKNOWN" : "Unknown", + "DETAILS" : "Details" }, "GITLAB": { "TITLE": "Configuration de GitLab", diff --git a/src/test/shell/data/i18n/pl.json b/src/test/shell/data/i18n/pl.json index f9fa8af3f..6e58ed899 100644 --- a/src/test/shell/data/i18n/pl.json +++ b/src/test/shell/data/i18n/pl.json @@ -191,7 +191,8 @@ "STATE" : "Stan", "UP" : "Aktywny", "DOWN" : "Nieaktywny", - "UNKNOWN" : "Nieznany" + "UNKNOWN" : "Nieznany", + "DETAILS" : "Detale" }, "GITLAB": { "TITLE": "Konfiguracja GitLab", -- GitLab From c6207da535135b7528c8b5e9ba7633ac1ce4b899 Mon Sep 17 00:00:00 2001 From: kbeyro <121854496+kbeyro@users.noreply.github.com> Date: Thu, 15 May 2025 12:32:00 +0200 Subject: [PATCH 04/10] add migration, change name --- .../externalservices/kubernetes/RemoteClusterManager.java | 3 +-- .../java/net/geant/nmaas/notifications/templates/MailType.java | 2 +- .../common/V1.8.0_20250515_1230__AddContactEmailToCluster.sql | 1 + src/test/shell/data/mails/clusterUnavailable.json | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) create mode 100644 src/main/resources/db/migration/common/V1.8.0_20250515_1230__AddContactEmailToCluster.sql diff --git a/src/main/java/net/geant/nmaas/externalservices/kubernetes/RemoteClusterManager.java b/src/main/java/net/geant/nmaas/externalservices/kubernetes/RemoteClusterManager.java index c18671f50..e8fc06fdd 100644 --- a/src/main/java/net/geant/nmaas/externalservices/kubernetes/RemoteClusterManager.java +++ b/src/main/java/net/geant/nmaas/externalservices/kubernetes/RemoteClusterManager.java @@ -29,7 +29,6 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.nio.file.Files; -import java.nio.file.NoSuchFileException; import java.nio.file.Path; import java.nio.file.Paths; import java.nio.file.StandardCopyOption; @@ -296,7 +295,7 @@ public class RemoteClusterManager { attr.put("clusterCodename", kCluster.getCodename()); attr.put("clusterName", kCluster.getName()); MailAttributes mailAttributes = MailAttributes.builder() - .mailType(MailType.CLUSTER_UNAVAILABLE) + .mailType(MailType.REMOTE_CLUSTER_UNAVAILABLE) .otherAttributes(attr) .addressees(Collections.singletonList(recipient)) .build(); diff --git a/src/main/java/net/geant/nmaas/notifications/templates/MailType.java b/src/main/java/net/geant/nmaas/notifications/templates/MailType.java index afa0a6167..1a2fb5119 100644 --- a/src/main/java/net/geant/nmaas/notifications/templates/MailType.java +++ b/src/main/java/net/geant/nmaas/notifications/templates/MailType.java @@ -26,5 +26,5 @@ public enum MailType { NEW_BULK_SSO_LOGIN, NEW_BULK_LOGIN, VLAB_REQUEST, - CLUSTER_UNAVAILABLE + REMOTE_CLUSTER_UNAVAILABLE } diff --git a/src/main/resources/db/migration/common/V1.8.0_20250515_1230__AddContactEmailToCluster.sql b/src/main/resources/db/migration/common/V1.8.0_20250515_1230__AddContactEmailToCluster.sql new file mode 100644 index 000000000..ad0284c06 --- /dev/null +++ b/src/main/resources/db/migration/common/V1.8.0_20250515_1230__AddContactEmailToCluster.sql @@ -0,0 +1 @@ +alter table k_cluster add COLUMN contact_email varchar(255) not null; \ No newline at end of file diff --git a/src/test/shell/data/mails/clusterUnavailable.json b/src/test/shell/data/mails/clusterUnavailable.json index 7930a84d8..6b95494ca 100644 --- a/src/test/shell/data/mails/clusterUnavailable.json +++ b/src/test/shell/data/mails/clusterUnavailable.json @@ -1,5 +1,5 @@ { - "mailType": "CLUSTER_UNAVAILABLE", + "mailType": "REMOTE_CLUSTER_UNAVAILABLE", "globalInformation": { "LOGO_ALT": "GÉANT logo", "PORTAL_LOGO_ALT": "nmaas logo", -- GitLab From 55113bae9b959b885487e22f1696b2d7e08817de Mon Sep 17 00:00:00 2001 From: kbeyro <121854496+kbeyro@users.noreply.github.com> Date: Fri, 16 May 2025 14:08:29 +0200 Subject: [PATCH 05/10] add state since --- src/test/shell/data/i18n/de.json | 3 ++- src/test/shell/data/i18n/en.json | 3 ++- src/test/shell/data/i18n/fr.json | 3 ++- src/test/shell/data/i18n/pl.json | 3 ++- 4 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/test/shell/data/i18n/de.json b/src/test/shell/data/i18n/de.json index d676fc09c..4d8597ce9 100644 --- a/src/test/shell/data/i18n/de.json +++ b/src/test/shell/data/i18n/de.json @@ -191,7 +191,8 @@ "UP" : "Up", "DOWN" : "Down", "UNKNOWN" : "Unknown", - "DETAILS" : "Details" + "DETAILS" : "Details", + "STATE_SINCE" : "State last change" }, "GITLAB": { "TITLE": "GitLab Konfiguration", diff --git a/src/test/shell/data/i18n/en.json b/src/test/shell/data/i18n/en.json index 1b9ec6abe..e2aea66b1 100644 --- a/src/test/shell/data/i18n/en.json +++ b/src/test/shell/data/i18n/en.json @@ -192,7 +192,8 @@ "UP" : "Up", "DOWN" : "Down", "UNKNOWN" : "Unknown", - "DETAILS" : "Details" + "DETAILS" : "Details", + "STATE_SINCE" : "State last change" }, "GITLAB": { "TITLE": "GitLab configuration", diff --git a/src/test/shell/data/i18n/fr.json b/src/test/shell/data/i18n/fr.json index 888c42e46..5051d9703 100644 --- a/src/test/shell/data/i18n/fr.json +++ b/src/test/shell/data/i18n/fr.json @@ -193,7 +193,8 @@ "UP" : "Up", "DOWN" : "Down", "UNKNOWN" : "Unknown", - "DETAILS" : "Details" + "DETAILS" : "Details", + "STATE_SINCE" : "State last change" }, "GITLAB": { "TITLE": "Configuration de GitLab", diff --git a/src/test/shell/data/i18n/pl.json b/src/test/shell/data/i18n/pl.json index c092433f5..f3e91fadc 100644 --- a/src/test/shell/data/i18n/pl.json +++ b/src/test/shell/data/i18n/pl.json @@ -192,7 +192,8 @@ "UP" : "Aktywny", "DOWN" : "Nieaktywny", "UNKNOWN" : "Nieznany", - "DETAILS" : "Detale" + "DETAILS" : "Detale", + "STATE_SINCE" : "Stan od" }, "GITLAB": { "TITLE": "Konfiguracja GitLab", -- GitLab From b852d3d704091dfefa3999dc179302ed3fe3fcd4 Mon Sep 17 00:00:00 2001 From: kbeyro <121854496+kbeyro@users.noreply.github.com> Date: Fri, 16 May 2025 14:11:53 +0200 Subject: [PATCH 06/10] add REMOTE_CLUSTER welcome message --- .../kubernetes/RemoteClusterManager.java | 11 ++-- .../notifications/templates/MailType.java | 3 +- .../shell/data/mails/clusterSupportEmail.json | 55 +++++++++++++++++++ .../shell/data/mails/clusterUnavailable.json | 8 +-- 4 files changed, 68 insertions(+), 9 deletions(-) create mode 100644 src/test/shell/data/mails/clusterSupportEmail.json diff --git a/src/main/java/net/geant/nmaas/externalservices/kubernetes/RemoteClusterManager.java b/src/main/java/net/geant/nmaas/externalservices/kubernetes/RemoteClusterManager.java index e8fc06fdd..f4e74e07c 100644 --- a/src/main/java/net/geant/nmaas/externalservices/kubernetes/RemoteClusterManager.java +++ b/src/main/java/net/geant/nmaas/externalservices/kubernetes/RemoteClusterManager.java @@ -107,6 +107,7 @@ public class RemoteClusterManager { KCluster cluster = this.clusterRepository.save(entity); log.debug("Cluster saved: {}", cluster.toString()); + sendMail(cluster, MailType.REMOTE_CLUSTER_WELCOME_SUPPORT); return toView(cluster); } @@ -265,12 +266,11 @@ public class RemoteClusterManager { log.error("Can not connect to cluster {}", cluster.getCodename()); log.error(e.getMessage()); updateStateIfNeeded(cluster, KClusterState.DOWN); -// sendMail(cluster); + } catch (RuntimeException ex) { log.error("Runtime error while checking health of cluster {}", ex.getMessage()); updateStateIfNeeded(cluster, KClusterState.UNKNOWN); -// sendMail(cluster); } catch (Exception ex) { log.error("Caught unexpected exception: {}", ex.getMessage(), ex); @@ -285,17 +285,20 @@ public class RemoteClusterManager { if (!cluster.getState().equals(newState)) { cluster.setState(newState); cluster.setCurrentStateSince(OffsetDateTime.now()); + if(cluster.getState().equals(KClusterState.DOWN) || cluster.getState().equals(KClusterState.UNKNOWN)) { + sendMail(cluster, MailType.REMOTE_CLUSTER_UNAVAILABLE); + }; } } - private void sendMail(KCluster kCluster) { + private void sendMail(KCluster kCluster, MailType mailType) { UserView recipient = UserView.builder().email(kCluster.getContactEmail()).username(kCluster.getContactEmail()).selectedLanguage("EN").build(); Map<String, Object> attr = new HashMap<>(); attr.put("clusterId", kCluster.getId()); attr.put("clusterCodename", kCluster.getCodename()); attr.put("clusterName", kCluster.getName()); MailAttributes mailAttributes = MailAttributes.builder() - .mailType(MailType.REMOTE_CLUSTER_UNAVAILABLE) + .mailType(mailType) .otherAttributes(attr) .addressees(Collections.singletonList(recipient)) .build(); diff --git a/src/main/java/net/geant/nmaas/notifications/templates/MailType.java b/src/main/java/net/geant/nmaas/notifications/templates/MailType.java index 1a2fb5119..feec41f29 100644 --- a/src/main/java/net/geant/nmaas/notifications/templates/MailType.java +++ b/src/main/java/net/geant/nmaas/notifications/templates/MailType.java @@ -26,5 +26,6 @@ public enum MailType { NEW_BULK_SSO_LOGIN, NEW_BULK_LOGIN, VLAB_REQUEST, - REMOTE_CLUSTER_UNAVAILABLE + REMOTE_CLUSTER_UNAVAILABLE, + REMOTE_CLUSTER_WELCOME_SUPPORT } diff --git a/src/test/shell/data/mails/clusterSupportEmail.json b/src/test/shell/data/mails/clusterSupportEmail.json new file mode 100644 index 000000000..264b95477 --- /dev/null +++ b/src/test/shell/data/mails/clusterSupportEmail.json @@ -0,0 +1,55 @@ +{ + "mailType": "REMOTE_CLUSTER_WELCOME_SUPPORT", + "globalInformation": { + "LOGO_ALT": "GÉANT logo", + "PORTAL_LOGO_ALT": "nmaas logo", + "SENDER_INFO": "Ⓒ GÉANT Association Hoekenrode 3 1102 BR - Amsterdam – Zuidoost- The Netherlands" + }, + "templates": [ + { + "language": "en", + "subject": "nmaas: Cluster support", + "template": { + "HEADER": "Dear ${username}", + "CONTENT": "<p>Your email address is added to support remote cluster <b>${clusterCodename}</b>. </p> <p>If you do not agree or do not proceed this action pleace contact administration for futher information.</p>", + "SENDER": "Best regards,<br />nmaas Team", + "NOREPLY": "This is an automatically generated message, please do not reply.", + "SENDER_POLICY": "" + } + }, + { + "language": "fr", + "subject": "nmaas: Cluster is unavailable", + "template": { + "HEADER": "Dear ${username}", + "CONTENT": "<p>Your email address is added to support remote cluster <b>${clusterCodename}</b>. </p> <p>If you do not agree or do not proceed this action pleace contact administration for futher information.</p>", + + "SENDER": "Best regards,<br />nmaas Team", + "NOREPLY": "This is an automatically generated message, please do not reply.", + "SENDER_POLICY": "" + } + }, + { + "language": "de", + "subject": "nmaas: Cluster is unavailable", + "template": { + "HEADER": "Dear ${username}", + "CONTENT": "<p>Your email address is added to support remote cluster <b>${clusterCodename}</b>. </p> <p>If you do not agree or do not proceed this action pleace contact administration for futher information.</p>", + "SENDER": "Best regards,<br />nmaas Team", + "NOREPLY": "This is an automatically generated message, please do not reply.", + "SENDER_POLICY": "" + } + }, + { + "language": "pl", + "subject": "nmaas: Cluster jest niedostępny", + "template": { + "HEADER": "Drogi ${username}", + "CONTENT": "<p>Your email address is added to support remote cluster <b>${clusterCodename}</b>. </p> <p>If you do not agree or do not proceed this action pleace contact administration for futher information.</p>", + "SENDER": "Z pozdrowieniami,<br />Zespół nmaas", + "NOREPLY": "Ta wiadomość została wygenerowana automatycznie.", + "SENDER_POLICY": "" + } + } + ] +} \ No newline at end of file diff --git a/src/test/shell/data/mails/clusterUnavailable.json b/src/test/shell/data/mails/clusterUnavailable.json index 6b95494ca..02e80b5e2 100644 --- a/src/test/shell/data/mails/clusterUnavailable.json +++ b/src/test/shell/data/mails/clusterUnavailable.json @@ -11,7 +11,7 @@ "subject": "nmaas: Cluster is unavailable", "template": { "HEADER": "Dear ${username}", - "CONTENT": "<p> Cluster <b>{clusterCodename}</b> is unavailable.</p> <p><b> Cluster ID:<b> {clusterId}</p> <p><b> Cluster CodeName:<b> {clusterCodename}</p> <p>Please contact the administration for further information.</p>", + "CONTENT": "<p> Cluster <b>${clusterCodename}</b> is unavailable.</p> <p><b> Cluster ID:<b> ${clusterId}</p> <p><b> Cluster CodeName:<b> ${clusterCodename}</p> <p>Please contact the administration for further information.</p>", "SENDER": "Best regards,<br />nmaas Team", "NOREPLY": "This is an automatically generated message, please do not reply.", "SENDER_POLICY": "" @@ -22,7 +22,7 @@ "subject": "nmaas: Cluster is unavailable", "template": { "HEADER": "Dear ${username}", - "CONTENT": "<p> Cluster <b>{clusterCodename}</b> is unavailable.</p> <p><b> Cluster ID:<b> {clusterId}</p> <p><b> Cluster CodeName:<b> {clusterCodename}</p> <p>Please contact the administration for further information.</p>", + "CONTENT": "<p> Cluster <b>${clusterCodename}</b> is unavailable.</p> <p><b> Cluster ID:<b> ${clusterId}</p> <p><b> Cluster CodeName:<b> ${clusterCodename}</p> <p>Please contact the administration for further information.</p>", "SENDER": "Best regards,<br />nmaas Team", "NOREPLY": "This is an automatically generated message, please do not reply.", @@ -34,7 +34,7 @@ "subject": "nmaas: Cluster is unavailable", "template": { "HEADER": "Dear ${username}", - "CONTENT": "<p> Cluster <b>{clusterCodename}</b> is unavailable.</p> <p><b> Cluster ID:<b> {clusterId}</p> <p><b> Cluster CodeName:<b> {clusterCodename}</p> <p>Please contact the administration for further information.</p>", + "CONTENT": "<p> Cluster <b>${clusterCodename}</b> is unavailable.</p> <p><b> Cluster ID:<b> ${clusterId}</p> <p><b> Cluster CodeName:<b> ${clusterCodename}</p> <p>Please contact the administration for further information.</p>", "SENDER": "Best regards,<br />nmaas Team", "NOREPLY": "This is an automatically generated message, please do not reply.", "SENDER_POLICY": "" @@ -45,7 +45,7 @@ "subject": "nmaas: Cluster jest niedostępny", "template": { "HEADER": "Drogi ${username}", - "CONTENT": "<p> Cluster <b>{clusterCodename}</b> is unavailable.</p> <p><b> Cluster ID:<b> {clusterId}</p> <p><b> Cluster CodeName:<b> {clusterCodename}</p> <p>Please contact the administration for further information.</p>", + "CONTENT": "<p> Cluster <b>${clusterCodename}</b> is unavailable.</p> <p><b> Cluster ID:<b> ${clusterId}</p> <p><b> Cluster CodeName:<b> ${clusterCodename}</p> <p>Please contact the administration for further information.</p>", "SENDER": "Z pozdrowieniami,<br />Zespół nmaas", "NOREPLY": "Ta wiadomość została wygenerowana automatycznie.", "SENDER_POLICY": "" -- GitLab From a1a6b8504c2f2993e5ec6c8cf5d144255f301999 Mon Sep 17 00:00:00 2001 From: kbeyro <121854496+kbeyro@users.noreply.github.com> Date: Fri, 16 May 2025 14:16:23 +0200 Subject: [PATCH 07/10] update logs for testings --- .../externalservices/kubernetes/ClusterMonitoringJob.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/net/geant/nmaas/externalservices/kubernetes/ClusterMonitoringJob.java b/src/main/java/net/geant/nmaas/externalservices/kubernetes/ClusterMonitoringJob.java index 784fa5100..2423eb67c 100644 --- a/src/main/java/net/geant/nmaas/externalservices/kubernetes/ClusterMonitoringJob.java +++ b/src/main/java/net/geant/nmaas/externalservices/kubernetes/ClusterMonitoringJob.java @@ -16,9 +16,9 @@ public class ClusterMonitoringJob implements Job { @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { - log.error("Triggering cluster health check..."); + log.info("Triggering cluster health check..."); remoteClusterManager.restoreFileIfMissing(); - log.warn("File checked, everything looks fine. Next time: Update clusters state."); + log.info("File checked, everything looks fine. Next stage: Update clusters state."); remoteClusterManager.updateAllClusterState(); } } -- GitLab From 7d1b2d3c1cf4d58422a039c6743e955c12e3491e Mon Sep 17 00:00:00 2001 From: kbeyro <121854496+kbeyro@users.noreply.github.com> Date: Fri, 16 May 2025 14:28:21 +0200 Subject: [PATCH 08/10] fix typo --- src/test/shell/data/mails/clusterSupportEmail.json | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/test/shell/data/mails/clusterSupportEmail.json b/src/test/shell/data/mails/clusterSupportEmail.json index 264b95477..192f7daa3 100644 --- a/src/test/shell/data/mails/clusterSupportEmail.json +++ b/src/test/shell/data/mails/clusterSupportEmail.json @@ -11,7 +11,7 @@ "subject": "nmaas: Cluster support", "template": { "HEADER": "Dear ${username}", - "CONTENT": "<p>Your email address is added to support remote cluster <b>${clusterCodename}</b>. </p> <p>If you do not agree or do not proceed this action pleace contact administration for futher information.</p>", + "CONTENT": "<p>Your email address is added to support remote cluster <b>${clusterCodename}</b>. </p> <p>If you do not agree or do not proceed this action please contact administration for further information.</p>", "SENDER": "Best regards,<br />nmaas Team", "NOREPLY": "This is an automatically generated message, please do not reply.", "SENDER_POLICY": "" @@ -22,7 +22,7 @@ "subject": "nmaas: Cluster is unavailable", "template": { "HEADER": "Dear ${username}", - "CONTENT": "<p>Your email address is added to support remote cluster <b>${clusterCodename}</b>. </p> <p>If you do not agree or do not proceed this action pleace contact administration for futher information.</p>", + "CONTENT": "<p>Your email address is added to support remote cluster <b>${clusterCodename}</b>. </p> <p>If you do not agree or do not proceed this action please contact administration for further information.</p>", "SENDER": "Best regards,<br />nmaas Team", "NOREPLY": "This is an automatically generated message, please do not reply.", @@ -34,7 +34,7 @@ "subject": "nmaas: Cluster is unavailable", "template": { "HEADER": "Dear ${username}", - "CONTENT": "<p>Your email address is added to support remote cluster <b>${clusterCodename}</b>. </p> <p>If you do not agree or do not proceed this action pleace contact administration for futher information.</p>", + "CONTENT": "<p>Your email address is added to support remote cluster <b>${clusterCodename}</b>. </p> <p>If you do not agree or do not proceed this action please contact administration for further information.</p>", "SENDER": "Best regards,<br />nmaas Team", "NOREPLY": "This is an automatically generated message, please do not reply.", "SENDER_POLICY": "" @@ -45,7 +45,7 @@ "subject": "nmaas: Cluster jest niedostępny", "template": { "HEADER": "Drogi ${username}", - "CONTENT": "<p>Your email address is added to support remote cluster <b>${clusterCodename}</b>. </p> <p>If you do not agree or do not proceed this action pleace contact administration for futher information.</p>", + "CONTENT": "<p>Your email address is added to support remote cluster <b>${clusterCodename}</b>. </p> <p>If you do not agree or do not proceed this action please contact administration for further information.</p>", "SENDER": "Z pozdrowieniami,<br />Zespół nmaas", "NOREPLY": "Ta wiadomość została wygenerowana automatycznie.", "SENDER_POLICY": "" -- GitLab From 107911f392ca0f6b721130a15209c7eec577ffd2 Mon Sep 17 00:00:00 2001 From: kbeyro <121854496+kbeyro@users.noreply.github.com> Date: Fri, 16 May 2025 14:31:30 +0200 Subject: [PATCH 09/10] update style --- src/test/shell/data/mails/clusterUnavailable.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/shell/data/mails/clusterUnavailable.json b/src/test/shell/data/mails/clusterUnavailable.json index 02e80b5e2..b126a6d6f 100644 --- a/src/test/shell/data/mails/clusterUnavailable.json +++ b/src/test/shell/data/mails/clusterUnavailable.json @@ -45,7 +45,7 @@ "subject": "nmaas: Cluster jest niedostępny", "template": { "HEADER": "Drogi ${username}", - "CONTENT": "<p> Cluster <b>${clusterCodename}</b> is unavailable.</p> <p><b> Cluster ID:<b> ${clusterId}</p> <p><b> Cluster CodeName:<b> ${clusterCodename}</p> <p>Please contact the administration for further information.</p>", + "CONTENT": "<p> Cluster <b>${clusterCodename}</b> is unavailable.</p> <p><b> Cluster ID:</b> ${clusterId}</p> <p><b> Cluster CodeName:</b> ${clusterCodename}</p> <p> <i>Please contact the administration for further information. </i></p>", "SENDER": "Z pozdrowieniami,<br />Zespół nmaas", "NOREPLY": "Ta wiadomość została wygenerowana automatycznie.", "SENDER_POLICY": "" -- GitLab From b1e6adbefec71d3e6a5cb11c7be49c2d475e6659 Mon Sep 17 00:00:00 2001 From: kbeyro <121854496+kbeyro@users.noreply.github.com> Date: Fri, 16 May 2025 14:39:25 +0200 Subject: [PATCH 10/10] filter out 0 values from popular app map --- .../geant/nmaas/portal/service/impl/DashboardServiceImpl.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/net/geant/nmaas/portal/service/impl/DashboardServiceImpl.java b/src/main/java/net/geant/nmaas/portal/service/impl/DashboardServiceImpl.java index 0862eface..1569cfee2 100644 --- a/src/main/java/net/geant/nmaas/portal/service/impl/DashboardServiceImpl.java +++ b/src/main/java/net/geant/nmaas/portal/service/impl/DashboardServiceImpl.java @@ -64,6 +64,9 @@ public class DashboardServiceImpl implements DashboardService { applicationDeploymentCountPerName.put(name, appInstanceRepo.countByName(name)); }); + //filter not deployed application + applicationDeploymentCountPerName.entrySet().removeIf(app -> app.getValue() == 0); + return DashboardView.builder() .domainsCount(domainRepository.count()) -- GitLab