diff --git a/NOTICE b/COPYRIGHT similarity index 77% rename from NOTICE rename to COPYRIGHT index f5da1a0ba26c0b2b4388922ebff7a5b843058d00..ac5e5f52a3f182430ef8f7aab108813d1362e9fc 100644 --- a/NOTICE +++ b/COPYRIGHT @@ -1,11 +1,11 @@ Copyright 2025 GÉANT Association Contributions to this work were made on behalf of the GÉANT project, a project that has received funding -from the European Union’s Horizon 2020 research and innovation programme under Grant Agreement No. 731122 (GN4-2) -and Grant Agreement No. 856726 (GN4-3). +from the European Union’s Horizon 2020 research and innovation programme under Grant Agreement No. 731122 (GN4-2), +Grant Agreement No. 856726 (GN4-3) and Grant Agreement No. 101100680 (GN5-1). -On behalf of GÉANT project, GÉANT Association is the sole owner of the copyright in all material -which was developed by the members of the GÉANT project: +On behalf of GÉANT project, GÉANT Association is the sole owner of the copyright in all material which was developed +by the members of the GÉANT project: - Poznan Supercomputing and Networking Center (PSNC), Poland; - RENATER, France; - University of Belgrade (UoB), Serbia; diff --git a/build.gradle b/build.gradle index b13e85b54780fea8729dd1ecf7dfeba7d0ac0d89..34f0707183e6c6741a090eca85db187bd1d2d0cc 100644 --- a/build.gradle +++ b/build.gradle @@ -2,7 +2,7 @@ plugins { id 'java' id 'idea' id 'jacoco' - id 'org.springframework.boot' version '3.4.5' + id 'org.springframework.boot' version '3.4.6' id 'io.spring.dependency-management' version '1.1.7' id 'com.gorylenko.gradle-git-properties' version '2.5.0' id 'org.sonarqube' version '6.2.0.5505' @@ -88,7 +88,6 @@ dependencies { implementation('org.springframework.boot:spring-boot-starter-mail') implementation('org.springframework.boot:spring-boot-starter-data-jpa') implementation('org.springframework.boot:spring-boot-starter-actuator') - implementation('org.springframework.boot:spring-boot-devtools') implementation('org.springframework.boot:spring-boot-starter-quartz') implementation('org.springframework.boot:spring-boot-starter-validation') implementation 'org.springframework.boot:spring-boot-starter-cache' @@ -112,7 +111,7 @@ dependencies { runtimeOnly('org.postgresql:postgresql') // Database audit trail - implementation 'org.hibernate.orm:hibernate-envers:6.6.15.Final' + implementation 'org.hibernate.orm:hibernate-envers:6.6.16.Final' // Database migrations implementation('org.flywaydb:flyway-core:11.8.2') @@ -126,7 +125,7 @@ dependencies { implementation('io.micrometer:micrometer-registry-prometheus:1.15.0') // Kubernetes API client - implementation('io.fabric8:kubernetes-client:7.3.0') + implementation('io.fabric8:kubernetes-client:7.3.1') // GitLab API client implementation 'org.gitlab4j:gitlab4j-api:6.0.0-rc.10' // version 6 for jakarta @@ -141,7 +140,7 @@ dependencies { implementation('com.opencsv:opencsv:5.11') // Tests - testImplementation('org.mockito:mockito-core:5.17.0') + testImplementation('org.mockito:mockito-core:5.18.0') testImplementation('org.springframework.boot:spring-boot-starter-test') integrationTestImplementation('org.springframework.boot:spring-boot-starter-test') diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ca025c83a7cc5e4f5eb7bc7a5ff8cae62df35ffb..002b867c48b3289ed9c179a968f56bef2859d3da 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,6 +1,6 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-8.14-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.1-bin.zip networkTimeout=10000 validateDistributionUrl=true zipStoreBase=GRADLE_USER_HOME diff --git a/src/main/java/net/geant/nmaas/externalservices/kubernetes/ClusterConfigView.java b/src/main/java/net/geant/nmaas/externalservices/kubernetes/ClusterConfigView.java index 642850b3a2950cbbec81aff53e1ffefe02e20c91..0b4b8e6e0af4984f8d0c2edf2c7f79a24c46ffbb 100644 --- a/src/main/java/net/geant/nmaas/externalservices/kubernetes/ClusterConfigView.java +++ b/src/main/java/net/geant/nmaas/externalservices/kubernetes/ClusterConfigView.java @@ -39,6 +39,7 @@ public class ClusterConfigView { .append("}"); return sb.toString(); } + @Getter @Setter @AllArgsConstructor @@ -126,13 +127,13 @@ public class ClusterConfigView { public static class UserToken { private String token; @JsonProperty("client-certificate-data") - private String client_certificate_data; + private String clientCertificateData; @JsonProperty("client-key-data") - private String client_key_data; + private String clientKeyData; @Override public String toString() { - return "{ token: " + token + ", client certificate data: " + client_certificate_data + ", client key data: " +client_key_data + " }"; + return "{ token: " + token + ", client certificate data: " + clientCertificateData + ", client key data: " + clientKeyData + " }"; } } } 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 2423eb67c11c24480e5e0cc06320cbde16bfd02c..2815b5fc52164840cd662f1ee67eec1c3c8b292c 100644 --- a/src/main/java/net/geant/nmaas/externalservices/kubernetes/ClusterMonitoringJob.java +++ b/src/main/java/net/geant/nmaas/externalservices/kubernetes/ClusterMonitoringJob.java @@ -2,23 +2,22 @@ package net.geant.nmaas.externalservices.kubernetes; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.quartz.Job; import org.quartz.JobExecutionContext; import org.quartz.JobExecutionException; import org.springframework.stereotype.Service; -import org.quartz.Job; -@Slf4j -@RequiredArgsConstructor @Service +@RequiredArgsConstructor +@Slf4j public class ClusterMonitoringJob implements Job { - private final RemoteClusterManager remoteClusterManager; + private final ClusterMonitoringService clusterMonitoringService; @Override public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException { log.info("Triggering cluster health check..."); - remoteClusterManager.restoreFileIfMissing(); - log.info("File checked, everything looks fine. Next stage: Update clusters state."); - remoteClusterManager.updateAllClusterState(); + clusterMonitoringService.updateAllClusterState(); } + } diff --git a/src/main/java/net/geant/nmaas/externalservices/kubernetes/ClusterMonitoringService.java b/src/main/java/net/geant/nmaas/externalservices/kubernetes/ClusterMonitoringService.java new file mode 100644 index 0000000000000000000000000000000000000000..ac297e9c2c296da699f50087ee832005dc161015 --- /dev/null +++ b/src/main/java/net/geant/nmaas/externalservices/kubernetes/ClusterMonitoringService.java @@ -0,0 +1,7 @@ +package net.geant.nmaas.externalservices.kubernetes; + +public interface ClusterMonitoringService { + + void 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 217cd98881535adfbb879bd20146fa4f6ab0dcc6..b1442b410254c2819f5cac8591976031f0d8c657 100644 --- a/src/main/java/net/geant/nmaas/externalservices/kubernetes/RemoteClusterManager.java +++ b/src/main/java/net/geant/nmaas/externalservices/kubernetes/RemoteClusterManager.java @@ -38,8 +38,8 @@ 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.Collections; import java.util.List; import java.util.Map; import java.util.Objects; @@ -49,9 +49,7 @@ import java.util.stream.Collectors; @Service @Slf4j @RequiredArgsConstructor -public class RemoteClusterManager { - - private final static ModelMapper modelMapper = new ModelMapper(); +public class RemoteClusterManager implements ClusterMonitoringService { private final KClusterRepository clusterRepository; private final KubernetesClusterIngressManager kClusterIngressManager; @@ -59,7 +57,7 @@ public class RemoteClusterManager { private final DomainService domainService; private final ApplicationEventPublisher eventPublisher; private final UserService userService; - + private final ModelMapper modelMapper; public RemoteClusterView getClusterView(Long id) { Optional<KCluster> cluster = clusterRepository.findById(id); @@ -72,7 +70,7 @@ public class RemoteClusterManager { public List<RemoteClusterView> getAllClusterView() { List<KCluster> clusters = clusterRepository.findAll(); - return clusters.stream().map(RemoteClusterManager::toView).collect(Collectors.toList()); + return clusters.stream().map(this::toView).collect(Collectors.toList()); } public KCluster getCluster(Long id) { @@ -141,11 +139,11 @@ public class RemoteClusterManager { .state(KClusterState.UNKNOWN) .contactEmail(view.getContactEmail()) .currentStateSince(OffsetDateTime.now()) - .domains(view.getDomainNames().stream().map(d -> { + .domains(!view.getDomainNames().isEmpty() ? view.getDomainNames().stream().map(d -> { Optional<Domain> dom = domainService.findDomain(d); return dom.orElse(null); } - ).toList()) + ).toList() : Collections.emptyList()) .build(), file); @@ -153,9 +151,7 @@ public class RemoteClusterManager { log.error("More than 1 cluster provided, not implemented yet"); } - } catch (IOException e) { - throw new RuntimeException(e); - } catch (NoSuchAlgorithmException e) { + } catch (IOException | NoSuchAlgorithmException e) { throw new RuntimeException(e); } @@ -189,7 +185,6 @@ public class RemoteClusterManager { updated = clusterRepository.save(updated); //TODO : implement file update logic return toView(updated); - } } @@ -239,25 +234,27 @@ public class RemoteClusterManager { if (view.getCodename() == null) { throw new IllegalArgumentException("Codename of the cluster is null"); } - } - public static RemoteClusterView toView(KCluster KCluster) { + private RemoteClusterView toView(KCluster KCluster) { RemoteClusterView view = modelMapper.map(KCluster, RemoteClusterView.class); view.setDomainNames(KCluster.getDomains().stream().map(Domain::getName).toList()); return view; } + @Override public void updateAllClusterState() { + restoreFileIfMissing(); List<KCluster> kClusters = clusterRepository.findAll(); kClusters.forEach(cluster -> { Config config = null; try { config = Config.fromKubeconfig(Files.readString(Path.of(cluster.getPathConfigFile()))); - } catch (IOException e) { - log.error("IO error with accesing the file {}", e.getMessage()); + } catch (IOException e) { + log.error("IO error with accessing the file {}", e.getMessage()); updateStateIfNeeded(cluster, KClusterState.UNKNOWN); } + try { KubernetesClient client = new KubernetesClientBuilder().withConfig(config).build(); log.debug("Get kubernetes version , something works {}", client.getKubernetesVersion().getPlatform()); @@ -268,12 +265,9 @@ public class RemoteClusterManager { log.error("Can not connect to cluster {}", cluster.getCodename()); log.error(e.getMessage()); updateStateIfNeeded(cluster, KClusterState.DOWN); - - - } catch (RuntimeException ex) { + } catch (RuntimeException ex) { log.error("Runtime error while checking health of cluster {}", ex.getMessage()); updateStateIfNeeded(cluster, KClusterState.UNKNOWN); - } catch (Exception ex) { log.error("Caught unexpected exception: {}", ex.getMessage(), ex); } @@ -282,14 +276,13 @@ public class RemoteClusterManager { clusterRepository.saveAll(kClusters); } - private void updateStateIfNeeded(KCluster cluster, KClusterState newState) { if (!cluster.getState().equals(newState)) { cluster.setState(newState); cluster.setCurrentStateSince(OffsetDateTime.now()); - if(cluster.getState().equals(KClusterState.DOWN) || cluster.getState().equals(KClusterState.UNKNOWN)) { + if (cluster.getState().equals(KClusterState.DOWN) || cluster.getState().equals(KClusterState.UNKNOWN)) { sendMail(cluster, MailType.REMOTE_CLUSTER_UNAVAILABLE); - }; + } } } @@ -312,7 +305,6 @@ public class RemoteClusterManager { .build(); this.eventPublisher.publishEvent(new NotificationEvent(this, mailAttributes)); - } public void restoreFileIfMissing() { @@ -334,9 +326,9 @@ public class RemoteClusterManager { }); } - 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/monitor/MonitorConfig.java b/src/main/java/net/geant/nmaas/monitor/MonitorConfig.java deleted file mode 100644 index 47b82c3ccb68d1947e6c350d55cdb5859645b181..0000000000000000000000000000000000000000 --- a/src/main/java/net/geant/nmaas/monitor/MonitorConfig.java +++ /dev/null @@ -1,60 +0,0 @@ -package net.geant.nmaas.monitor; - -import lombok.extern.slf4j.Slf4j; -import net.geant.nmaas.monitor.model.MonitorEntryView; -import net.geant.nmaas.scheduling.ScheduleManager; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.transaction.annotation.Transactional; - -import java.util.Arrays; -import java.util.List; -import java.util.Optional; - -@Configuration -@Slf4j -public class MonitorConfig { - - @Bean - public InitializingBean insertDefaultMonitoringJobs() { - return new InitializingBean() { - - @Autowired - private List<MonitorService> monitorServices; - - @Autowired - private ScheduleManager scheduleManager; - - @Autowired - private MonitorManager monitorManager; - - @Override - @Transactional - public void afterPropertiesSet() { - Arrays.stream(ServiceType.values()) - .filter(serviceType -> !scheduleManager.jobExists(serviceType.toString())) // if job does not exist - .forEach(serviceType -> { - MonitorEntryView monitorEntry; - if (monitorManager.existsByServiceName(serviceType)) { // if entry exists - monitorEntry = monitorManager.getMonitorEntries(serviceType.toString()); // read it from database - } else { - monitorEntry = serviceType.getDefaultMonitorEntry(); // if entry does not exist - monitorManager.createMonitorEntry(monitorEntry); // create new default entry - } - Optional<MonitorService> service = monitorServices.stream() - .filter(s -> s.getServiceType().equals(serviceType)) - .filter(MonitorService::schedulable) - .findFirst(); - if (service.isPresent()) { - scheduleManager.createJob(service.get(), monitorEntry); - } else { - log.warn("Monitor service for {} not found or is not schedulable", serviceType); - } - }); - } - }; - } - -} diff --git a/src/main/java/net/geant/nmaas/monitor/MonitorService.java b/src/main/java/net/geant/nmaas/monitor/MonitorService.java index 264117c5728ac578a4b1072e3ff8279f3cc23214..cbb34a1c2debe2ebdaf431f9983ff1c3d9579485 100644 --- a/src/main/java/net/geant/nmaas/monitor/MonitorService.java +++ b/src/main/java/net/geant/nmaas/monitor/MonitorService.java @@ -23,7 +23,7 @@ public abstract class MonitorService implements Job { this.monitorManager.updateMonitorEntry(new Date(), this.getServiceType(), status); } - protected boolean schedulable() { + public boolean schedulable() { return true; } diff --git a/src/main/java/net/geant/nmaas/nmservice/deployment/NmServiceRepositoryManager.java b/src/main/java/net/geant/nmaas/nmservice/deployment/NmServiceRepositoryManager.java index ff222aefc7a2e484ee89b7eaaf46b7e991a82eb8..4c9d0b2e5c26e43375e778698880905959fdcd5a 100644 --- a/src/main/java/net/geant/nmaas/nmservice/deployment/NmServiceRepositoryManager.java +++ b/src/main/java/net/geant/nmaas/nmservice/deployment/NmServiceRepositoryManager.java @@ -9,11 +9,11 @@ import net.geant.nmaas.nmservice.deployment.entities.NmServiceInfo; import net.geant.nmaas.nmservice.deployment.repository.NmServiceInfoRepository; import net.geant.nmaas.orchestration.Identifier; import net.geant.nmaas.orchestration.exceptions.InvalidDeploymentIdException; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.event.EventListener; import org.springframework.transaction.annotation.Propagation; import org.springframework.transaction.annotation.Transactional; +import java.time.LocalDateTime; import java.util.List; import java.util.Map; import java.util.Optional; @@ -21,12 +21,14 @@ import java.util.Optional; @Slf4j public abstract class NmServiceRepositoryManager<T extends NmServiceInfo> { - @Autowired protected GitLabProjectRepository gitLabProjectRepository; - - @Autowired protected NmServiceInfoRepository<T> repository; + public NmServiceRepositoryManager(GitLabProjectRepository gitLabProjectRepository, NmServiceInfoRepository<T> repository) { + this.gitLabProjectRepository = gitLabProjectRepository; + this.repository = repository; + } + @Transactional(propagation = Propagation.REQUIRES_NEW) public void storeService(T serviceInfo) { if (repository.findByDeploymentId(serviceInfo.getDeploymentId()).isEmpty()) { @@ -69,8 +71,7 @@ public abstract class NmServiceRepositoryManager<T extends NmServiceInfo> { try { updateServiceState(event.getDeploymentId(), event.getState()); } catch (Exception ex) { - long timestamp = System.currentTimeMillis(); - log.error("Error reported at {}", timestamp, ex); + log.error("Error reported at {}", LocalDateTime.now(), ex); } } diff --git a/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/KubernetesRepositoryManager.java b/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/KubernetesRepositoryManager.java index 6d5763f0776311c862ca69451931822f18f46788..a6fd0c4771d34bbb36b8c79908aa9a4cb02098c3 100644 --- a/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/KubernetesRepositoryManager.java +++ b/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/KubernetesRepositoryManager.java @@ -1,11 +1,13 @@ package net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes; +import net.geant.nmaas.nmservice.configuration.repositories.GitLabProjectRepository; import net.geant.nmaas.nmservice.deployment.NmServiceRepositoryManager; import net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes.entities.KubernetesNmServiceInfo; import net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes.entities.ServiceAccessMethod; import net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes.entities.ServiceStorageVolume; import net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes.repositories.ServiceAccessMethodRepository; import net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes.repositories.ServiceStorageVolumeRepository; +import net.geant.nmaas.nmservice.deployment.repository.NmServiceInfoRepository; import net.geant.nmaas.orchestration.Identifier; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Scope; @@ -25,11 +27,15 @@ import static net.geant.nmaas.nmservice.deployment.containerorchestrators.kubern @Scope(proxyMode = ScopedProxyMode.TARGET_CLASS) public class KubernetesRepositoryManager extends NmServiceRepositoryManager<KubernetesNmServiceInfo> { - @Autowired - private ServiceStorageVolumeRepository storageVolumeRepository; + private final ServiceStorageVolumeRepository storageVolumeRepository; + private final ServiceAccessMethodRepository accessMethodRepository; @Autowired - private ServiceAccessMethodRepository accessMethodRepository; + public KubernetesRepositoryManager(GitLabProjectRepository gitLabProjectRepository, NmServiceInfoRepository<KubernetesNmServiceInfo> repository, ServiceStorageVolumeRepository storageVolumeRepository, ServiceAccessMethodRepository accessMethodRepository) { + super(gitLabProjectRepository, repository); + this.storageVolumeRepository = storageVolumeRepository; + this.accessMethodRepository = accessMethodRepository; + } @Override @Transactional(propagation = Propagation.REQUIRES_NEW) diff --git a/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/components/helm/HelmCommandExecutor.java b/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/components/helm/HelmCommandExecutor.java index 09b4dde389d79c1c394df8bd662f855127a36798..01a3fd9b4307cc5eea008c719b76d0442d1d6b4c 100644 --- a/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/components/helm/HelmCommandExecutor.java +++ b/src/main/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/components/helm/HelmCommandExecutor.java @@ -1,5 +1,6 @@ package net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes.components.helm; +import lombok.NoArgsConstructor; import lombok.Setter; import net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes.components.helm.commands.HelmDeleteCommand; import net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes.components.helm.commands.HelmInstallCommand; @@ -23,23 +24,28 @@ import java.util.Map; import static net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes.components.helm.HelmCommand.HELM_VERSION_2; +@NoArgsConstructor @Component public class HelmCommandExecutor { - @Autowired private CommandExecutor commandExecutor; - @Setter - @Value("${helm.version:v3}") private String helmVersion; - @Setter - @Value("${helm.repositoryName}") private String helmRepositoryName; - - @Value("${helm.enableTls:false}") private Boolean enableTls; + @Autowired + public HelmCommandExecutor(CommandExecutor commandExecutor, + @Value("${helm.version:v3}") String helmVersion, + @Value("${helm.repositoryName}") String helmRepositoryName, + @Value("${helm.enableTls:false}") Boolean enableTls) { + this.commandExecutor = commandExecutor; + this.helmVersion = helmVersion; + this.helmRepositoryName = helmRepositoryName; + this.enableTls = enableTls; + } + void executeHelmInstallCommand(String namespace, String releaseName, KubernetesTemplate template, Map<String, String> arguments) { executeInstall(namespace, releaseName, template, arguments); } diff --git a/src/main/java/net/geant/nmaas/orchestration/jobs/DomainGroupJob.java b/src/main/java/net/geant/nmaas/orchestration/jobs/DomainGroupJob.java new file mode 100644 index 0000000000000000000000000000000000000000..7fe42451ebba65a1061e0981e789e9c0d823826a --- /dev/null +++ b/src/main/java/net/geant/nmaas/orchestration/jobs/DomainGroupJob.java @@ -0,0 +1,55 @@ +package net.geant.nmaas.orchestration.jobs; + +import lombok.extern.slf4j.Slf4j; +import net.geant.nmaas.orchestration.exceptions.WebServiceCommunicationException; +import net.geant.nmaas.portal.api.domain.DomainGroupView; +import net.geant.nmaas.portal.api.domain.DomainGroupWebhookDto; +import net.geant.nmaas.portal.api.domain.WebhookEventDto; +import net.geant.nmaas.portal.api.exception.MissingElementException; +import net.geant.nmaas.portal.persistent.entity.WebhookEventType; +import net.geant.nmaas.portal.service.WebhookEventService; +import org.modelmapper.ModelMapper; +import org.quartz.JobDataMap; +import org.quartz.JobExecutionContext; +import org.quartz.JobExecutionException; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; +import org.springframework.web.client.RestClient; + +import java.security.GeneralSecurityException; + +@Slf4j +@Component +public class DomainGroupJob extends WebhookJob { + + @Autowired + public DomainGroupJob(RestClient restClient, WebhookEventService webhookEventService, ModelMapper modelMapper) { + super(restClient, webhookEventService, modelMapper); + } + + @Override + public void execute(JobExecutionContext context) throws JobExecutionException { + JobDataMap dataMap = context.getJobDetail().getJobDataMap(); + Long webhookId = dataMap.getLong("webhookId"); + String action = dataMap.getString("action"); + DomainGroupView domainGroup = (DomainGroupView) dataMap.get("domainGroup"); + + try { + WebhookEventDto webhook = webhookEventService.getById(webhookId); + if (!WebhookEventType.DOMAIN_GROUP_CHANGE.equals(webhook.getEventType())) { + log.warn("Webhook's event type with id {} has been updated. DomainGroupJob is abandoned", webhookId); + return; + } + DomainGroupWebhookDto view = new DomainGroupWebhookDto(domainGroup, action); + callWebhook(webhook, view); + } catch (GeneralSecurityException e) { + log.error("Failed to decrypt webhook with id {}", webhookId); + throw new JobExecutionException("Failed webhook decryption"); + } catch (MissingElementException e) { + log.warn("Webhook does not exist. DomainGroupJob is abandoned"); + } catch (WebServiceCommunicationException e) { + log.error("Failed to communicate with external system for the webhoook of domain group with id {}", domainGroup.getId()); + throw new JobExecutionException("Failed communication with external system"); + } + } +} diff --git a/src/main/java/net/geant/nmaas/portal/api/domain/ApplicationStatePerDomainView.java b/src/main/java/net/geant/nmaas/portal/api/domain/ApplicationStatePerDomainView.java index 7ed46a20911b349308d0eebcdb3bbd0b93c36dcd..ccce6b28f866030a0897efc855ff62458a58f250 100644 --- a/src/main/java/net/geant/nmaas/portal/api/domain/ApplicationStatePerDomainView.java +++ b/src/main/java/net/geant/nmaas/portal/api/domain/ApplicationStatePerDomainView.java @@ -3,9 +3,11 @@ package net.geant.nmaas.portal.api.domain; import lombok.Getter; import lombok.Setter; +import java.io.Serializable; + @Getter @Setter -public class ApplicationStatePerDomainView { +public class ApplicationStatePerDomainView implements Serializable { Long applicationBaseId; String applicationBaseName; boolean enabled; diff --git a/src/main/java/net/geant/nmaas/portal/api/domain/DomainBase.java b/src/main/java/net/geant/nmaas/portal/api/domain/DomainBase.java index 9bfd26a5b81a42a51a4c4dfe7c763ec4214a4e22..cf77585ec84652b6db6cbc25f83f9b9a8fe00981 100644 --- a/src/main/java/net/geant/nmaas/portal/api/domain/DomainBase.java +++ b/src/main/java/net/geant/nmaas/portal/api/domain/DomainBase.java @@ -4,10 +4,12 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import java.io.Serializable; + @Getter @Setter @NoArgsConstructor -public class DomainBase { +public class DomainBase implements Serializable { Long id; String name; diff --git a/src/main/java/net/geant/nmaas/portal/api/domain/DomainGroupView.java b/src/main/java/net/geant/nmaas/portal/api/domain/DomainGroupView.java index 2e40d9005a04d0013f0e5867c3fffcd1ac2a7fed..a5855816dd63f59af5c3088119d4c7230cbe539a 100644 --- a/src/main/java/net/geant/nmaas/portal/api/domain/DomainGroupView.java +++ b/src/main/java/net/geant/nmaas/portal/api/domain/DomainGroupView.java @@ -6,6 +6,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; import lombok.Setter; +import java.io.Serializable; import java.util.ArrayList; import java.util.List; @@ -13,7 +14,7 @@ import java.util.List; @NoArgsConstructor @Getter @Setter -public class DomainGroupView { +public class DomainGroupView implements Serializable { @NotNull private Long id; diff --git a/src/main/java/net/geant/nmaas/portal/api/domain/DomainGroupWebhookDto.java b/src/main/java/net/geant/nmaas/portal/api/domain/DomainGroupWebhookDto.java new file mode 100644 index 0000000000000000000000000000000000000000..75685180793fe87af636eabd3623888851ca0aeb --- /dev/null +++ b/src/main/java/net/geant/nmaas/portal/api/domain/DomainGroupWebhookDto.java @@ -0,0 +1,14 @@ +package net.geant.nmaas.portal.api.domain; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +@AllArgsConstructor +@Getter +@Setter +public class DomainGroupWebhookDto { + + private DomainGroupView domainGroup; + private String action; +} diff --git a/src/main/java/net/geant/nmaas/portal/api/domain/WebhookEventDto.java b/src/main/java/net/geant/nmaas/portal/api/domain/WebhookEventDto.java index 9049b9c921ed43cbec2e5e99d1557bfeca286512..fe179f6636ba9bbcfca008fd8bb805f183668d4b 100644 --- a/src/main/java/net/geant/nmaas/portal/api/domain/WebhookEventDto.java +++ b/src/main/java/net/geant/nmaas/portal/api/domain/WebhookEventDto.java @@ -25,7 +25,7 @@ public class WebhookEventDto { @Pattern(regexp = "^(Authorization|X-.*)?$", message = "Authorization header must be either 'Authorization' or start with 'X-'") private String authorizationHeader; - public WebhookEventDto (Long id, String name, String targetUrl, WebhookEventType eventType){ + public WebhookEventDto(Long id, String name, String targetUrl, WebhookEventType eventType) { this.id = id; this.name = name; this.targetUrl = targetUrl; diff --git a/src/main/java/net/geant/nmaas/portal/api/info/DashboardController.java b/src/main/java/net/geant/nmaas/portal/api/info/DashboardController.java index baaed209904a5850dfc2048b7491e1475587e97d..2e423bde14dc0e2a9d60d0b7f6007ab906bbd3a4 100644 --- a/src/main/java/net/geant/nmaas/portal/api/info/DashboardController.java +++ b/src/main/java/net/geant/nmaas/portal/api/info/DashboardController.java @@ -5,11 +5,15 @@ import lombok.extern.slf4j.Slf4j; import net.geant.nmaas.portal.api.domain.ContentView; import net.geant.nmaas.portal.persistent.entity.Content; import net.geant.nmaas.portal.service.DashboardService; +import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import java.time.OffsetDateTime; + @RestController @AllArgsConstructor @RequestMapping("/api/dashboard") @@ -19,8 +23,11 @@ public class DashboardController { private DashboardService dashboardService; @GetMapping("/admin") - public DashboardView getDashboardAdmin() { - DashboardView view = dashboardService.getSystemDashboard(); + @PreAuthorize("hasRole('ROLE_SYSTEM_ADMIN')") + public DashboardView getDashboardAdmin( @RequestParam("startDate") OffsetDateTime startDate, + @RequestParam("end") OffsetDateTime endDate) { + checkDate(startDate,endDate); + DashboardView view = dashboardService.getSystemDashboard(startDate,endDate); log.error("View : {}", view.toString()); return view; @@ -32,4 +39,14 @@ public class DashboardController { log.error("View : {}", view.toString()); return view; } + + private void checkDate(OffsetDateTime startDate, OffsetDateTime endDate) { + if(startDate == null || endDate == null) { + throw new IllegalArgumentException("Start date can not be null"); + } + + if(startDate.isAfter(endDate)) { + throw new IllegalArgumentException("Start date is after end date."); + } + } } diff --git a/src/main/java/net/geant/nmaas/portal/api/market/AppInstanceController.java b/src/main/java/net/geant/nmaas/portal/api/market/AppInstanceController.java index ca76028b193748dc1aa5763788e62f5b0512d737..81365d8a6ed7c899f34f4ee9eeff9ac7f03351a2 100644 --- a/src/main/java/net/geant/nmaas/portal/api/market/AppInstanceController.java +++ b/src/main/java/net/geant/nmaas/portal/api/market/AppInstanceController.java @@ -57,6 +57,7 @@ import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; import java.lang.reflect.Field; @@ -252,7 +253,9 @@ public class AppInstanceController extends AppBaseController { @Transactional public Id createAppInstance(@RequestBody AppInstanceRequest appInstanceRequest, @NotNull Principal principal, - @PathVariable Long domainId) { + @PathVariable Long domainId, + @RequestParam(name = "clusterId",required = false) Long clusterId) { + log.error("Cluster = {}", clusterId); Application app = getApp(appInstanceRequest.getApplicationId()); Domain domain = domainService.findDomain(domainId) .orElseThrow(() -> new MissingElementException("Domain not found")); diff --git a/src/main/java/net/geant/nmaas/portal/api/market/WebhookEventAdvice.java b/src/main/java/net/geant/nmaas/portal/api/market/WebhookEventAdvice.java index 36715f90ccc58a89d4303c7fbd7a16c18313e3e6..29373cd06012f63eae82d7318e269db931f52e85 100644 --- a/src/main/java/net/geant/nmaas/portal/api/market/WebhookEventAdvice.java +++ b/src/main/java/net/geant/nmaas/portal/api/market/WebhookEventAdvice.java @@ -1,5 +1,6 @@ package net.geant.nmaas.portal.api.market; +import lombok.extern.slf4j.Slf4j; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.MethodArgumentNotValidException; @@ -10,6 +11,7 @@ import java.util.HashMap; import java.util.Map; @ControllerAdvice(assignableTypes = WebhookEventController.class) +@Slf4j public class WebhookEventAdvice { @ExceptionHandler(MethodArgumentNotValidException.class) @@ -19,6 +21,8 @@ public class WebhookEventAdvice { ex.getBindingResult().getFieldErrors().forEach(error -> errors.put(error.getField(), error.getDefaultMessage())); + log.warn("Responding with 400 with errors: {}", errors); + return new ResponseEntity<>(errors, HttpStatus.BAD_REQUEST); } diff --git a/src/main/java/net/geant/nmaas/portal/api/security/EncryptionService.java b/src/main/java/net/geant/nmaas/portal/api/security/EncryptionService.java index 91b2f05e98c65dc45146b954ca640fab646fa31d..e15c1e4ed29c58420fc0960f7d82391eeb95cfd8 100644 --- a/src/main/java/net/geant/nmaas/portal/api/security/EncryptionService.java +++ b/src/main/java/net/geant/nmaas/portal/api/security/EncryptionService.java @@ -1,5 +1,6 @@ package net.geant.nmaas.portal.api.security; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.stereotype.Service; @@ -18,11 +19,15 @@ public class EncryptionService { private static final int GCM_TAG_LENGTH = 128; private static final int IV_LENGTH = 12; - @Value("${security.encryption.secret-key}") - private String secretKey; + private final String secretKey; + private final String algorithm; - @Value("${security.encryption.algorithm}") - private String algorithm; + @Autowired + public EncryptionService(@Value("${security.encryption.secret-key}") String secretKey, + @Value("${security.encryption.algorithm}") String algorithm) { + this.secretKey = secretKey; + this.algorithm = algorithm; + } public String encrypt(String plainText) throws GeneralSecurityException { Cipher cipher = Cipher.getInstance(algorithm); diff --git a/src/main/java/net/geant/nmaas/portal/persistent/entity/Domain.java b/src/main/java/net/geant/nmaas/portal/persistent/entity/Domain.java index 8f36be69979e7cb408093cdf4b820eb0f0e54ebd..1a740275d82971bc0163ff8dce08872c95a12ed3 100644 --- a/src/main/java/net/geant/nmaas/portal/persistent/entity/Domain.java +++ b/src/main/java/net/geant/nmaas/portal/persistent/entity/Domain.java @@ -1,14 +1,5 @@ package net.geant.nmaas.portal.persistent.entity; -import lombok.AccessLevel; -import lombok.EqualsAndHashCode; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; -import net.geant.nmaas.dcn.deployment.entities.DomainDcnDetails; -import net.geant.nmaas.orchestration.entities.DomainTechDetails; -import net.geant.nmaas.externalservices.kubernetes.entities.KCluster; - import jakarta.persistence.CascadeType; import jakarta.persistence.Column; import jakarta.persistence.ElementCollection; @@ -24,14 +15,22 @@ import jakarta.persistence.OneToOne; import jakarta.persistence.Table; import jakarta.persistence.UniqueConstraint; import jakarta.validation.constraints.NotNull; +import lombok.AccessLevel; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; +import net.geant.nmaas.dcn.deployment.entities.DomainDcnDetails; +import net.geant.nmaas.externalservices.kubernetes.entities.KCluster; +import net.geant.nmaas.orchestration.entities.DomainTechDetails; + import java.io.Serializable; import java.util.ArrayList; import java.util.List; -import java.util.stream.Collectors; @Entity @Table(uniqueConstraints = { - @UniqueConstraint(columnNames={"name"}), @UniqueConstraint(columnNames={"codename"}) + @UniqueConstraint(columnNames = {"name"}), @UniqueConstraint(columnNames = {"codename"}) }) @NoArgsConstructor(access = AccessLevel.PROTECTED) @Getter @@ -39,86 +38,85 @@ import java.util.stream.Collectors; @EqualsAndHashCode(onlyExplicitlyIncluded = true) public class Domain implements Serializable { - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - Long id; + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + Long id; - @EqualsAndHashCode.Include - @NotNull + @EqualsAndHashCode.Include + @NotNull @Column(nullable = false, unique = true) private String codename; - @EqualsAndHashCode.Include - @NotNull - @Column(nullable = false, unique=true) - String name; - - @OneToOne(cascade=CascadeType.ALL, orphanRemoval=true) - private DomainDcnDetails domainDcnDetails; - - @OneToOne(cascade=CascadeType.ALL, orphanRemoval=true) - private DomainTechDetails domainTechDetails; - - boolean active; - - boolean deleted; - - /** List of applications with state per domain **/ - @ElementCollection(fetch = FetchType.LAZY) - private List<ApplicationStatePerDomain> applicationStatePerDomain = new ArrayList<>(); - - @ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.ALL}) - @JoinTable( - name = "domains_groups", - joinColumns = { @JoinColumn(name = "domain_id") }, - inverseJoinColumns = { @JoinColumn(name = "group_id") } - ) - private List<DomainGroup> groups = new ArrayList<>(); - - @ManyToMany(mappedBy = "domains") - private List<KCluster> clusters = new ArrayList<>(); - - public Domain(String name, String codename) { - super(); - this.name = name; - this.codename = codename; - this.active = true; - } - - public Domain(String name, String codename, boolean active) { - this(name, codename); - this.active = active; - } - - public Domain(Long id, String name, String codename) { - this(name, codename); - this.id = id; - } - - public Domain(Long id, String name, String codename, boolean active) { - this(id, name, codename); - this.active = active; - } - - public void addApplicationState(ApplicationBase applicationBase){ - this.addApplicationState(applicationBase, true); + @EqualsAndHashCode.Include + @NotNull + @Column(nullable = false, unique = true) + String name; + + @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true) + private DomainDcnDetails domainDcnDetails; + + @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true) + private DomainTechDetails domainTechDetails; + + boolean active; + + boolean deleted; + + /** + * List of applications with state per domain + **/ + @ElementCollection(fetch = FetchType.LAZY) + private List<ApplicationStatePerDomain> applicationStatePerDomain = new ArrayList<>(); + + @ManyToMany(fetch = FetchType.LAZY, cascade = {CascadeType.ALL}) + @JoinTable( + name = "domains_groups", + joinColumns = {@JoinColumn(name = "domain_id")}, + inverseJoinColumns = {@JoinColumn(name = "group_id")} + ) + private List<DomainGroup> groups = new ArrayList<>(); + + @ManyToMany(mappedBy = "domains") + private List<KCluster> clusters = new ArrayList<>(); + + public Domain(String name, String codename) { + super(); + this.name = name; + this.codename = codename; + this.active = true; } - public void addApplicationState(ApplicationBase applicationBase, boolean enabled){ - this.addApplicationState(new ApplicationStatePerDomain(applicationBase, enabled)); + public Domain(String name, String codename, boolean active) { + this(name, codename); + this.active = active; } - public void addApplicationState(ApplicationStatePerDomain appState) { - if (!this.applicationStatePerDomain.stream().map(ApplicationStatePerDomain::getApplicationBase) - .map(ApplicationBase::getId).collect(Collectors.toList()).contains(appState.getApplicationBase().getId())) { - this.applicationStatePerDomain.add(appState); - } - } + public Domain(Long id, String name, String codename) { + this(name, codename); + this.id = id; + } - public void addGroup(DomainGroup group) { - this.groups.add(group); - group.getDomains().add(this); - } + public Domain(Long id, String name, String codename, boolean active) { + this(id, name, codename); + this.active = active; + } -} + public void addApplicationState(ApplicationBase applicationBase) { + this.addApplicationState(applicationBase, true); + } + + public void addApplicationState(ApplicationBase applicationBase, boolean enabled) { + this.addApplicationState(new ApplicationStatePerDomain(applicationBase, enabled)); + } + public void addApplicationState(ApplicationStatePerDomain appState) { + if (!this.applicationStatePerDomain.stream() + .map(ApplicationStatePerDomain::getApplicationBase) + .map(ApplicationBase::getId) + .toList() + .contains(appState.getApplicationBase().getId())) { + this.applicationStatePerDomain.add(appState); + } + } + +} diff --git a/src/main/java/net/geant/nmaas/portal/persistent/entity/DomainGroup.java b/src/main/java/net/geant/nmaas/portal/persistent/entity/DomainGroup.java index 186f96dcf0397919d061d6c389ae6f42e4bfb751..74d554474081b8ed4233bba8c9d4b2ae03ac7669 100644 --- a/src/main/java/net/geant/nmaas/portal/persistent/entity/DomainGroup.java +++ b/src/main/java/net/geant/nmaas/portal/persistent/entity/DomainGroup.java @@ -67,7 +67,7 @@ public class DomainGroup implements Serializable { public DomainGroup(String name, String codename) { super(); this.name = name; - this.codename = name; + this.codename = codename; } public DomainGroup(Long id, String name, String codename) { diff --git a/src/main/java/net/geant/nmaas/portal/persistent/repositories/AppInstanceRepository.java b/src/main/java/net/geant/nmaas/portal/persistent/repositories/AppInstanceRepository.java index 2208598413ba01837521d2a0ee46fb809f0a7928..3002af2e17a43f61ef9bb1fcfb515436054de9d5 100644 --- a/src/main/java/net/geant/nmaas/portal/persistent/repositories/AppInstanceRepository.java +++ b/src/main/java/net/geant/nmaas/portal/persistent/repositories/AppInstanceRepository.java @@ -49,11 +49,14 @@ public interface AppInstanceRepository extends JpaRepository<AppInstance, Long> @Query("select count(ai.id) FROM AppInstance ai where ai.createdAt >= :sinceTime") int countAllDeployedSinceTime(@Param("sinceTime") long sinceTime); + @Query("select count(ai.id) FROM AppInstance ai where ai.createdAt >= :sinceTime AND ai.createdAt <= :endTime") + int countAllDeployedSinceTime(@Param("sinceTime") long sinceTime, @Param("sinceTime") long toTime); + @Query("select count(ai.id) FROM AppInstance ai JOIN AppDeployment ad on ad.deploymentId = ai.internalId where ai.application.name = ?1") int countByName(String name); - @Query("select ai FROM AppInstance ai where ai.createdAt >= :sinceTime") - List<AppInstance> findAllInTimePeriod(@Param("sinceTime") long sinceTime); + @Query("select ai FROM AppInstance ai where ai.createdAt >= :sinceTime AND ai.createdAt <= :endTime") + List<AppInstance> findAllInTimePeriod(@Param("sinceTime") long start, @Param("endTime") long end); int countAllByOwner(User user); diff --git a/src/main/java/net/geant/nmaas/portal/service/ApplicationBaseService.java b/src/main/java/net/geant/nmaas/portal/service/ApplicationBaseService.java index 0f6ce93f96b401c9bada376339e903d6556bb9e0..18aba500d32976210be1fa1057164f939898d0ba 100644 --- a/src/main/java/net/geant/nmaas/portal/service/ApplicationBaseService.java +++ b/src/main/java/net/geant/nmaas/portal/service/ApplicationBaseService.java @@ -1,6 +1,5 @@ package net.geant.nmaas.portal.service; -import net.geant.nmaas.portal.api.domain.ApplicationBaseS; import net.geant.nmaas.portal.api.domain.ApplicationBaseViewS; import net.geant.nmaas.portal.persistent.entity.ApplicationBase; import net.geant.nmaas.portal.persistent.entity.ApplicationState; @@ -10,6 +9,7 @@ import java.util.List; public interface ApplicationBaseService { ApplicationBase create(ApplicationBase applicationBase); + ApplicationBase update(ApplicationBase applicationBase); ApplicationBase updateOwner(Long id, String owner); @@ -17,14 +17,17 @@ public interface ApplicationBaseService { void updateApplicationVersionState(String name, String version, ApplicationState state); List<ApplicationBase> findAll(); + List<ApplicationBase> findAllActiveApps(); List<ApplicationBaseViewS> findAllActiveAppsSmall(); ApplicationBase getBaseApp(Long id); + ApplicationBase findByName(String name); boolean exists(String name); + boolean isAppActive(ApplicationBase application); void deleteAppBase(ApplicationBase base); diff --git a/src/main/java/net/geant/nmaas/portal/service/DashboardService.java b/src/main/java/net/geant/nmaas/portal/service/DashboardService.java index 53307d2275d9f4d2aea107fb5a959869c671a137..2c172aacac41010fb4b3e072ec1eb70b6afde746 100644 --- a/src/main/java/net/geant/nmaas/portal/service/DashboardService.java +++ b/src/main/java/net/geant/nmaas/portal/service/DashboardService.java @@ -3,9 +3,11 @@ package net.geant.nmaas.portal.service; import net.geant.nmaas.portal.api.info.DashboardView; import net.geant.nmaas.portal.api.info.DomainDashboardView; +import java.time.OffsetDateTime; + public interface DashboardService { - public DashboardView getSystemDashboard(); + public DashboardView getSystemDashboard(OffsetDateTime startDate, OffsetDateTime endDate); public DomainDashboardView getSystemDomainDashboard(Long domainId); } diff --git a/src/main/java/net/geant/nmaas/portal/service/impl/ApplicationBaseServiceImpl.java b/src/main/java/net/geant/nmaas/portal/service/impl/ApplicationBaseServiceImpl.java index 0231ea424db52d97c5655dbd9a8e316598c9b0e3..5e77ddf1b9091ef1bf47c1e68634d9a8731267d5 100644 --- a/src/main/java/net/geant/nmaas/portal/service/impl/ApplicationBaseServiceImpl.java +++ b/src/main/java/net/geant/nmaas/portal/service/impl/ApplicationBaseServiceImpl.java @@ -1,10 +1,8 @@ package net.geant.nmaas.portal.service.impl; import jakarta.transaction.Transactional; -import lombok.AllArgsConstructor; -import lombok.extern.log4j.Log4j2; +import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; -import net.geant.nmaas.portal.api.domain.AppDescriptionView; import net.geant.nmaas.portal.api.domain.ApplicationBaseS; import net.geant.nmaas.portal.api.domain.ApplicationBaseViewS; import net.geant.nmaas.portal.api.exception.MissingElementException; @@ -21,7 +19,6 @@ import net.geant.nmaas.portal.service.ApplicationStatePerDomainService; import net.geant.nmaas.portal.service.DomainService; import org.apache.commons.lang3.StringUtils; import org.modelmapper.ModelMapper; -import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.CachePut; import org.springframework.context.ApplicationEventPublisher; @@ -36,7 +33,7 @@ import java.util.Optional; import java.util.stream.Collectors; @Service -@AllArgsConstructor +@RequiredArgsConstructor @Slf4j public class ApplicationBaseServiceImpl implements ApplicationBaseService { @@ -47,9 +44,7 @@ public class ApplicationBaseServiceImpl implements ApplicationBaseService { private final ApplicationStatePerDomainService applicationStatePerDomainService; private final ApplicationEventPublisher eventPublisher; private final DomainService domainService; - - @Autowired - protected ModelMapper modelMapper; + private final ModelMapper modelMapper; @Override @Transactional diff --git a/src/main/java/net/geant/nmaas/portal/service/impl/ConfigurationManagerImpl.java b/src/main/java/net/geant/nmaas/portal/service/impl/ConfigurationManagerImpl.java index 9c4252840c81db87da30490f2698a559f11d3d89..2c7c5427605a31c98b52b510ae0989c577862936 100644 --- a/src/main/java/net/geant/nmaas/portal/service/impl/ConfigurationManagerImpl.java +++ b/src/main/java/net/geant/nmaas/portal/service/impl/ConfigurationManagerImpl.java @@ -10,7 +10,7 @@ import net.geant.nmaas.portal.persistent.entity.InternationalizationSimple; import net.geant.nmaas.portal.persistent.repositories.ConfigurationRepository; import net.geant.nmaas.portal.persistent.repositories.InternationalizationSimpleRepository; import net.geant.nmaas.portal.service.ConfigurationManager; -import net.geant.nmaas.scheduling.BulkDeploymentScheduleConfig; +import net.geant.nmaas.scheduling.BulkDeploymentScheduleInit; import net.geant.nmaas.scheduling.ScheduleManager; import org.modelmapper.ModelMapper; import org.springframework.stereotype.Component; @@ -57,8 +57,8 @@ public class ConfigurationManagerImpl implements ConfigurationManager { } if (!updatedConfiguration.getBulkDeploymentJobCron().equalsIgnoreCase(configuration.get().getBulkDeploymentJobCron())) { // job needs to be recreated - scheduleManager.deleteJob(BulkDeploymentScheduleConfig.BULK_DEPLOYMENT_JOB); - scheduleManager.createJob(bulkDeploymentJob, BulkDeploymentScheduleConfig.BULK_DEPLOYMENT_JOB, updatedConfiguration.getBulkDeploymentJobCron()); + scheduleManager.deleteJob(BulkDeploymentScheduleInit.BULK_DEPLOYMENT_JOB); + scheduleManager.createJob(bulkDeploymentJob, BulkDeploymentScheduleInit.BULK_DEPLOYMENT_JOB, updatedConfiguration.getBulkDeploymentJobCron()); } repository.save(modelMapper.map(updatedConfiguration, Configuration.class)); } 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 1569cfee20dba94bf5c523dcffd7610d72c12bd3..420bf728bc72fac8b601463b616f1e3c4c739945 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 @@ -47,14 +47,16 @@ public class DashboardServiceImpl implements DashboardService { @Override - public DashboardView getSystemDashboard() { + public DashboardView getSystemDashboard(OffsetDateTime startDate, OffsetDateTime endDate) { + + long startTimeStamp = System.currentTimeMillis() - startDate.toEpochSecond(); + long endTimeStamp = System.currentTimeMillis() - endDate.toEpochSecond(); - long weekTimestamp = System.currentTimeMillis() - Duration.ofDays(7).toMillis(); List<String> baseNames = applicationBaseRepository.findAllNames(); Map<String, Integer> applicationDeploymentCountPerName = new HashMap<>(); - List<DashboardDeploymentsView> deploymentsViews = appInstanceRepo.findAllInTimePeriod(weekTimestamp) + List<DashboardDeploymentsView> deploymentsViews = appInstanceRepo.findAllInTimePeriod(startTimeStamp, endTimeStamp) .stream().map(entry -> DashboardDeploymentsView.builder().user(entry.getOwner().getUsername()) .domainName(entry.getDomain().getName()) .applicationName(entry.getApplication().getName()) @@ -72,7 +74,7 @@ public class DashboardServiceImpl implements DashboardService { .domainsCount(domainRepository.count()) .userCount(userRepository.count()) .instanceCount(appInstanceRepo.count()) - .instanceCountInPeriod(appInstanceRepo.countAllDeployedSinceTime(weekTimestamp)) + .instanceCountInPeriod(appInstanceRepo.countAllDeployedSinceTime(startTimeStamp, endTimeStamp)) .instanceCountInPeriodDetails(deploymentsViews) .popularApps(applicationDeploymentCountPerName).build(); } diff --git a/src/main/java/net/geant/nmaas/portal/service/impl/DomainGroupServiceImpl.java b/src/main/java/net/geant/nmaas/portal/service/impl/DomainGroupServiceImpl.java index 3dbc1baf929b1624f7abf0b246166ea6966ce1e9..2823c728ce858495ce6f708d8968d072848e1503 100644 --- a/src/main/java/net/geant/nmaas/portal/service/impl/DomainGroupServiceImpl.java +++ b/src/main/java/net/geant/nmaas/portal/service/impl/DomainGroupServiceImpl.java @@ -2,6 +2,8 @@ package net.geant.nmaas.portal.service.impl; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import net.geant.nmaas.orchestration.jobs.DomainCreationJob; +import net.geant.nmaas.orchestration.jobs.DomainGroupJob; import net.geant.nmaas.portal.api.domain.ApplicationStatePerDomainView; import net.geant.nmaas.portal.api.domain.DomainGroupView; import net.geant.nmaas.portal.api.exception.MissingElementException; @@ -11,16 +13,21 @@ import net.geant.nmaas.portal.persistent.entity.ApplicationStatePerDomain; import net.geant.nmaas.portal.persistent.entity.Domain; import net.geant.nmaas.portal.persistent.entity.DomainGroup; import net.geant.nmaas.portal.persistent.entity.User; +import net.geant.nmaas.portal.persistent.entity.WebhookEventType; import net.geant.nmaas.portal.persistent.repositories.DomainGroupRepository; +import net.geant.nmaas.portal.persistent.repositories.WebhookEventRepository; import net.geant.nmaas.portal.service.ApplicationStatePerDomainService; import net.geant.nmaas.portal.service.DomainGroupService; +import net.geant.nmaas.scheduling.ScheduleManager; import org.apache.commons.lang3.StringUtils; import org.modelmapper.ModelMapper; import org.springframework.stereotype.Service; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.Iterator; import java.util.List; +import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; @@ -31,6 +38,8 @@ public class DomainGroupServiceImpl implements DomainGroupService { private final DomainGroupRepository domainGroupRepository; private final ApplicationStatePerDomainService applicationStatePerDomainService; + private final WebhookEventRepository webhookEventRepository; + private final ScheduleManager scheduleManager; private final ModelMapper modelMapper; @@ -55,6 +64,10 @@ public class DomainGroupServiceImpl implements DomainGroupService { DomainGroup domainGroupEntity = modelMapper.map(domainGroup, DomainGroup.class); domainGroupEntity.setApplicationStatePerDomain(applicationStatePerDomainList); domainGroupEntity = domainGroupRepository.save(domainGroupEntity); + + //call existing webhooks + DomainGroupView domainGroupView = modelMapper.map(domainGroupEntity, DomainGroupView.class); + webhookEventRepository.findIdByEventType(WebhookEventType.DOMAIN_GROUP_CHANGE).forEach(id -> scheduleManager.createOneTimeJob(DomainGroupJob.class, "DomainGroup_" + id + "_" + domainGroupView.getId()+ "_" + LocalDateTime.now(), Map.of("webhookId", id, "action", "create","domainGroup",domainGroupView))); return modelMapper.map(domainGroupEntity, DomainGroupView.class); } @@ -81,6 +94,7 @@ public class DomainGroupServiceImpl implements DomainGroupService { @Override public void deleteDomainGroup(Long domainGroupId) { DomainGroup domainGroup = domainGroupRepository.findById(domainGroupId).orElseThrow(); + DomainGroupView domainGroupView = modelMapper.map(domainGroup, DomainGroupView.class); List<Domain> toRemove = new ArrayList<>(domainGroup.getDomains()); Iterator<Domain> iterator = toRemove.iterator(); while (iterator.hasNext()) { @@ -90,6 +104,8 @@ public class DomainGroupServiceImpl implements DomainGroupService { iterator.remove(); } domainGroupRepository.deleteById(domainGroupId); + //call existing webhooks + webhookEventRepository.findIdByEventType(WebhookEventType.DOMAIN_GROUP_CHANGE).forEach(id -> scheduleManager.createOneTimeJob(DomainGroupJob.class, "DomainGroup_" + id + "_" + domainGroup.getId()+ "_" + LocalDateTime.now(), Map.of("webhookId", id, "action", "delete","domainGroup",domainGroupView))); } @Override @@ -128,7 +144,11 @@ public class DomainGroupServiceImpl implements DomainGroupService { } domainGroupRepository.save(domainGroup); - return modelMapper.map(domainGroup, DomainGroupView.class); + + //call existing webhooks + DomainGroupView domainGroupView = modelMapper.map(domainGroup, DomainGroupView.class); + webhookEventRepository.findIdByEventType(WebhookEventType.DOMAIN_GROUP_CHANGE).forEach(id -> scheduleManager.createOneTimeJob(DomainGroupJob.class, "DomainGroup_" + id + "_" + domainGroupView.getId()+ "_" + LocalDateTime.now(), Map.of("webhookId", id, "action", "update","domainGroup",domainGroupView))); + return domainGroupView; } protected void checkParam(DomainGroupView domainGroup) { diff --git a/src/main/java/net/geant/nmaas/scheduling/AppUpgradeScheduleConfig.java b/src/main/java/net/geant/nmaas/scheduling/AppUpgradeScheduleConfig.java deleted file mode 100644 index 79e7298c15cd0e4477bf5376d357edf0ad71511a..0000000000000000000000000000000000000000 --- a/src/main/java/net/geant/nmaas/scheduling/AppUpgradeScheduleConfig.java +++ /dev/null @@ -1,64 +0,0 @@ -package net.geant.nmaas.scheduling; - -import com.google.common.base.Strings; -import lombok.extern.slf4j.Slf4j; -import net.geant.nmaas.nmservice.deployment.bulks.BulkDeploymentJob; -import net.geant.nmaas.orchestration.AppUpgradeSummaryJob; -import net.geant.nmaas.orchestration.AppUpgradeTriggerJob; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.transaction.annotation.Transactional; - -@Configuration -@Slf4j -public class AppUpgradeScheduleConfig { - - private static final String APP_UPGRADE_JOB_NAME = "AppUpgradeJob"; - private static final String APP_UPGRADE_SUMMARY_JOB_NAME = "AppUpgradeSummaryJob"; - - - @Bean - public InitializingBean insertDefaultAppUpgradeJob() { - return new InitializingBean() { - - @Autowired - private AppUpgradeTriggerJob appUpgradeTriggerJob; - - @Autowired - private AppUpgradeSummaryJob appUpgradeSummaryJob; - - @Autowired - private BulkDeploymentJob bulkDeploymentJob; - - @Autowired - private ScheduleManager scheduleManager; - - @Value("${nmaas.service.upgrade.cron}") - String appUpgradeCron; - - @Value("${nmaas.service.upgrade-summary.cron}") - String appUpgradeSummaryCron; - - @Override - @Transactional - public void afterPropertiesSet() { - if (Strings.isNullOrEmpty(appUpgradeCron)) { - log.warn("Application upgrade cron expression not provided"); - log.warn("Automatic application upgrades are disabled!"); - } else { - this.scheduleManager.createJob(appUpgradeTriggerJob, APP_UPGRADE_JOB_NAME, appUpgradeCron); - } - if (Strings.isNullOrEmpty(appUpgradeSummaryCron)) { - log.warn("Application upgrade summary cron expression not provided"); - log.warn("Won't send out email notifications about automatic upgrades in given period"); - } else { - this.scheduleManager.createJob(appUpgradeSummaryJob, APP_UPGRADE_SUMMARY_JOB_NAME, appUpgradeSummaryCron); - } - } - }; - } - -} diff --git a/src/main/java/net/geant/nmaas/scheduling/AppUpgradeScheduleInit.java b/src/main/java/net/geant/nmaas/scheduling/AppUpgradeScheduleInit.java new file mode 100644 index 0000000000000000000000000000000000000000..0558c7f8db4e1f79a8ecc26d91f7754c4c19e661 --- /dev/null +++ b/src/main/java/net/geant/nmaas/scheduling/AppUpgradeScheduleInit.java @@ -0,0 +1,56 @@ +package net.geant.nmaas.scheduling; + +import com.google.common.base.Strings; +import lombok.extern.slf4j.Slf4j; +import net.geant.nmaas.orchestration.AppUpgradeSummaryJob; +import net.geant.nmaas.orchestration.AppUpgradeTriggerJob; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +@Component +@Slf4j +public class AppUpgradeScheduleInit implements InitializingBean { + + private static final String APP_UPGRADE_JOB_NAME = "AppUpgradeJob"; + private static final String APP_UPGRADE_SUMMARY_JOB_NAME = "AppUpgradeSummaryJob"; + + private final AppUpgradeTriggerJob appUpgradeTriggerJob; + private final AppUpgradeSummaryJob appUpgradeSummaryJob; + private final ScheduleManager scheduleManager; + private final String appUpgradeCron; + private final String appUpgradeSummaryCron; + + @Autowired + public AppUpgradeScheduleInit(AppUpgradeTriggerJob appUpgradeTriggerJob, + AppUpgradeSummaryJob appUpgradeSummaryJob, + ScheduleManager scheduleManager, + @Value("${nmaas.service.upgrade.cron}") String appUpgradeCron, + @Value("${nmaas.service.upgrade-summary.cron}") String appUpgradeSummaryCron) { + this.appUpgradeTriggerJob = appUpgradeTriggerJob; + this.appUpgradeSummaryJob = appUpgradeSummaryJob; + this.scheduleManager = scheduleManager; + this.appUpgradeCron = appUpgradeCron; + this.appUpgradeSummaryCron = appUpgradeSummaryCron; + } + + @Override + @Transactional + public void afterPropertiesSet() { + if (Strings.isNullOrEmpty(appUpgradeCron)) { + log.warn("Application upgrade cron expression not provided"); + log.warn("Automatic application upgrades are disabled!"); + } else { + this.scheduleManager.createJob(appUpgradeTriggerJob, APP_UPGRADE_JOB_NAME, appUpgradeCron); + } + if (Strings.isNullOrEmpty(appUpgradeSummaryCron)) { + log.warn("Application upgrade summary cron expression not provided"); + log.warn("Won't send out email notifications about automatic upgrades in given period"); + } else { + this.scheduleManager.createJob(appUpgradeSummaryJob, APP_UPGRADE_SUMMARY_JOB_NAME, appUpgradeSummaryCron); + } + } + +} \ No newline at end of file diff --git a/src/main/java/net/geant/nmaas/scheduling/BulkDeploymentScheduleConfig.java b/src/main/java/net/geant/nmaas/scheduling/BulkDeploymentScheduleConfig.java deleted file mode 100644 index dfa061644f71810f0debdc1968078caad54451bd..0000000000000000000000000000000000000000 --- a/src/main/java/net/geant/nmaas/scheduling/BulkDeploymentScheduleConfig.java +++ /dev/null @@ -1,54 +0,0 @@ -package net.geant.nmaas.scheduling; - -import com.google.common.base.Strings; -import lombok.extern.slf4j.Slf4j; -import net.geant.nmaas.nmservice.deployment.bulks.BulkDeploymentJob; -import net.geant.nmaas.portal.service.ConfigurationManager; -import org.springframework.beans.factory.InitializingBean; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.Configuration; -import org.springframework.context.annotation.DependsOn; -import org.springframework.transaction.annotation.Transactional; - -@Configuration -@Slf4j -public class BulkDeploymentScheduleConfig { - - public static final String BULK_DEPLOYMENT_JOB = "BulkDeploymentJob"; - - @Bean - @DependsOn({"portalConfiguration"}) - public InitializingBean insertDefaultBulkDeploymentJob() { - return new InitializingBean() { - - @Autowired - private BulkDeploymentJob bulkDeploymentJob; - - @Autowired - private ScheduleManager scheduleManager; - - @Autowired - private ConfigurationManager configurationManager; - - @Value("${nmaas.service.bulk-deployment.cron}") - String bulkDeploymentCron; - - @Override - @Transactional - public void afterPropertiesSet() { - String bulkDeploymentCronFromDb = configurationManager.getConfiguration().getBulkDeploymentJobCron(); - if (!Strings.isNullOrEmpty(bulkDeploymentCronFromDb)) { - log.debug("Scheduling bulk deployment job based on cron loaded from the database"); - this.scheduleManager.createJob(bulkDeploymentJob, BULK_DEPLOYMENT_JOB, bulkDeploymentCronFromDb); - } else if (Strings.isNullOrEmpty(bulkDeploymentCron)) { - log.warn("Bulk deployment cron expression not provided"); - } else { - this.scheduleManager.createJob(bulkDeploymentJob, BULK_DEPLOYMENT_JOB, bulkDeploymentCron); - } - } - }; - } - -} \ No newline at end of file diff --git a/src/main/java/net/geant/nmaas/scheduling/BulkDeploymentScheduleInit.java b/src/main/java/net/geant/nmaas/scheduling/BulkDeploymentScheduleInit.java new file mode 100644 index 0000000000000000000000000000000000000000..25e6b52bce83d21ad2e55b368cd536a6f4e7bfef --- /dev/null +++ b/src/main/java/net/geant/nmaas/scheduling/BulkDeploymentScheduleInit.java @@ -0,0 +1,51 @@ +package net.geant.nmaas.scheduling; + +import com.google.common.base.Strings; +import lombok.extern.slf4j.Slf4j; +import net.geant.nmaas.nmservice.deployment.bulks.BulkDeploymentJob; +import net.geant.nmaas.portal.service.ConfigurationManager; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.DependsOn; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +@Component +@DependsOn({"portalConfiguration"}) +@Slf4j +public class BulkDeploymentScheduleInit implements InitializingBean { + + public static final String BULK_DEPLOYMENT_JOB = "BulkDeploymentJob"; + + private final BulkDeploymentJob bulkDeploymentJob; + private final ScheduleManager scheduleManager; + private final ConfigurationManager configurationManager; + private final String bulkDeploymentCron; + + @Autowired + public BulkDeploymentScheduleInit(BulkDeploymentJob bulkDeploymentJob, + ScheduleManager scheduleManager, + ConfigurationManager configurationManager, + @Value("${nmaas.service.bulk-deployment.cron}") String bulkDeploymentCron) { + this.bulkDeploymentJob = bulkDeploymentJob; + this.scheduleManager = scheduleManager; + this.configurationManager = configurationManager; + this.bulkDeploymentCron = bulkDeploymentCron; + } + + @Override + @Transactional + public void afterPropertiesSet() { + String bulkDeploymentCronFromDb = configurationManager.getConfiguration().getBulkDeploymentJobCron(); + if (!Strings.isNullOrEmpty(bulkDeploymentCronFromDb)) { + log.debug("Scheduling bulk deployment job based on cron loaded from the database"); + this.scheduleManager.createJob(bulkDeploymentJob, BULK_DEPLOYMENT_JOB, bulkDeploymentCronFromDb); + } else if (Strings.isNullOrEmpty(bulkDeploymentCron)) { + log.warn("Bulk deployment cron expression not provided"); + } else { + this.scheduleManager.createJob(bulkDeploymentJob, BULK_DEPLOYMENT_JOB, bulkDeploymentCron); + } + } + +} \ No newline at end of file diff --git a/src/main/java/net/geant/nmaas/scheduling/ClusterHealthCheckInit.java b/src/main/java/net/geant/nmaas/scheduling/ClusterHealthCheckScheduleInit.java similarity index 82% rename from src/main/java/net/geant/nmaas/scheduling/ClusterHealthCheckInit.java rename to src/main/java/net/geant/nmaas/scheduling/ClusterHealthCheckScheduleInit.java index aff057a2f1baedee36b5cad8b1715713aba7f42f..e92a04e3ee42bd7f29520167f0f47f38a7837c36 100644 --- a/src/main/java/net/geant/nmaas/scheduling/ClusterHealthCheckInit.java +++ b/src/main/java/net/geant/nmaas/scheduling/ClusterHealthCheckScheduleInit.java @@ -14,7 +14,7 @@ import org.springframework.transaction.annotation.Transactional; @Component @DependsOn({"portalConfiguration"}) @Slf4j -public class ClusterHealthCheckInit implements InitializingBean { +public class ClusterHealthCheckScheduleInit implements InitializingBean { public static final String CLUSTER_HEALTH_CHECK = "ClusterHealthCheck"; @@ -24,10 +24,10 @@ public class ClusterHealthCheckInit implements InitializingBean { private final String healthCheckJobCron; @Autowired - public ClusterHealthCheckInit(ClusterMonitoringJob clusterMonitoringJob, - ScheduleManager scheduleManager, - ConfigurationManager configurationManager, - @Value("${nmaas.service.health-check.cron}") String healthCheckJobCron) { + public ClusterHealthCheckScheduleInit(ClusterMonitoringJob clusterMonitoringJob, + ScheduleManager scheduleManager, + ConfigurationManager configurationManager, + @Value("${nmaas.service.health-check.cron}") String healthCheckJobCron) { this.clusterMonitoringJob = clusterMonitoringJob; this.scheduleManager = scheduleManager; this.configurationManager = configurationManager; diff --git a/src/main/java/net/geant/nmaas/scheduling/JobDescriptor.java b/src/main/java/net/geant/nmaas/scheduling/JobDescriptor.java index da683123c09f6ac0c979a88c17e41666649869c7..59500eecefc6b74988164bc69154d0454f10087f 100644 --- a/src/main/java/net/geant/nmaas/scheduling/JobDescriptor.java +++ b/src/main/java/net/geant/nmaas/scheduling/JobDescriptor.java @@ -1,25 +1,13 @@ package net.geant.nmaas.scheduling; -import lombok.AllArgsConstructor; -import lombok.Getter; -import lombok.NoArgsConstructor; -import lombok.Setter; import net.geant.nmaas.monitor.ServiceType; import net.geant.nmaas.monitor.TimeFormat; import org.quartz.Trigger; -@Getter -@Setter -@NoArgsConstructor -@AllArgsConstructor -public class JobDescriptor { - private ServiceType serviceName; - - private Long checkInterval; - - private TimeFormat timeFormat; +public record JobDescriptor(ServiceType serviceName, Long checkInterval, TimeFormat timeFormat) { Trigger buildTrigger() { return new TriggerDescriptor(serviceName, checkInterval, timeFormat).buildTrigger(); } + } diff --git a/src/main/java/net/geant/nmaas/scheduling/MonitorScheduleInit.java b/src/main/java/net/geant/nmaas/scheduling/MonitorScheduleInit.java new file mode 100644 index 0000000000000000000000000000000000000000..27066a19a948d88f9673464419cd7207b814495b --- /dev/null +++ b/src/main/java/net/geant/nmaas/scheduling/MonitorScheduleInit.java @@ -0,0 +1,51 @@ +package net.geant.nmaas.scheduling; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import net.geant.nmaas.monitor.MonitorManager; +import net.geant.nmaas.monitor.MonitorService; +import net.geant.nmaas.monitor.ServiceType; +import net.geant.nmaas.monitor.model.MonitorEntryView; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; + +import java.util.Arrays; +import java.util.List; +import java.util.Optional; + +@Component +@RequiredArgsConstructor +@Slf4j +public class MonitorScheduleInit implements InitializingBean { + + private final List<MonitorService> monitorServices; + private final ScheduleManager scheduleManager; + private final MonitorManager monitorManager; + + @Override + @Transactional + public void afterPropertiesSet() { + Arrays.stream(ServiceType.values()) + .filter(serviceType -> !scheduleManager.jobExists(serviceType.toString())) // if job does not exist + .forEach(serviceType -> { + MonitorEntryView monitorEntry; + if (monitorManager.existsByServiceName(serviceType)) { // if entry exists + monitorEntry = monitorManager.getMonitorEntries(serviceType.toString()); // read it from database + } else { + monitorEntry = serviceType.getDefaultMonitorEntry(); // if entry does not exist + monitorManager.createMonitorEntry(monitorEntry); // create new default entry + } + Optional<MonitorService> service = monitorServices.stream() + .filter(s -> s.getServiceType().equals(serviceType)) + .filter(MonitorService::schedulable) + .findFirst(); + if (service.isPresent()) { + scheduleManager.createJob(service.get(), monitorEntry); + } else { + log.warn("Monitor service for {} not found or is not schedulable", serviceType); + } + }); + } + +} \ No newline at end of file diff --git a/src/main/java/net/geant/nmaas/scheduling/OneTimeJobListener.java b/src/main/java/net/geant/nmaas/scheduling/OneTimeJobListener.java index 343943a3fba7fd557da39da7ff04019173ca6b6f..fad1d93347ce67d81a9575aa7332971201000951 100644 --- a/src/main/java/net/geant/nmaas/scheduling/OneTimeJobListener.java +++ b/src/main/java/net/geant/nmaas/scheduling/OneTimeJobListener.java @@ -15,6 +15,7 @@ import org.quartz.TriggerBuilder; @RequiredArgsConstructor @Slf4j public class OneTimeJobListener implements JobListener { + private final Scheduler scheduler; private final JobKey jobKey; @@ -24,10 +25,12 @@ public class OneTimeJobListener implements JobListener { } @Override - public void jobToBeExecuted(JobExecutionContext context) {} + public void jobToBeExecuted(JobExecutionContext context) { + } @Override - public void jobExecutionVetoed(JobExecutionContext context) {} + public void jobExecutionVetoed(JobExecutionContext context) { + } @Override public void jobWasExecuted(JobExecutionContext context, JobExecutionException jobException) { diff --git a/src/main/java/net/geant/nmaas/scheduling/ScheduleManager.java b/src/main/java/net/geant/nmaas/scheduling/ScheduleManager.java index bea3ad79324a45e30ef9be8f3936c71904dfaae9..d1bb8a301e6cf19fd619cc51ecd38b5c5f312f52 100644 --- a/src/main/java/net/geant/nmaas/scheduling/ScheduleManager.java +++ b/src/main/java/net/geant/nmaas/scheduling/ScheduleManager.java @@ -2,11 +2,9 @@ package net.geant.nmaas.scheduling; import com.google.common.collect.ImmutableSet; import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; import lombok.extern.slf4j.Slf4j; import net.geant.nmaas.monitor.MonitorService; import net.geant.nmaas.monitor.model.MonitorEntryView; -import net.geant.nmaas.orchestration.jobs.WebhookJob; import org.quartz.Job; import org.quartz.JobDataMap; import org.quartz.JobDetail; @@ -45,13 +43,13 @@ public class ScheduleManager { JobDescriptor jobDescriptor = new JobDescriptor(monitorEntryView.getServiceName(), monitorEntryView.getCheckInterval(), monitorEntryView.getTimeFormat()); validateJobDescriptor(jobDescriptor); try { - if (scheduler.checkExists(jobKey(jobDescriptor.getServiceName().getName()))) { - log.error("Job with name {} already exists", jobDescriptor.getServiceName()); - throw new IllegalStateException(String.format("Job with name %s already exists", jobDescriptor.getServiceName())); + if (scheduler.checkExists(jobKey(jobDescriptor.serviceName().getName()))) { + log.error("Job with name {} already exists", jobDescriptor.serviceName()); + throw new IllegalStateException(String.format("Job with name %s already exists", jobDescriptor.serviceName())); } else { - JobDetail jobDetail = newJob(service.getClass()).withIdentity(jobDescriptor.getServiceName().getName()).build(); + JobDetail jobDetail = newJob(service.getClass()).withIdentity(jobDescriptor.serviceName().getName()).build(); Trigger trigger = jobDescriptor.buildTrigger(); - log.info("Scheduling job: {}", jobDescriptor.getServiceName().toString()); + log.info("Scheduling job: {}", jobDescriptor.serviceName().toString()); scheduler.scheduleJob(jobDetail, ImmutableSet.of(trigger), false); } } catch (SchedulerException e) { @@ -80,10 +78,10 @@ public class ScheduleManager { JobDescriptor jobDescriptor = new JobDescriptor(monitorEntryView.getServiceName(), monitorEntryView.getCheckInterval(), monitorEntryView.getTimeFormat()); validateJobDescriptor(jobDescriptor); try{ - Trigger trigger = scheduler.getTrigger(TriggerKey.triggerKey(jobDescriptor.getServiceName().getName())); + Trigger trigger = scheduler.getTrigger(TriggerKey.triggerKey(jobDescriptor.serviceName().getName())); if (trigger != null) { trigger = jobDescriptor.buildTrigger(); - scheduler.rescheduleJob(TriggerKey.triggerKey(jobDescriptor.getServiceName().getName()), trigger); + scheduler.rescheduleJob(TriggerKey.triggerKey(jobDescriptor.serviceName().getName()), trigger); if(!monitorEntryView.isActive()){ this.pauseJob(trigger.getJobKey().getName()); } @@ -149,9 +147,9 @@ public class ScheduleManager { } private void validateJobDescriptor(JobDescriptor jobDescriptor){ - if(jobDescriptor.getServiceName() == null) + if(jobDescriptor.serviceName() == null) throw new IllegalStateException("Service name cannot be null"); - if(jobDescriptor.getCheckInterval() == null || jobDescriptor.getCheckInterval() <= 0) + if(jobDescriptor.checkInterval() == null || jobDescriptor.checkInterval() <= 0) throw new IllegalStateException("Check interval cannot be less or equal 0"); } diff --git a/src/main/java/net/geant/nmaas/scheduling/TriggerDescriptor.java b/src/main/java/net/geant/nmaas/scheduling/TriggerDescriptor.java index 6a46fa6ffaaefee0e1f91375e0963993ceab1b09..4cd35788293b43d481bd60995e87443bfc0c28d3 100644 --- a/src/main/java/net/geant/nmaas/scheduling/TriggerDescriptor.java +++ b/src/main/java/net/geant/nmaas/scheduling/TriggerDescriptor.java @@ -28,22 +28,22 @@ public class TriggerDescriptor { private String createCronString() { if (timeFormat.equals(TimeFormat.H) && checkInterval.equals(24L)) { return "0 0 0 * * ?"; - } else if(timeFormat.equals(TimeFormat.H)) { + } else if (timeFormat.equals(TimeFormat.H)) { return String.format("0 0 0/%d * * ?", checkInterval); } return String.format("0 0/%d * * * ?", checkInterval); } Trigger buildTrigger() { - if(cron != null && !cron.isEmpty()) { - if(!isValidExpression(cron)) { + if (cron != null && !cron.isEmpty()) { + if (!isValidExpression(cron)) { throw new IllegalStateException("Provided interval is incorrect. You can choose from 1-59 minutes and 1-24 hours."); } return newTrigger() .withIdentity(serviceName.getName()) .withSchedule(cronSchedule(cron) - .withMisfireHandlingInstructionFireAndProceed() - .inTimeZone(TimeZone.getTimeZone(ZoneId.systemDefault()))) + .withMisfireHandlingInstructionFireAndProceed() + .inTimeZone(TimeZone.getTimeZone(ZoneId.systemDefault()))) .usingJobData("cron", cron) .build(); } diff --git a/src/test/java/net/geant/nmaas/externalservices/kubernetes/ClusterServiceTest.java b/src/test/java/net/geant/nmaas/externalservices/kubernetes/ClusterServiceTest.java index 817e7ec0b67c1c8d06117eb06012f88b3ee62112..3900192a944632388da86024f2e3b3f8e435c3ff 100644 --- a/src/test/java/net/geant/nmaas/externalservices/kubernetes/ClusterServiceTest.java +++ b/src/test/java/net/geant/nmaas/externalservices/kubernetes/ClusterServiceTest.java @@ -3,11 +3,7 @@ package net.geant.nmaas.externalservices.kubernetes; import net.geant.nmaas.externalservices.kubernetes.api.model.RemoteClusterView; import net.geant.nmaas.externalservices.kubernetes.entities.KCluster; import net.geant.nmaas.externalservices.kubernetes.repositories.KClusterRepository; -import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.MockitoAnnotations; import org.modelmapper.ModelMapper; import java.util.List; @@ -15,51 +11,41 @@ import java.util.Optional; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.mock; import static org.mockito.Mockito.times; import static org.mockito.Mockito.verify; import static org.mockito.Mockito.when; class ClusterServiceTest { - @Mock - private KClusterRepository KClusterRepository; - - @Mock - private KubernetesClusterIngressManager kClusterIngressManager; - - @Mock - private KubernetesClusterDeploymentManager kClusterDeploymentManager; - - @InjectMocks - private RemoteClusterManager remoteClusterManager; - + private final KClusterRepository kClusterRepository = mock(KClusterRepository.class); + private final KubernetesClusterIngressManager kClusterIngressManager = mock(KubernetesClusterIngressManager.class); + private final KubernetesClusterDeploymentManager kClusterDeploymentManager = mock(KubernetesClusterDeploymentManager.class); private final ModelMapper modelMapper = new ModelMapper(); - @BeforeEach - void setUp() { - MockitoAnnotations.openMocks(this); - } + private final RemoteClusterManager remoteClusterManager = new RemoteClusterManager( + kClusterRepository, kClusterIngressManager, kClusterDeploymentManager, null, null, modelMapper); @Test void getClusterView_validId_returnsRemoteClusterView() { Long id = 1L; KCluster remoteCluster = KCluster.builder().id(id).name("Cluster").description("Description").build(); RemoteClusterView remoteClusterView = modelMapper.map(remoteCluster, RemoteClusterView.class); - when(KClusterRepository.findById(id)).thenReturn(Optional.of(remoteCluster)); + when(kClusterRepository.findById(id)).thenReturn(Optional.of(remoteCluster)); RemoteClusterView result = remoteClusterManager.getClusterView(id); assertEquals(remoteClusterView.getName(), result.getName()); - verify(KClusterRepository, times(1)).findById(id); + verify(kClusterRepository, times(1)).findById(id); } @Test void getClusterView_invalidId_throwsException() { Long id = 100L; - when(KClusterRepository.findById(id)).thenReturn(Optional.empty()); + when(kClusterRepository.findById(id)).thenReturn(Optional.empty()); assertThrows(IllegalArgumentException.class, () -> remoteClusterManager.getClusterView(id)); - verify(KClusterRepository, times(1)).findById(id); + verify(kClusterRepository, times(1)).findById(id); } @Test @@ -67,12 +53,12 @@ class ClusterServiceTest { KCluster cluster1 = KCluster.builder().id(1L).name("Cluster1").build(); KCluster cluster2 = KCluster.builder().id(2L).name("Cluster2").build(); - when(KClusterRepository.findAll()).thenReturn(List.of(cluster1, cluster2)); + when(kClusterRepository.findAll()).thenReturn(List.of(cluster1, cluster2)); List<RemoteClusterView> result = remoteClusterManager.getAllClusterView(); assertEquals(2, result.size()); - verify(KClusterRepository, times(1)).findAll(); + verify(kClusterRepository, times(1)).findAll(); } // @Test diff --git a/src/test/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/components/helm/HelmCommandExecutorTest.java b/src/test/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/components/helm/HelmCommandExecutorTest.java index e96d14b86a73f90cdd51edecd8e2759898a5a549..d9b0c8479e07ca3d17c2ee27fbb78d7dad11d15e 100644 --- a/src/test/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/components/helm/HelmCommandExecutorTest.java +++ b/src/test/java/net/geant/nmaas/nmservice/deployment/containerorchestrators/kubernetes/components/helm/HelmCommandExecutorTest.java @@ -8,23 +8,20 @@ import static org.hamcrest.MatcherAssert.assertThat; public class HelmCommandExecutorTest { private static final String EXAMPLE_HELM_STATUS_COMMAND_OUTPUT = - "LAST DEPLOYED: Wed Dec 6 13:48:59 2017\n" + - "NAMESPACE: default\n" + - "STATUS: DEPLOYED\n" + - "" + - "RESOURCES:" + - "==> v1/PersistentVolumeClaim" + - "NAME STATUS VOLUME CAPACITY ACCESSMODES STORAGECLASS AGE" + - "c21584cd-666c-42de-9df7-d72b7bae5aae Bound pvc-d4eb67d2-da83-11e7-bec9-5254002cd33f 1Gi RWO managed-nfs-storage 1m" + - "" + - "==> v1/Service" + - "NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE" + - "c21584cd-666c-42de-9df7-d72b7bae5aae-nmaas-oxidized 10.13.82.246 <none> 80/TCP 1m" + - "" + - "==> v1beta1/Deployment" + - "NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE" + - "c21584cd-666c-42de-9df7-d72b7bae5aae-nmaas-oxidized 1 1 1 1 1m" + - ""; + """ + LAST DEPLOYED: Wed Dec 6 13:48:59 2017 + NAMESPACE: default + STATUS: DEPLOYED + RESOURCES:\ + ==> v1/PersistentVolumeClaim\ + NAME STATUS VOLUME CAPACITY ACCESSMODES STORAGECLASS AGE\ + c21584cd-666c-42de-9df7-d72b7bae5aae Bound pvc-d4eb67d2-da83-11e7-bec9-5254002cd33f 1Gi RWO managed-nfs-storage 1m\ + ==> v1/Service\ + NAME CLUSTER-IP EXTERNAL-IP PORT(S) AGE\ + c21584cd-666c-42de-9df7-d72b7bae5aae-nmaas-oxidized 10.13.82.246 <none> 80/TCP 1m\ + ==> v1beta1/Deployment\ + NAME DESIRED CURRENT UP-TO-DATE AVAILABLE AGE\ + c21584cd-666c-42de-9df7-d72b7bae5aae-nmaas-oxidized 1 1 1 1 1m"""; private static final String EXAMPLE_HELM_STATUS_FOR_V3_COMMAND_OUTPUT = "NAME: testbastion\n" + diff --git a/src/test/java/net/geant/nmaas/portal/api/info/DashboardControllerTest.java b/src/test/java/net/geant/nmaas/portal/api/info/DashboardControllerTest.java new file mode 100644 index 0000000000000000000000000000000000000000..bc11435c61a1723aa7a7f9cb4b467e0d1e33bfa6 --- /dev/null +++ b/src/test/java/net/geant/nmaas/portal/api/info/DashboardControllerTest.java @@ -0,0 +1,68 @@ +package net.geant.nmaas.portal.api.info; + +import net.geant.nmaas.portal.api.info.DashboardView; +import net.geant.nmaas.portal.api.info.DomainDashboardView; +import net.geant.nmaas.portal.service.DashboardService; +import org.junit.jupiter.api.Test; + +import java.time.OffsetDateTime; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +public class DashboardControllerTest { + + private final DashboardService dashboardService = mock(DashboardService.class); + private final DashboardController dashboardController = new DashboardController(dashboardService); + + @Test + void shouldGetDashboardAdmin() { + OffsetDateTime start = OffsetDateTime.now().minusDays(1); + OffsetDateTime end = OffsetDateTime.now(); + DashboardView dashboardView = DashboardView.builder().domainsCount(1L).build(); + + when(dashboardService.getSystemDashboard(start, end)).thenReturn(dashboardView); + + DashboardView result = dashboardController.getDashboardAdmin(start, end); + + assertNotNull(result); + assertEquals(1L, result.getDomainsCount()); + } + + @Test + void shouldGetDashboardDomain() { + Long domainId = 1L; + DomainDashboardView domainDashboardView = DomainDashboardView.builder().build(); + + when(dashboardService.getSystemDomainDashboard(domainId)).thenReturn(domainDashboardView); + + DomainDashboardView result = dashboardController.getDashboardDomain(domainId); + + assertNotNull(result); + } + + @Test + void shouldThrowExceptionWhenStartDateIsNull() { + OffsetDateTime end = OffsetDateTime.now(); + assertThrows(IllegalArgumentException.class, () -> { + dashboardController.getDashboardAdmin(null, end); + }); + } + + @Test + void shouldThrowExceptionWhenEndDateIsNull() { + OffsetDateTime start = OffsetDateTime.now(); + assertThrows(IllegalArgumentException.class, () -> { + dashboardController.getDashboardAdmin(start, null); + }); + } + + @Test + void shouldThrowExceptionWhenStartDateAfterEndDate() { + OffsetDateTime start = OffsetDateTime.now(); + OffsetDateTime end = start.minusDays(1); + assertThrows(IllegalArgumentException.class, () -> { + dashboardController.getDashboardAdmin(start, end); + }); + } +} diff --git a/src/test/java/net/geant/nmaas/portal/api/market/AppInstanceControllerTest.java b/src/test/java/net/geant/nmaas/portal/api/market/AppInstanceControllerTest.java index 2faa5e053ba5dd25799e05cf608467f0513c0fa7..6cd0015acc4573aa191cb1c4aeb38e6971c9d33a 100644 --- a/src/test/java/net/geant/nmaas/portal/api/market/AppInstanceControllerTest.java +++ b/src/test/java/net/geant/nmaas/portal/api/market/AppInstanceControllerTest.java @@ -453,7 +453,7 @@ public class AppInstanceControllerTest { when(applicationService.findApplication(appInstanceRequest.getApplicationId())).thenReturn(Optional.of(application)); assertThrows(IllegalArgumentException.class, () -> { - this.appInstanceController.createAppInstance(appInstanceRequest, principal,domainId); + this.appInstanceController.createAppInstance(appInstanceRequest, principal,domainId, null); }); } diff --git a/src/test/java/net/geant/nmaas/portal/api/security/EncryptionServiceTest.java b/src/test/java/net/geant/nmaas/portal/api/security/EncryptionServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..f8d1e2a1010e67784795c80f1f5b7cd93e9af761 --- /dev/null +++ b/src/test/java/net/geant/nmaas/portal/api/security/EncryptionServiceTest.java @@ -0,0 +1,23 @@ +package net.geant.nmaas.portal.api.security; + +import org.junit.jupiter.api.Test; + +import java.security.GeneralSecurityException; + +import static org.assertj.core.api.Assertions.assertThat; + +public class EncryptionServiceTest { + + private static final String KEY = "nmaasplatformgn5"; + private static final String ALGORITHM = "AES/GCM/NoPadding"; + + private final EncryptionService service = new EncryptionService(KEY, ALGORITHM); + + @Test + void shouldEncryptAndDecrypt() throws GeneralSecurityException { + final String plainText = "test-content"; + String encryptedText = service.encrypt(plainText); + assertThat(plainText).isEqualTo(service.decrypt(encryptedText)); + } + +} diff --git a/src/test/java/net/geant/nmaas/portal/persistent/entity/AppRateTest.java b/src/test/java/net/geant/nmaas/portal/persistent/entity/AppRateTest.java index d356fb068d44ce69996ac709f27f892182dc4696..1f97d123f82a05c8c25d67dbc96f1461e091820d 100644 --- a/src/test/java/net/geant/nmaas/portal/persistent/entity/AppRateTest.java +++ b/src/test/java/net/geant/nmaas/portal/persistent/entity/AppRateTest.java @@ -8,7 +8,7 @@ import static org.junit.jupiter.api.Assertions.assertNotEquals; public class AppRateTest { @Test - public void shouldBeEqual() { + void shouldBeEqual() { assertEquals(new AppRate.AppRateId(1L, 2L), new AppRate.AppRateId(1L, 2L)); assertNotEquals(new AppRate.AppRateId(1L, 2L), new AppRate.AppRateId(1L, 3L)); } diff --git a/src/test/java/net/geant/nmaas/portal/persistent/entity/ApplicationSubscriptionTest.java b/src/test/java/net/geant/nmaas/portal/persistent/entity/ApplicationSubscriptionTest.java index b69b8884bf52bcbc81203d5979ddf296585697ac..676fa6173696a48377f1d2dca5c255d9be0de3c6 100644 --- a/src/test/java/net/geant/nmaas/portal/persistent/entity/ApplicationSubscriptionTest.java +++ b/src/test/java/net/geant/nmaas/portal/persistent/entity/ApplicationSubscriptionTest.java @@ -7,7 +7,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class ApplicationSubscriptionTest { @Test - public void shouldBeEqual() { + void shouldBeEqual() { assertEquals( new ApplicationSubscription( new Domain("name", "codename"), diff --git a/src/test/java/net/geant/nmaas/portal/persistent/entity/ApplicationTest.java b/src/test/java/net/geant/nmaas/portal/persistent/entity/ApplicationTest.java index ca73a44d6b83e7e495f6e0402be7013de0b78d43..be1169b203504f2b38d979864612dfa90f66a75d 100644 --- a/src/test/java/net/geant/nmaas/portal/persistent/entity/ApplicationTest.java +++ b/src/test/java/net/geant/nmaas/portal/persistent/entity/ApplicationTest.java @@ -7,7 +7,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class ApplicationTest { @Test - public void shouldBeEqual() { + void shouldBeEqual() { assertEquals(new Application(1L, "name1","testversion"), new Application(1L, "name2","testversion")); } diff --git a/src/test/java/net/geant/nmaas/portal/persistent/entity/DomainGroupTest.java b/src/test/java/net/geant/nmaas/portal/persistent/entity/DomainGroupTest.java index 30322ea6ef04f24e81033e0671e2200b5f789f7f..db982dd99547aecbf5ccdcd142e2b4c6eb2afb78 100644 --- a/src/test/java/net/geant/nmaas/portal/persistent/entity/DomainGroupTest.java +++ b/src/test/java/net/geant/nmaas/portal/persistent/entity/DomainGroupTest.java @@ -7,9 +7,10 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class DomainGroupTest { @Test - public void shouldBeEqual() { + void shouldBeEqual() { final String commonName = "name"; final String commonCodeName = "codename"; assertEquals(new DomainGroup(commonName, commonCodeName), new DomainGroup(commonName, commonCodeName)); } + } diff --git a/src/test/java/net/geant/nmaas/portal/persistent/entity/DomainTest.java b/src/test/java/net/geant/nmaas/portal/persistent/entity/DomainTest.java index 628a7313001909afa2e8f9c4fc8ca5e6c343ac0e..1f2f685cf3bae96ec75f3eb0a73790646e3c61a7 100644 --- a/src/test/java/net/geant/nmaas/portal/persistent/entity/DomainTest.java +++ b/src/test/java/net/geant/nmaas/portal/persistent/entity/DomainTest.java @@ -7,11 +7,11 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class DomainTest { @Test - public void shouldBeEqual() { + void shouldBeEqual() { final String commonName = "name"; final String commonCodeName = "codename"; assertEquals(new Domain(commonName, commonCodeName), new Domain(commonName, commonCodeName)); assertEquals(new Domain(1L, commonName, commonCodeName), new Domain(2L, commonName, commonCodeName)); } -} +} \ No newline at end of file diff --git a/src/test/java/net/geant/nmaas/portal/persistent/entity/InternationalizationTest.java b/src/test/java/net/geant/nmaas/portal/persistent/entity/InternationalizationTest.java index 83911122a5462c6b6e0a206888321d55d32d35a5..f0868eeb87dd9c68054c6a2a7d3f34b87cecfcbf 100644 --- a/src/test/java/net/geant/nmaas/portal/persistent/entity/InternationalizationTest.java +++ b/src/test/java/net/geant/nmaas/portal/persistent/entity/InternationalizationTest.java @@ -9,6 +9,7 @@ import org.junit.platform.commons.util.StringUtils; import java.io.IOException; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.fail; @@ -19,52 +20,52 @@ public class InternationalizationTest { private InternationalizationSimple internationalizationSimple; private static final String content = "{\n" + - " \"NAVBAR\": {\n" + - " \"MARKET\": \"Applications\",\n" + - " \"SUBSCRIPTIONS\": \"Subscriptions\",\n" + - " \"INSTANCES\": \"Instances\",\n" + - " \"MANAGEMENT\": \"Management\",\n" + - " \"DOMAINS\": \"Domains\",\n" + - " \"USERS\": \"Users\",\n" + - " \"INVENTORY\": \"External components\",\n" + - " \"MONITOR\": \"Monitoring\",\n" + - " \"SETTINGS\": \"Settings\",\n" + - " \"PROFILE\": \"Profile\",\n" + - " \"LOGOUT\": \"Logout\",\n" + - " \"LOGIN_REGISTER\": \"Login | Register\",\n" + - " \"LANGUAGE\": \"Language\",\n" + - " \"ABOUT\": \"About\",\n" + - " \"BACK\": \"Back to Home Page\",\n" + - " \"LANGUAGES\": \"Languages\"\n" + - " }\n" + - "}"; + " \"NAVBAR\": {\n" + + " \"MARKET\": \"Applications\",\n" + + " \"SUBSCRIPTIONS\": \"Subscriptions\",\n" + + " \"INSTANCES\": \"Instances\",\n" + + " \"MANAGEMENT\": \"Management\",\n" + + " \"DOMAINS\": \"Domains\",\n" + + " \"USERS\": \"Users\",\n" + + " \"INVENTORY\": \"External components\",\n" + + " \"MONITOR\": \"Monitoring\",\n" + + " \"SETTINGS\": \"Settings\",\n" + + " \"PROFILE\": \"Profile\",\n" + + " \"LOGOUT\": \"Logout\",\n" + + " \"LOGIN_REGISTER\": \"Login | Register\",\n" + + " \"LANGUAGE\": \"Language\",\n" + + " \"ABOUT\": \"About\",\n" + + " \"BACK\": \"Back to Home Page\",\n" + + " \"LANGUAGES\": \"Languages\"\n" + + " }\n" + + "}"; @BeforeEach - public void setup(){ + void setup() { this.internationalization = new InternationalizationView("english", true, content); } @Test - public void internationalizationShouldDeserializeToInternationalizationSimple(){ + void internationalizationShouldDeserializeToInternationalizationSimple() { this.internationalizationSimple = this.internationalization.getAsInternationalizationSimple(); assertTrue(StringUtils.isNotBlank(internationalization.getContent())); // assertEquals(internationalization.getId(), internationalizationSimple.getId()); assertEquals(internationalization.getLanguage(), internationalizationSimple.getLanguage()); assertEquals(internationalization.isEnabled(), internationalizationSimple.isEnabled()); - assertTrue(internationalizationSimple.getLanguageNodes().size() > 0); + assertFalse(internationalizationSimple.getLanguageNodes().isEmpty()); } @Test - public void InternationalizationSimpleShouldSerializeToInternationalization(){ + void InternationalizationSimpleShouldSerializeToInternationalization() { this.internationalizationSimple = this.internationalization.getAsInternationalizationSimple(); InternationalizationView test = this.internationalizationSimple.getAsInternationalizationView(); // assertEquals(internationalization.getId(), test.getId()); assertEquals(internationalization.getLanguage(), test.getLanguage()); assertEquals(internationalization.isEnabled(), test.isEnabled()); ObjectMapper om = new ObjectMapper(); - try{ + try { assertEquals(om.readTree(internationalization.getContent()), om.readTree(test.getContent())); - } catch (IOException ioe){ + } catch (IOException ioe) { fail(); } } diff --git a/src/test/java/net/geant/nmaas/portal/persistent/entity/SSHKeyEntityTest.java b/src/test/java/net/geant/nmaas/portal/persistent/entity/SSHKeyEntityTest.java index 75a508c1807b70ce76b37a16a02f1cf42ca70fde..6a99077dbbe39e9df6da0c504c1350c875923f8e 100644 --- a/src/test/java/net/geant/nmaas/portal/persistent/entity/SSHKeyEntityTest.java +++ b/src/test/java/net/geant/nmaas/portal/persistent/entity/SSHKeyEntityTest.java @@ -5,6 +5,7 @@ import org.junit.jupiter.api.Test; import static org.junit.jupiter.api.Assertions.assertEquals; public class SSHKeyEntityTest { + // ubuntu 18.04 LTS // https://stackoverflow.com/questions/9607295/calculate-rsa-key-fingerprint // ssh-keygen -t rsa -b 4096 diff --git a/src/test/java/net/geant/nmaas/portal/persistent/entity/UserRoleTest.java b/src/test/java/net/geant/nmaas/portal/persistent/entity/UserRoleTest.java index f79d9782a56fadff70129f47eaf5ed01b78b5614..51f32630ed549e7357acb22a9a5f0e2e0fa41301 100644 --- a/src/test/java/net/geant/nmaas/portal/persistent/entity/UserRoleTest.java +++ b/src/test/java/net/geant/nmaas/portal/persistent/entity/UserRoleTest.java @@ -11,13 +11,13 @@ public class UserRoleTest { final private Role role = Role.ROLE_DOMAIN_ADMIN; @Test - public void shouldBeEqual() { + void shouldBeEqual() { assertEquals(new UserRole(user, domain, role).getId(), new UserRole(new User("username"), new Domain(1L, "name", "codename"), Role.ROLE_DOMAIN_ADMIN).getId()); } @Test - public void shouldGetCorrectAuthorityString() { + void shouldGetCorrectAuthorityString() { UserRole userRole = new UserRole(user, domain, role); assertEquals("1:ROLE_DOMAIN_ADMIN", userRole.getAuthority()); } diff --git a/src/test/java/net/geant/nmaas/portal/persistent/entity/UserTest.java b/src/test/java/net/geant/nmaas/portal/persistent/entity/UserTest.java index e4042848692ec06ec1c6e5fc023a195dfef74522..a359592ee8e6c5dd3ca79c0c46196ebb9d8783e5 100644 --- a/src/test/java/net/geant/nmaas/portal/persistent/entity/UserTest.java +++ b/src/test/java/net/geant/nmaas/portal/persistent/entity/UserTest.java @@ -7,7 +7,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; public class UserTest { @Test - public void shouldBeEqual() { + void shouldBeEqual() { final String commonUsername = "username"; assertEquals(new User(commonUsername), new User(commonUsername)); assertEquals(new User(1L, commonUsername, true, new Domain(), Role.ROLE_GUEST), diff --git a/src/test/java/net/geant/nmaas/portal/service/impl/DashboardServiceImplTest.java b/src/test/java/net/geant/nmaas/portal/service/impl/DashboardServiceImplTest.java index 7cbd86f0dab7037c5e5ead9f1ed0bf7a8ad4ad32..ae02141d1f25687a9ab34e683cac3d4ba49930cb 100644 --- a/src/test/java/net/geant/nmaas/portal/service/impl/DashboardServiceImplTest.java +++ b/src/test/java/net/geant/nmaas/portal/service/impl/DashboardServiceImplTest.java @@ -15,6 +15,7 @@ import net.geant.nmaas.portal.service.DomainService; import net.geant.nmaas.portal.service.UserLoginRegisterService; import net.geant.nmaas.portal.service.UserService; import org.junit.jupiter.api.Test; +import org.mockito.ArgumentCaptor; import java.time.OffsetDateTime; import java.util.Collections; @@ -49,11 +50,13 @@ public class DashboardServiceImplTest { userLoginRegisterService ); - @Test + @Test void getSystemDashboardShouldThrowExceptionWhenRepositoryFails() { when(domainRepository.count()).thenThrow(new RuntimeException("Database error")); - assertThrows(RuntimeException.class, dashboardService::getSystemDashboard); + assertThrows(RuntimeException.class, () -> + dashboardService.getSystemDashboard(OffsetDateTime.now().minusDays(1), OffsetDateTime.now()) + ); } @Test @@ -118,9 +121,41 @@ public class DashboardServiceImplTest { when(appInstanceRepo.countAllDeployedSinceTime(anyLong())).thenReturn((int) 3L); when(applicationBaseRepository.findAllNames()).thenReturn(Collections.emptyList()); - DashboardView result = dashboardService.getSystemDashboard(); + DashboardView result = dashboardService.getSystemDashboard(OffsetDateTime.now().minusDays(1), OffsetDateTime.now()); assert result != null; assert result.getPopularApps().isEmpty(); } + + @Test +void getSystemDashboardShouldCalculateCorrectTimestamps() { + OffsetDateTime startDate = OffsetDateTime.now().minusHours(5); + OffsetDateTime endDate = OffsetDateTime.now(); + + // Mock required repository methods + when(domainRepository.count()).thenReturn(1L); + when(userRepository.count()).thenReturn(1L); + when(appInstanceRepo.count()).thenReturn(1L); + when(appInstanceRepo.countAllDeployedSinceTime(org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyLong())).thenReturn(1); + when(applicationBaseRepository.findAllNames()).thenReturn(Collections.emptyList()); + when(appInstanceRepo.findAllInTimePeriod(org.mockito.ArgumentMatchers.anyLong(), org.mockito.ArgumentMatchers.anyLong())).thenReturn(Collections.emptyList()); + + // Call the method + dashboardService.getSystemDashboard(startDate, endDate); + + // Capture the arguments + ArgumentCaptor<Long> startCaptor = ArgumentCaptor.forClass(Long.class); + ArgumentCaptor<Long> endCaptor = ArgumentCaptor.forClass(Long.class); + + // Verify that the method was called with calculated timestamps + org.mockito.Mockito.verify(appInstanceRepo).countAllDeployedSinceTime(startCaptor.capture(), endCaptor.capture()); + + long startTimestamp = startCaptor.getValue(); + long endTimestamp = endCaptor.getValue(); + + // The timestamps should be positive and start should be greater than end (since it's calculated as now - toEpochSecond) + assert startTimestamp > 0; + assert endTimestamp > 0; + assert startTimestamp > endTimestamp; +} } diff --git a/src/test/java/net/geant/nmaas/portal/service/impl/DomainGroupServiceTest.java b/src/test/java/net/geant/nmaas/portal/service/impl/DomainGroupServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..2b1c864275d91a098134dccf3cf1d81e22f73751 --- /dev/null +++ b/src/test/java/net/geant/nmaas/portal/service/impl/DomainGroupServiceTest.java @@ -0,0 +1,144 @@ +package net.geant.nmaas.portal.service.impl; + +import net.geant.nmaas.orchestration.jobs.DomainGroupJob; +import net.geant.nmaas.portal.api.domain.DomainGroupView; +import net.geant.nmaas.portal.persistent.entity.DomainGroup; +import net.geant.nmaas.portal.persistent.entity.WebhookEvent; +import net.geant.nmaas.portal.persistent.entity.WebhookEventType; +import net.geant.nmaas.portal.persistent.repositories.DomainGroupRepository; +import net.geant.nmaas.portal.persistent.repositories.WebhookEventRepository; +import net.geant.nmaas.portal.service.ApplicationStatePerDomainService; +import net.geant.nmaas.portal.service.DomainGroupService; +import net.geant.nmaas.scheduling.ScheduleManager; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.modelmapper.ModelMapper; +import org.quartz.JobListener; +import org.quartz.ListenerManager; +import org.quartz.Matcher; +import org.quartz.Scheduler; +import org.quartz.SchedulerException; + +import java.util.Optional; +import java.util.stream.Stream; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.ArgumentMatchers.argThat; +import static org.mockito.Mockito.doNothing; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + +class DomainGroupServiceTest { + + DomainGroupRepository domainGroupRepository = mock(DomainGroupRepository.class); + ApplicationStatePerDomainService applicationStatePerDomainService = mock(ApplicationStatePerDomainService.class); + WebhookEventRepository webhookEventRepository = mock(WebhookEventRepository.class); + Scheduler scheduler = mock(Scheduler.class); + ListenerManager listenerManager = mock(ListenerManager.class); + ScheduleManager scheduleManager; + ModelMapper modelMapper = new ModelMapper(); + DomainGroupService domainGroupService; + + @BeforeEach + void setup() { + scheduleManager = new ScheduleManager(scheduler); + domainGroupService = new DomainGroupServiceImpl(domainGroupRepository, applicationStatePerDomainService, webhookEventRepository, scheduleManager, modelMapper); + } + + @Test + void shouldCreateDomainGroup() throws SchedulerException { + // Setup webhook event + WebhookEvent webhookEvent = new WebhookEvent(1L, "webhook", "https://example.com/webhook", WebhookEventType.DOMAIN_GROUP_CHANGE, null, null); + when(webhookEventRepository.findIdByEventType(WebhookEventType.DOMAIN_GROUP_CHANGE)) + .thenReturn(Stream.of(1L)); + when(webhookEventRepository.findById(1L)) + .thenReturn(Optional.of(webhookEvent)); + + // Setup domain group + String name = "testgroup"; + String codename = "testgrp"; + DomainGroup domainGroup = new DomainGroup(name, codename); + domainGroup.setId(10L); + when(domainGroupRepository.save(any(DomainGroup.class))).thenReturn(domainGroup); + when(domainGroupRepository.findById(10L)).thenReturn(Optional.of(domainGroup)); + when(scheduler.getListenerManager()).thenReturn(listenerManager); + doNothing().when(listenerManager).addJobListener(any(JobListener.class), any(Matcher.class)); + + // Create domain group + DomainGroupView domainGroupView = new DomainGroupView(); + domainGroupView.setName(name); + domainGroupView.setCodename(codename); + DomainGroupView result = this.domainGroupService.createDomainGroup(domainGroupView); + + // Verify webhook job was scheduled with correct parameters for creation + verify(scheduler, times(1)).scheduleJob( + argThat(jobDetail -> + jobDetail.getKey().getName().startsWith("DomainGroup_1_10_") && + jobDetail.getJobClass().equals(DomainGroupJob.class) + ), + argThat(trigger -> + trigger.getKey().getName().startsWith("DomainGroup_1_10_") + ) + ); + + // Verify domain group was created correctly + assertThat("Codenames are not the same", result.getCodename().equals(codename)); + assertThat("Names are not the same", result.getName().equals(name)); + + // Update domain group + when(webhookEventRepository.findIdByEventType(WebhookEventType.DOMAIN_GROUP_CHANGE)) + .thenReturn(Stream.of(1L)); + when(webhookEventRepository.findById(1L)) + .thenReturn(Optional.of(webhookEvent)); + domainGroupView.setCodename(codename + "2"); + domainGroupView.setId(10L); + result = this.domainGroupService.updateDomainGroup(10L, domainGroupView); + + // Verify webhook job was scheduled with correct parameters for update + verify(scheduler, times(2)).scheduleJob( + argThat(jobDetail -> + jobDetail.getKey().getName().startsWith("DomainGroup_1_10_") && + jobDetail.getJobClass().equals(DomainGroupJob.class) + ), + argThat(trigger -> + trigger.getKey().getName().startsWith("DomainGroup_1_10_") + ) + ); + + // Verify domain group was updated correctly + assertThat("Updated codenames are not the same", result.getCodename().equals(codename + "2")); + assertThat("Names are not the same after update", result.getName().equals(name)); + } + + @Test + void shouldDeleteDomainGroup() throws SchedulerException { + // Setup webhook event + WebhookEvent webhookEvent = new WebhookEvent(1L, "webhook", "https://example.com/webhook", WebhookEventType.DOMAIN_GROUP_CHANGE, null, null); + when(webhookEventRepository.findIdByEventType(WebhookEventType.DOMAIN_GROUP_CHANGE)) + .thenReturn(Stream.of(1L)); + when(webhookEventRepository.findById(1L)) + .thenReturn(Optional.of(webhookEvent)); + + DomainGroup domainGroup = new DomainGroup("testgroup", "testgrp"); + domainGroup.setId(10L); + when(domainGroupRepository.findById(10L)).thenReturn(Optional.of(domainGroup)); + when(scheduler.getListenerManager()).thenReturn(listenerManager); + doNothing().when(listenerManager).addJobListener(any(JobListener.class), any(Matcher.class)); + this.domainGroupService.deleteDomainGroup(10L); + verify(domainGroupRepository, times(1)).deleteById(10L); + + verify(scheduler, times(1)).scheduleJob( + argThat(jobDetail -> + jobDetail.getKey().getName().startsWith("DomainGroup_1_10_") && + jobDetail.getJobClass().equals(DomainGroupJob.class) + ), + argThat(trigger -> + trigger.getKey().getName().startsWith("DomainGroup_1_10_") + ) + ); + } +} diff --git a/src/test/java/net/geant/nmaas/portal/service/impl/DomainServiceTest.java b/src/test/java/net/geant/nmaas/portal/service/impl/DomainServiceTest.java index 6f9276d7ff38e95d21e9293a6cd9cad9f5308adf..5fb8cb4c800f2519d943b89133014c20531a2147 100644 --- a/src/test/java/net/geant/nmaas/portal/service/impl/DomainServiceTest.java +++ b/src/test/java/net/geant/nmaas/portal/service/impl/DomainServiceTest.java @@ -94,8 +94,6 @@ class DomainServiceTest { Scheduler scheduler = mock(Scheduler.class); ListenerManager listenerManager = mock(ListenerManager.class); ScheduleManager scheduleManager; - EncryptionService encryptionService = mock(EncryptionService.class); - WebhookEventService webhookEventService; DomainService domainService; @@ -103,15 +101,14 @@ class DomainServiceTest { void setup() { validator = new DefaultCodenameValidator("[a-z-]{2,12}"); namespaceValidator = new DefaultCodenameValidator("[a-z-]{0,64}"); - domainGroupService = new DomainGroupServiceImpl(domainGroupRepository, applicationStatePerDomainService, modelMapper); scheduleManager = new ScheduleManager( scheduler); + domainGroupService = new DomainGroupServiceImpl(domainGroupRepository, applicationStatePerDomainService, webhookEventRepository, scheduleManager, modelMapper); domainService = new DomainServiceImpl(validator, namespaceValidator, domainRepository, domainDcnDetailsRepository, domainTechDetailsRepository, userService, userRoleRepo, dcnRepositoryManager, modelMapper, applicationStatePerDomainService, domainGroupService, eventPublisher, domainAnnotationsRepository, webhookEventRepository, scheduleManager); ((DomainServiceImpl) domainService).globalDomain = "GLOBAL"; - webhookEventService = new WebhookEventService(webhookEventRepository, encryptionService, modelMapper); } @Test diff --git a/src/test/java/net/geant/nmaas/scheduling/SchedulingManagerTest.java b/src/test/java/net/geant/nmaas/scheduling/SchedulingManagerTest.java index 259997e46e8dae575a7806951a48ab661253a19a..8e7e559c057c89e7d3f6bff241d9d3588671c1c4 100644 --- a/src/test/java/net/geant/nmaas/scheduling/SchedulingManagerTest.java +++ b/src/test/java/net/geant/nmaas/scheduling/SchedulingManagerTest.java @@ -1,11 +1,11 @@ package net.geant.nmaas.scheduling; import net.geant.nmaas.gitlab.GitLabManager; -import net.geant.nmaas.monitor.targets.GitLabMonitorService; import net.geant.nmaas.monitor.MonitorManager; import net.geant.nmaas.monitor.ServiceType; import net.geant.nmaas.monitor.TimeFormat; import net.geant.nmaas.monitor.model.MonitorEntryView; +import net.geant.nmaas.monitor.targets.GitLabMonitorService; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.quartz.JobKey; @@ -33,22 +33,22 @@ public class SchedulingManagerTest { private ScheduleManager scheduleManager; @BeforeEach - public void setup() throws Exception{ + void setup() throws Exception { gitLabMonitorService = new GitLabMonitorService(); gitLabMonitorService.setGitLabManager(gitLabManager); gitLabMonitorService.setMonitorManager(monitorManager); - scheduleManager = new ScheduleManager( scheduler); + scheduleManager = new ScheduleManager(scheduler); when(scheduler.checkExists(JobKey.jobKey(ServiceType.GITLAB.getName()))).thenReturn(false); } @Test - public void shouldCreateJob() throws Exception { + void shouldCreateJob() throws Exception { this.scheduleManager.createJob(this.gitLabMonitorService, monitorEntryView); verify(scheduler, times(1)).scheduleJob(any(), anySet(), anyBoolean()); } @Test - public void shouldNotCreateJobWhenJobExists() { + void shouldNotCreateJobWhenJobExists() { assertThrows(IllegalStateException.class, () -> { when(scheduler.checkExists(JobKey.jobKey(ServiceType.GITLAB.getName()))).thenReturn(true); this.scheduleManager.createJob(this.gitLabMonitorService, monitorEntryView); @@ -56,7 +56,7 @@ public class SchedulingManagerTest { } @Test - public void shouldUpdateJob() throws Exception { + void shouldUpdateJob() throws Exception { JobDescriptor jobDescriptor = new JobDescriptor(ServiceType.GITLAB, 3L, TimeFormat.MIN); when(scheduler.getTrigger(TriggerKey.triggerKey(ServiceType.GITLAB.getName()))).thenReturn(jobDescriptor.buildTrigger()); this.scheduleManager.updateJob(monitorEntryView); @@ -64,7 +64,7 @@ public class SchedulingManagerTest { } @Test - public void shouldNotUpdate() throws Exception { + void shouldNotUpdate() throws Exception { when(scheduler.getTrigger(TriggerKey.triggerKey(ServiceType.GITLAB.getName()))).thenReturn(null); this.scheduleManager.updateJob(monitorEntryView); verify(scheduler, times(0)).rescheduleJob(any(), any()); diff --git a/src/test/shell/data/i18n/de.json b/src/test/shell/data/i18n/de.json index 4d8597ce9478703a17493293fef4a55df1d87057..3a07ad01ea518de6b8fadbe92c36b89fe0cf5d69 100644 --- a/src/test/shell/data/i18n/de.json +++ b/src/test/shell/data/i18n/de.json @@ -192,7 +192,8 @@ "DOWN" : "Down", "UNKNOWN" : "Unknown", "DETAILS" : "Details", - "STATE_SINCE" : "State last change" + "STATE_SINCE" : "State last change", + "CONTACT_MAIL" : "Contact mail" }, "GITLAB": { "TITLE": "GitLab Konfiguration", @@ -514,7 +515,9 @@ "DEPLOY_BUTTON": "Installieren", "USE_DEFAULT_VERSION": "Standardversion verwenden", "SELECT_VERSION": "Version auswählen", - "AUTO_UPGRADES_ENABLED": "Automatische upgrades aktivieren" + "AUTO_UPGRADES_ENABLED": "Automatische upgrades aktivieren", + "REMOTE_CLUSTER" : "Use remote cluster", + "SELECT_CLUSTER": "Remote location" }, "ADD_MEMBERS_MODAL": { "HEADER": "Add members", @@ -1307,5 +1310,21 @@ }, "SHARED" : { "TOGGLE" : "Toggle all" + }, + "WEBHOOKS" : { + "TITLE" : "Webhooks settings", + "TITLE_SHORT" : "Webhooks", + "ID" : "Id", + "NAME" : "Name", + "TARGET_URL" : "Target Url", + "TYPE" : "Webhook type", + "TOKEN" : "Token value", + "AUTH" : "Authorization header", + "DOMAIN_CREATION" : "Domain creation", + "APPLICATION_DEPLOYMENT" : "Application deployment", + "USER_ASSIGNMENT" : "User assignment", + "DOMAIN_GROUP_CHANGE" : "Domain group change", + "NEW" : "Add webhook", + "DETAILS" : "Webhooks details" } } diff --git a/src/test/shell/data/i18n/en.json b/src/test/shell/data/i18n/en.json index e2aea66b117a82b6da5343dadcc94354ebcae2c1..6484fdeaec15e61cf86d41d39c73b4bec33e264e 100644 --- a/src/test/shell/data/i18n/en.json +++ b/src/test/shell/data/i18n/en.json @@ -192,6 +192,7 @@ "UP" : "Up", "DOWN" : "Down", "UNKNOWN" : "Unknown", + "CONTACT_MAIL" : "Contact mail", "DETAILS" : "Details", "STATE_SINCE" : "State last change" }, @@ -516,7 +517,9 @@ "DEPLOY_BUTTON": "Deploy", "USE_DEFAULT_VERSION": "Use default version", "SELECT_VERSION": "Select version", - "AUTO_UPGRADES_ENABLED": "Enable automatic upgrades" + "AUTO_UPGRADES_ENABLED": "Enable automatic upgrades", + "REMOTE_CLUSTER" : "Use remote cluster", + "SELECT_CLUSTER": "Remote location" }, "ADD_MEMBERS_MODAL": { "HEADER": "Add members", @@ -1312,5 +1315,21 @@ }, "SHARED" : { "TOGGLE" : "Toggle all" + }, + "WEBHOOKS" : { + "TITLE" : "Webhooks settings", + "TITLE_SHORT" : "Webhooks", + "ID" : "Id", + "NAME" : "Name", + "TARGET_URL" : "Target Url", + "TYPE" : "Webhook type", + "TOKEN" : "Token value", + "AUTH" : "Authorization header", + "DOMAIN_CREATION" : "Domain creation", + "APPLICATION_DEPLOYMENT" : "Application deployment", + "USER_ASSIGNMENT" : "User assignment", + "DOMAIN_GROUP_CHANGE" : "Domain group change", + "NEW" : "Add webhook", + "DETAILS" : "Webhooks details" } } diff --git a/src/test/shell/data/i18n/fr.json b/src/test/shell/data/i18n/fr.json index 5051d97030f59fd51f7f4e1848de3bb1f27c4801..449bdc580a1e0151fc743896e5d7a56e21695968 100644 --- a/src/test/shell/data/i18n/fr.json +++ b/src/test/shell/data/i18n/fr.json @@ -193,6 +193,7 @@ "UP" : "Up", "DOWN" : "Down", "UNKNOWN" : "Unknown", + "CONTACT_MAIL" : "Contact mail", "DETAILS" : "Details", "STATE_SINCE" : "State last change" }, @@ -516,7 +517,9 @@ "DEPLOY_BUTTON": "Déployer", "USE_DEFAULT_VERSION": "Utiliser la version par défaut", "SELECT_VERSION": "Sélectionnez la version", - "AUTO_UPGRADES_ENABLED": "Activer les mises à jour automatiques" + "AUTO_UPGRADES_ENABLED": "Activer les mises à jour automatiques", + "REMOTE_CLUSTER" : "Use remote cluster", + "SELECT_CLUSTER": "Remote location" }, "ADD_MEMBERS_MODAL": { "HEADER": "Add members", @@ -1311,5 +1314,21 @@ }, "SHARED" : { "TOGGLE" : "Toggle all" + }, + "WEBHOOKS" : { + "TITLE" : "Webhooks settings", + "TITLE_SHORT" : "Webhooks", + "ID" : "Id", + "NAME" : "Name", + "TARGET_URL" : "Target Url", + "TYPE" : "Webhook type", + "TOKEN" : "Token value", + "AUTH" : "Authorization header", + "DOMAIN_CREATION" : "Domain creation", + "APPLICATION_DEPLOYMENT" : "Application deployment", + "USER_ASSIGNMENT" : "User assignment", + "DOMAIN_GROUP_CHANGE" : "Domain group change", + "NEW" : "Add webhook", + "DETAILS" : "Webhooks details" } } diff --git a/src/test/shell/data/i18n/pl.json b/src/test/shell/data/i18n/pl.json index f3e91fadca23a09c96a470ee314048e333909297..ae51c715ddda44898afc023c818db0fb15c47109 100644 --- a/src/test/shell/data/i18n/pl.json +++ b/src/test/shell/data/i18n/pl.json @@ -192,6 +192,7 @@ "UP" : "Aktywny", "DOWN" : "Nieaktywny", "UNKNOWN" : "Nieznany", + "CONTACT_MAIL" : "Email kontaktowy", "DETAILS" : "Detale", "STATE_SINCE" : "Stan od" }, @@ -515,7 +516,9 @@ "DEPLOY_BUTTON": "Uruchom", "USE_DEFAULT_VERSION": "Użyj domyślnej wersji", "SELECT_VERSION": "Wybierz wersję", - "AUTO_UPGRADES_ENABLED": "Włącz automatyczne aktualizacje" + "AUTO_UPGRADES_ENABLED": "Włącz automatyczne aktualizacje", + "REMOTE_CLUSTER" : "Uruchomienie na zewnęntrzym klastrze", + "SELECT_CLUSTER": "Zdalna lokalizacja" }, "ADD_MEMBERS_MODAL": { "HEADER": "Dodaj użytkowników", @@ -1310,5 +1313,21 @@ }, "SHARED" : { "TOGGLE" : "Toggle all" + }, + "WEBHOOKS" : { + "TITLE" : "Webhooks settings", + "TITLE_SHORT" : "Webhooks", + "ID" : "Id", + "NAME" : "Name", + "TARGET_URL" : "Target Url", + "TYPE" : "Webhook type", + "TOKEN" : "Token value", + "AUTH" : "Authorization header", + "DOMAIN_CREATION" : "Domain creation", + "APPLICATION_DEPLOYMENT" : "Application deployment", + "USER_ASSIGNMENT" : "User assignment", + "DOMAIN_GROUP_CHANGE" : "Domain group change", + "NEW" : "Add webhook", + "DETAILS" : "Webhooks details" } } diff --git a/src/test/shell/data/mails/clusterSupportEmail.json b/src/test/shell/data/mails/clusterSupportEmail.json index 192f7daa3413adb05771036fc34018fec0516938..91d233386a0b4af00a7ac227b6fc87b0d17db805 100644 --- a/src/test/shell/data/mails/clusterSupportEmail.json +++ b/src/test/shell/data/mails/clusterSupportEmail.json @@ -8,10 +8,10 @@ "templates": [ { "language": "en", - "subject": "nmaas: Cluster support", + "subject": "nmaas: Remote 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 please contact administration for further information.</p>", + "CONTENT": "<p>Your email address was set as a support contact for remote cluster <b>${clusterCodename}</b> added to nmaas.</p> <p>If you do not agree or consider this as a mistake please contact the nmaas administrator for further information.</p>", "SENDER": "Best regards,<br />nmaas Team", "NOREPLY": "This is an automatically generated message, please do not reply.", "SENDER_POLICY": "" @@ -19,11 +19,10 @@ }, { "language": "fr", - "subject": "nmaas: Cluster is unavailable", + "subject": "nmaas: Remote 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 please contact administration for further information.</p>", - + "CONTENT": "<p>Your email address was set as a support contact for remote cluster <b>${clusterCodename}</b> added to nmaas.</p> <p>If you do not agree or consider this as a mistake please contact the nmaas administrator for further information.</p>", "SENDER": "Best regards,<br />nmaas Team", "NOREPLY": "This is an automatically generated message, please do not reply.", "SENDER_POLICY": "" @@ -31,10 +30,10 @@ }, { "language": "de", - "subject": "nmaas: Cluster is unavailable", + "subject": "nmaas: Remote 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 please contact administration for further information.</p>", + "CONTENT": "<p>Your email address was set as a support contact for remote cluster <b>${clusterCodename}</b> added to nmaas.</p> <p>If you do not agree or consider this as a mistake please contact the nmaas administrator for further information.</p>", "SENDER": "Best regards,<br />nmaas Team", "NOREPLY": "This is an automatically generated message, please do not reply.", "SENDER_POLICY": "" @@ -42,10 +41,10 @@ }, { "language": "pl", - "subject": "nmaas: Cluster jest niedostępny", + "subject": "nmaas: Wsparcie zdalnego klastra", "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 please contact administration for further information.</p>", + "CONTENT": "<p>Twój adres e-mail został ustawiony jako kontakt pomocy technicznej dla zdalnego klastra <b>${clusterCodename}</b> dodanego do nmaas.</p> <p>Jeśli się z tym nie zgadzasz lub uważasz to za pomyłkę, skontaktuj się z administratorem nmaas, aby uzyskać dalsze informacje.</p>", "SENDER": "Z pozdrowieniami,<br />Zespół nmaas", "NOREPLY": "Ta wiadomość została wygenerowana automatycznie.", "SENDER_POLICY": "" diff --git a/src/test/shell/data/mails/clusterUnavailable.json b/src/test/shell/data/mails/clusterUnavailable.json index b126a6d6fbd55f29d996e7dd69e5ef8f7757cbf3..b6d76247d4f8e11fbfbe94218a1b5b70fae932b1 100644 --- a/src/test/shell/data/mails/clusterUnavailable.json +++ b/src/test/shell/data/mails/clusterUnavailable.json @@ -8,10 +8,10 @@ "templates": [ { "language": "en", - "subject": "nmaas: Cluster is unavailable", + "subject": "nmaas: Remote 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>Internal nmaas monitoring indicates that cluster <b>${clusterCodename}</b> became unavailable.</p> <p><b> Cluster ID:<b> ${clusterId}</p> <p><b> Cluster CodeName:<b> ${clusterCodename}</p> <p>Please contact the nmaas administrator for further information.</p>", "SENDER": "Best regards,<br />nmaas Team", "NOREPLY": "This is an automatically generated message, please do not reply.", "SENDER_POLICY": "" @@ -19,11 +19,10 @@ }, { "language": "fr", - "subject": "nmaas: Cluster is unavailable", + "subject": "nmaas: Remote 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>Internal nmaas monitoring indicates that cluster <b>${clusterCodename}</b> became unavailable.</p> <p><b> Cluster ID:<b> ${clusterId}</p> <p><b> Cluster CodeName:<b> ${clusterCodename}</p> <p>Please contact the nmaas administrator for further information.</p>", "SENDER": "Best regards,<br />nmaas Team", "NOREPLY": "This is an automatically generated message, please do not reply.", "SENDER_POLICY": "" @@ -31,10 +30,10 @@ }, { "language": "de", - "subject": "nmaas: Cluster is unavailable", + "subject": "nmaas: Remote 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>Internal nmaas monitoring indicates that cluster <b>${clusterCodename}</b> became unavailable.</p> <p><b> Cluster ID:<b> ${clusterId}</p> <p><b> Cluster CodeName:<b> ${clusterCodename}</p> <p>Please contact the nmaas administrator for further information.</p>", "SENDER": "Best regards,<br />nmaas Team", "NOREPLY": "This is an automatically generated message, please do not reply.", "SENDER_POLICY": "" @@ -42,10 +41,10 @@ }, { "language": "pl", - "subject": "nmaas: Cluster jest niedostępny", + "subject": "nmaas: Zdalny klaster 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> <i>Please contact the administration for further information. </i></p>", + "CONTENT": "<p>Wewnętrzny monitoring nmaas wskazuje, że klaster <b>${clusterCodename</b> stał się niedostępny.</p> <p><b> Identyfikator klastra:<b> ${clusterId</p> <p><b> Nazwa kodowa klastra:<b> ${clusterCodename</p> <p>Aby uzyskać więcej informacji, skontaktuj się z administratorem nmaas.</p>", "SENDER": "Z pozdrowieniami,<br />Zespół nmaas", "NOREPLY": "Ta wiadomość została wygenerowana automatycznie.", "SENDER_POLICY": ""