Skip to content
Snippets Groups Projects
Commit 76a166a6 authored by kbeyro's avatar kbeyro
Browse files

Merge branch 'develop' into update-dashboard

parents 64e5e03f 88eae440
Branches
No related tags found
2 merge requests!273Release 1.8.0 update,!247Resolve "Revisit paging mechanism for endpoints providing data for table/list views"
Pipeline #94897 failed
Showing
with 303 additions and 209 deletions
......@@ -111,7 +111,7 @@ dependencies {
runtimeOnly('org.postgresql:postgresql')
// Database audit trail
implementation 'org.hibernate.orm:hibernate-envers:6.6.17.Final'
implementation 'org.hibernate.orm:hibernate-envers:6.6.18.Final'
// Database migrations
implementation('org.flywaydb:flyway-core:11.9.1')
......
package net.geant.nmaas.nmservice.deployment;
import net.geant.nmaas.externalservices.kubernetes.RemoteClusterManager;
import net.geant.nmaas.externalservices.kubernetes.RemoteClusterMonitoringService;
import net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes.KubernetesRepositoryManager;
import net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes.entities.*;
import net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes.entities.HelmChartRepositoryEmbeddable;
import net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes.entities.KubernetesChart;
import net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes.entities.KubernetesNmServiceInfo;
import net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes.entities.KubernetesTemplate;
import net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes.entities.ServiceAccessMethodType;
import net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes.entities.ServiceStorageVolumeType;
import net.geant.nmaas.nmservice.deployment.exceptions.NmServiceRequestVerificationException;
import net.geant.nmaas.orchestration.Identifier;
import net.geant.nmaas.orchestration.entities.AppAccessMethod;
......@@ -15,6 +21,7 @@ import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.bean.override.mockito.MockitoBean;
import org.springframework.test.context.junit.jupiter.SpringExtension;
import java.util.Collections;
......@@ -25,7 +32,6 @@ import static org.hamcrest.Matchers.equalTo;
import static org.hamcrest.Matchers.is;
import static org.hamcrest.core.IsNull.notNullValue;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.when;
@ExtendWith(SpringExtension.class)
......@@ -38,9 +44,12 @@ class ServiceDeploymentWithKubernetesTest {
@Autowired
private KubernetesRepositoryManager repositoryManager;
@Autowired
@MockitoBean
private RemoteClusterManager remoteClusterManager;
@MockitoBean
private RemoteClusterMonitoringService remoteClusterMonitoringService;
private static final Identifier deploymentId = Identifier.newInstance(1L);
@AfterEach
......@@ -57,7 +66,32 @@ class ServiceDeploymentWithKubernetesTest {
@Test
void shouldConfirmSupportForDeploymentOnKubernetes() {
AppDeploymentSpec appDeploymentSpec = new AppDeploymentSpec();
AppDeployment appDeployment = appDeployment();
AppDeployment appDeployment = appDeployment(null);
appDeploymentSpec.setSupportedDeploymentEnvironments(Collections.singletonList(AppDeploymentEnv.KUBERNETES));
appDeploymentSpec.setKubernetesTemplate(new KubernetesTemplate(
null,
new KubernetesChart(null, "test", "0.0.0"),
"archive",
null,
new HelmChartRepositoryEmbeddable("test", "http://test")
));
appDeploymentSpec.setStorageVolumes(Collections.singleton(new AppStorageVolume(ServiceStorageVolumeType.MAIN, 2, null)));
appDeploymentSpec.setAccessMethods(Collections.singleton(new AppAccessMethod(ServiceAccessMethodType.DEFAULT, "name", "tag", null)));
orchestrator.verifyDeploymentEnvironmentSupportAndBuildNmServiceInfo(Identifier.newInstance(1L), appDeployment, appDeploymentSpec);
KubernetesNmServiceInfo info = repositoryManager.loadService(deploymentId);
assertThat(info, is(notNullValue()));
assertThat(info.getDeploymentId(), equalTo(appDeployment.getDeploymentId()));
assertThat(info.getDeploymentName(), equalTo(appDeployment.getDeploymentName()));
assertThat(info.getDomain(), equalTo(appDeployment.getDomain()));
assertThat(info.getDescriptiveDeploymentId().getValue(), equalTo("domain-appname-100"));
}
@Test
void shouldConfirmSupportForDeploymentOnKubernetesRemoteCluster() {
AppDeploymentSpec appDeploymentSpec = new AppDeploymentSpec();
AppDeployment appDeployment = appDeployment(1L);
appDeploymentSpec.setSupportedDeploymentEnvironments(Collections.singletonList(AppDeploymentEnv.KUBERNETES));
appDeploymentSpec.setKubernetesTemplate(new KubernetesTemplate(
null,
......@@ -68,6 +102,8 @@ class ServiceDeploymentWithKubernetesTest {
));
appDeploymentSpec.setStorageVolumes(Collections.singleton(new AppStorageVolume(ServiceStorageVolumeType.MAIN, 2, null)));
appDeploymentSpec.setAccessMethods(Collections.singleton(new AppAccessMethod(ServiceAccessMethodType.DEFAULT, "name", "tag", null)));
when(remoteClusterManager.clusterExists(1L)).thenReturn(Boolean.TRUE);
when(remoteClusterMonitoringService.clusterAvailable(1L)).thenReturn(Boolean.TRUE);
orchestrator.verifyDeploymentEnvironmentSupportAndBuildNmServiceInfo(Identifier.newInstance(1L), appDeployment, appDeploymentSpec);
KubernetesNmServiceInfo info = repositoryManager.loadService(deploymentId);
......@@ -81,16 +117,16 @@ class ServiceDeploymentWithKubernetesTest {
@Test
void shouldNotifyIncompatibilityForDeploymentOnKubernetes() {
AppDeploymentSpec appDeploymentSpec = new AppDeploymentSpec();
AppDeployment appDeployment = appDeployment(null);
appDeploymentSpec.setSupportedDeploymentEnvironments(Collections.emptyList());
appDeploymentSpec.setKubernetesTemplate(new KubernetesTemplate());
assertThrows(NmServiceRequestVerificationException.class, () -> {
AppDeploymentSpec appDeploymentSpec = new AppDeploymentSpec();
AppDeployment appDeployment = appDeployment();
appDeploymentSpec.setSupportedDeploymentEnvironments(Collections.emptyList());
appDeploymentSpec.setKubernetesTemplate(new KubernetesTemplate());
orchestrator.verifyDeploymentEnvironmentSupportAndBuildNmServiceInfo(null, appDeployment, appDeploymentSpec);
});
}
private AppDeployment appDeployment() {
private AppDeployment appDeployment(Long remoteClusterId) {
return AppDeployment.builder()
.instanceId(100L)
.deploymentId(deploymentId)
......@@ -99,7 +135,7 @@ class ServiceDeploymentWithKubernetesTest {
.applicationId(Identifier.newInstance("appId"))
.deploymentName("deploy")
.configFileRepositoryRequired(false)
.remoteClusterId(1L)
.remoteClusterId(remoteClusterId)
.appName("AppName").build();
}
......
......@@ -2,8 +2,12 @@ package net.geant.nmaas.externalservices.kubernetes;
import org.springframework.web.multipart.MultipartFile;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
......@@ -40,4 +44,62 @@ public class RemoteClusterHelper {
return hexString.toString();
}
public static class StringMultipartFile implements MultipartFile {
private final byte[] content;
private final String name;
private final String originalFilename;
private final String contentType;
public StringMultipartFile(String name, String originalFilename, String contentType, String contentStr) {
this.name = name;
this.originalFilename = originalFilename;
this.contentType = contentType;
this.content = contentStr.getBytes(StandardCharsets.UTF_8);
}
@Override
public String getName() {
return name;
}
@Override
public String getOriginalFilename() {
return originalFilename;
}
@Override
public String getContentType() {
return contentType;
}
@Override
public boolean isEmpty() {
return content.length == 0;
}
@Override
public long getSize() {
return content.length;
}
@Override
public byte[] getBytes() {
return content;
}
@Override
public InputStream getInputStream() {
return new ByteArrayInputStream(content);
}
@Override
public void transferTo(File dest) throws IOException, IllegalStateException {
try (FileOutputStream out = new FileOutputStream(dest)) {
out.write(content);
}
}
}
}
package net.geant.nmaas.externalservices.kubernetes;
import net.geant.nmaas.externalservices.kubernetes.api.model.RemoteClusterView;
import net.geant.nmaas.externalservices.kubernetes.entities.KCluster;
import org.springframework.web.multipart.MultipartFile;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
import java.util.List;
public interface RemoteClusterManagementService {
RemoteClusterView getCluster(Long id, Principal principal);
KCluster getClusterEntity(Long id);
List<RemoteClusterView> getAllClusters();
List<RemoteClusterView> getClustersInDomain(Long domainId);
RemoteClusterView saveCluster(KCluster entity, MultipartFile file) throws IOException, NoSuchAlgorithmException;
RemoteClusterView updateCluster(RemoteClusterView cluster, Long id);
void removeCluster(Long id);
boolean clusterExists(Long id);
RemoteClusterView saveClusterFile(RemoteClusterView view, MultipartFile file);
RemoteClusterView mapFile(RemoteClusterView view, MultipartFile file);
}
......@@ -18,7 +18,6 @@ import org.modelmapper.ModelMapper;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.security.NoSuchAlgorithmException;
import java.security.Principal;
......@@ -35,7 +34,7 @@ import static net.geant.nmaas.externalservices.kubernetes.RemoteClusterHelper.sa
@Service
@RequiredArgsConstructor
@Slf4j
public class RemoteClusterManager {
public class RemoteClusterManager implements RemoteClusterManagementService {
private final KClusterRepository clusterRepository;
private final KubernetesClusterIngressManager kClusterIngressManager;
......@@ -45,10 +44,11 @@ public class RemoteClusterManager {
private final UserService userService;
private final ModelMapper modelMapper;
public RemoteClusterView getClusterView(Long id, Principal principal) {
@Override
public RemoteClusterView getCluster(Long id, Principal principal) {
Optional<KCluster> cluster = clusterRepository.findById(id);
if (cluster.isPresent()) {
if(userService.isAdmin(principal.getName()) || userService.isUserAdminInAnyDomain(cluster.get().getDomains(), principal.getName()) ) {
if (userService.isAdmin(principal.getName()) || userService.isUserAdminInAnyDomain(cluster.get().getDomains(), principal.getName())) {
return toView(cluster.get());
} else {
throw new IllegalArgumentException("No access to cluster " + id);
......@@ -58,12 +58,14 @@ public class RemoteClusterManager {
}
}
public List<RemoteClusterView> getAllClusterView() {
@Override
public List<RemoteClusterView> getAllClusters() {
List<KCluster> clusters = clusterRepository.findAll();
return clusters.stream().map(this::toView).collect(Collectors.toList());
}
public KCluster getCluster(Long id) {
@Override
public KCluster getClusterEntity(Long id) {
Optional<KCluster> cluster = clusterRepository.findById(id);
if (cluster.isPresent()) {
return cluster.get();
......@@ -72,13 +74,12 @@ public class RemoteClusterManager {
}
}
//if domain GLOBAL return all
// if domain GLOBAL return all
public List<RemoteClusterView> getClustersInDomain(Long domainId) {
log.warn("Looking cluster in domain {}", domainId);
List<KCluster> clusters = new ArrayList<>();
Optional<Domain> domainOtp = domainService.getGlobalDomain();
if(domainOtp.isPresent()) {
if(domainId.equals(domainOtp.get().getId())) {
Optional<Domain> domainFromDb = domainService.getGlobalDomain();
List<KCluster> clusters;
if (domainFromDb.isPresent()) {
if (domainId.equals(domainFromDb.get().getId())) {
clusters = clusterRepository.findAll();
} else {
clusters = clusterRepository.findByDomains_Id(domainId);
......@@ -89,11 +90,7 @@ public class RemoteClusterManager {
return clusters.stream().map(this::toView).collect(Collectors.toList());
}
public File getFileFromCluster(Long id) {
KCluster cluster = getCluster(id);
return new File(cluster.getPathConfigFile());
}
@Override
public RemoteClusterView saveCluster(KCluster entity, MultipartFile file) throws IOException, NoSuchAlgorithmException {
checkRequest(entity);
......@@ -119,6 +116,7 @@ public class RemoteClusterManager {
}
}
@Override
public RemoteClusterView mapFile(RemoteClusterView view, MultipartFile file) {
checkRequestRead(view);
......@@ -161,6 +159,7 @@ public class RemoteClusterManager {
return null;
}
@Override
public RemoteClusterView saveClusterFile(RemoteClusterView view, MultipartFile file) {
checkRequest(view);
try {
......@@ -212,6 +211,7 @@ public class RemoteClusterManager {
).toList();
}
@Override
public RemoteClusterView updateCluster(RemoteClusterView cluster, Long id) {
Optional<KCluster> entity = clusterRepository.findById(id);
......@@ -280,10 +280,11 @@ public class RemoteClusterManager {
return view;
}
@Override
public void removeCluster(Long id) {
try {
if (clusterRepository.existsById(id)) {
this.clusterRepository.deleteById(id);
clusterRepository.deleteById(id);
}
} catch (RuntimeException ex) {
log.warn("Can not delete cluster {}", id);
......@@ -291,6 +292,7 @@ public class RemoteClusterManager {
}
}
@Override
public boolean clusterExists(Long id) {
if (id == null) {
return false;
......
......@@ -6,7 +6,7 @@ import lombok.extern.slf4j.Slf4j;
import net.geant.nmaas.externalservices.kubernetes.entities.KCluster;
import net.geant.nmaas.externalservices.kubernetes.entities.KClusterState;
import net.geant.nmaas.externalservices.kubernetes.repositories.KClusterRepository;
import net.geant.nmaas.kubernetes.KubernetesApiService;
import net.geant.nmaas.kubernetes.KubernetesApiClientService;
import net.geant.nmaas.kubernetes.KubernetesClientSetupException;
import net.geant.nmaas.notifications.templates.MailType;
import org.springframework.stereotype.Service;
......@@ -25,19 +25,30 @@ import static net.geant.nmaas.externalservices.kubernetes.RemoteClusterHelper.sa
@Service
@RequiredArgsConstructor
@Slf4j
public class RemoteClusterMonitor implements ClusterMonitoringService {
public class RemoteClusterMonitor implements RemoteClusterMonitoringService {
private final KClusterRepository clusterRepository;
private final KubernetesApiService kubernetesApiService;
private final KubernetesApiClientService kubernetesApiClientService;
private final RemoteClusterMailer mailer;
@Override
public boolean clusterAvailable(Long id) {
final KCluster cluster = clusterRepository.getReferenceById(id);
try {
kubernetesApiClientService.getKubernetesVersion(cluster);
return true;
} catch (Exception e) {
return false;
}
}
@Override
public void updateAllClusterState() {
restoreKubeconfigFileIfMissing();
List<KCluster> kClusters = clusterRepository.findAll();
kClusters.forEach(cluster -> {
try {
final String version = kubernetesApiService.getKubernetesVersion(cluster);
final String version = kubernetesApiClientService.getKubernetesVersion(cluster);
log.debug("Received version information for cluster {} -> {}", cluster.getCodename(), version);
updateStateIfNeeded(cluster, KClusterState.UP);
} catch (KubernetesClientSetupException e) {
......@@ -70,7 +81,7 @@ public class RemoteClusterMonitor implements ClusterMonitoringService {
List<KCluster> clusters = clusterRepository.findAll();
clusters.forEach(cluster -> {
if (!isFileAvailable(cluster.getPathConfigFile())) {
MultipartFile file = new StringMultipartFile("file",
MultipartFile file = new RemoteClusterHelper.StringMultipartFile("file",
"config.yaml",
"application/x-yaml",
cluster.getClusterConfigFile());
......
......@@ -10,14 +10,14 @@ import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
@Slf4j
public class ClusterMonitoringJob implements Job {
public class RemoteClusterMonitoringJob implements Job {
private final ClusterMonitoringService clusterMonitoringService;
private final RemoteClusterMonitoringService remoteClusterMonitoringService;
@Override
public void execute(JobExecutionContext jobExecutionContext) throws JobExecutionException {
log.info("Triggering cluster health check...");
clusterMonitoringService.updateAllClusterState();
remoteClusterMonitoringService.updateAllClusterState();
}
}
package net.geant.nmaas.externalservices.kubernetes;
public interface ClusterMonitoringService {
public interface RemoteClusterMonitoringService {
boolean clusterAvailable(Long id);
void updateAllClusterState();
......
package net.geant.nmaas.externalservices.kubernetes;
import org.springframework.web.multipart.MultipartFile;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
public class StringMultipartFile implements MultipartFile {
private final byte[] content;
private final String name;
private final String originalFilename;
private final String contentType;
public StringMultipartFile(String name, String originalFilename, String contentType, String contentStr) {
this.name = name;
this.originalFilename = originalFilename;
this.contentType = contentType;
this.content = contentStr.getBytes(StandardCharsets.UTF_8);
}
@Override
public String getName() {
return name;
}
@Override
public String getOriginalFilename() {
return originalFilename;
}
@Override
public String getContentType() {
return contentType;
}
@Override
public boolean isEmpty() {
return content.length == 0;
}
@Override
public long getSize() {
return content.length;
}
@Override
public byte[] getBytes() {
return content;
}
@Override
public InputStream getInputStream() {
return new ByteArrayInputStream(content);
}
@Override
public void transferTo(File dest) throws IOException, IllegalStateException {
try (FileOutputStream out = new FileOutputStream(dest)) {
out.write(content);
}
}
}
\ No newline at end of file
......@@ -3,8 +3,7 @@ package net.geant.nmaas.externalservices.kubernetes.api;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.geant.nmaas.externalservices.kubernetes.RemoteClusterManager;
import net.geant.nmaas.externalservices.kubernetes.RemoteClusterManagementService;
import net.geant.nmaas.externalservices.kubernetes.api.model.RemoteClusterView;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.web.bind.annotation.DeleteMapping;
......@@ -24,22 +23,20 @@ import java.util.List;
@RestController
@RequestMapping(value = "/api/management/cluster")
@RequiredArgsConstructor
@Slf4j
public class RemoteClusterManagerController {
private final RemoteClusterManager remoteClusterManager;
private final RemoteClusterManagementService remoteClusterManager;
@PreAuthorize("hasRole('ROLE_SYSTEM_ADMIN') || hasRole('ROLE_OPERATOR') || hasRole('ROLE_DOMAIN_ADMIN')")
@GetMapping("/{id}")
public RemoteClusterView getKubernetesCluster(@PathVariable Long id, Principal principal) {
return remoteClusterManager.getClusterView(id, principal);
return remoteClusterManager.getCluster(id, principal);
}
@PreAuthorize("hasRole('ROLE_SYSTEM_ADMIN') || hasRole('ROLE_OPERATOR')")
@GetMapping("/all")
public List<RemoteClusterView> getAllKubernetesCluster() {
return remoteClusterManager.getAllClusterView();
return remoteClusterManager.getAllClusters();
}
@PreAuthorize("hasRole('ROLE_SYSTEM_ADMIN') || hasRole('ROLE_OPERATOR') || hasPermission(#domainId, 'domain', 'OWNER')")
......@@ -54,7 +51,6 @@ public class RemoteClusterManagerController {
ObjectMapper objectMapper = new ObjectMapper();
try {
RemoteClusterView cluster = objectMapper.readValue(viewString, RemoteClusterView.class);
log.info("New remote Kubernetes cluster created");
return remoteClusterManager.saveClusterFile(cluster, file);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
......@@ -79,7 +75,6 @@ public class RemoteClusterManagerController {
ObjectMapper objectMapper = new ObjectMapper();
try {
RemoteClusterView cluster = objectMapper.readValue(viewString, RemoteClusterView.class);
log.info("New remote Kubernetes cluster to be readed");
return remoteClusterManager.mapFile(cluster, file);
} catch (JsonProcessingException e) {
throw new RuntimeException(e);
......
package net.geant.nmaas.kubernetes;
import io.fabric8.kubernetes.api.model.apps.Deployment;
import io.fabric8.kubernetes.client.KubernetesClient;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
......@@ -11,7 +12,7 @@ import java.util.Objects;
@Component
@RequiredArgsConstructor
@Slf4j
public class KubernetesApiService {
public class KubernetesApiClientService {
private final KubernetesApiClientFactory kubernetesApiClientFactory;
......@@ -22,6 +23,16 @@ public class KubernetesApiService {
return version;
}
public Deployment getDeployment(KCluster kCluster, String namespace, String deploymentName) {
try (KubernetesClient client = initClient(kCluster)) {
return client.apps()
.deployments()
.inNamespace(namespace)
.withName(deploymentName)
.get();
}
}
public void scaleDeployment(KCluster kCluster, String namespace, String deploymentName, int replicas) {
KubernetesClient client = initClient(kCluster);
client.apps()
......
package net.geant.nmaas.kubernetes;
import io.fabric8.kubernetes.api.model.apps.Deployment;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.geant.nmaas.externalservices.kubernetes.KubernetesClusterNamespaceService;
import net.geant.nmaas.externalservices.kubernetes.entities.KCluster;
import net.geant.nmaas.orchestration.Identifier;
import org.springframework.stereotype.Component;
import java.util.Objects;
@Component
@RequiredArgsConstructor
@Slf4j
public class KubernetesApiJanitorService {
private final KubernetesClusterNamespaceService namespaceService;
private final KubernetesApiClientService kubernetesApiClientService;
public boolean checkIfReady(KCluster kCluster, Identifier deploymentId, String domain) {
final String namespace = namespaceService.namespace(domain);
final Deployment deployment = kubernetesApiClientService.getDeployment(kCluster, deploymentId.value(), namespace);
if (Objects.nonNull(deployment)) {
return Objects.equals(deployment.getSpec().getReplicas(), deployment.getStatus().getReadyReplicas());
}
log.info("Deployment {} not found in namespace {}", deploymentId.value(), namespace);
return false;
}
}
\ No newline at end of file
......@@ -4,17 +4,17 @@ import com.google.common.base.Strings;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.geant.nmaas.externalservices.kubernetes.KubernetesClusterIngressManager;
import net.geant.nmaas.externalservices.kubernetes.RemoteClusterManager;
import net.geant.nmaas.externalservices.kubernetes.RemoteClusterManagementService;
import net.geant.nmaas.externalservices.kubernetes.RemoteClusterMonitoringService;
import net.geant.nmaas.externalservices.kubernetes.entities.IngressControllerConfigOption;
import net.geant.nmaas.externalservices.kubernetes.entities.KCluster;
import net.geant.nmaas.gitlab.GitLabManager;
import net.geant.nmaas.gitlab.exceptions.GitLabInvalidConfigurationException;
import net.geant.nmaas.kubernetes.KubernetesApiJanitorService;
import net.geant.nmaas.nmservice.deployment.ContainerOrchestrator;
import net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes.components.cluster.KClusterCheckException;
import net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes.components.helm.HelmChartIngressVariable;
import net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes.components.ingress.IngressControllerManipulationException;
import net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes.janitor.JanitorResponseException;
import net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes.janitor.JanitorService;
import net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes.entities.KubernetesNmServiceInfo;
import net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes.entities.KubernetesTemplate;
import net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes.entities.ParameterType;
......@@ -22,6 +22,8 @@ import net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes.en
import net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes.entities.ServiceAccessMethodView;
import net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes.entities.ServiceStorageVolume;
import net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes.exceptions.KServiceManipulationException;
import net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes.janitor.JanitorResponseException;
import net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes.janitor.JanitorService;
import net.geant.nmaas.nmservice.deployment.exceptions.ContainerCheckFailedException;
import net.geant.nmaas.nmservice.deployment.exceptions.ContainerOrchestratorInternalErrorException;
import net.geant.nmaas.nmservice.deployment.exceptions.CouldNotDeployNmServiceException;
......@@ -51,6 +53,7 @@ import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;
......@@ -84,7 +87,9 @@ public class KubernetesManager implements ContainerOrchestrator {
private final KubernetesClusterIngressManager ingressManager;
private final GitLabManager gitLabManager;
private final JanitorService janitorService;
private final RemoteClusterManager remoteClusterManager;
private final KubernetesApiJanitorService kubernetesApiJanitorService;
private final RemoteClusterManagementService remoteClusterManager;
private final RemoteClusterMonitoringService remoteClusterMonitor;
@Override
@Loggable(LogLevel.INFO)
......@@ -100,7 +105,16 @@ public class KubernetesManager implements ContainerOrchestrator {
} catch (IllegalArgumentException iae) {
throw new NmServiceRequestVerificationException(iae.getMessage());
}
//todo
if (Objects.nonNull(appDeployment.getRemoteClusterId())) {
if (!remoteClusterManager.clusterExists(appDeployment.getRemoteClusterId())) {
throw new NmServiceRequestVerificationException(String.format("Remote cluster with id %s doesn't exist", appDeployment.getRemoteClusterId()));
} else {
if (!remoteClusterMonitor.clusterAvailable(appDeployment.getRemoteClusterId())) {
throw new NmServiceRequestVerificationException(String.format("Remote cluster with id %s is currently unavailable", appDeployment.getRemoteClusterId()));
}
}
}
KubernetesNmServiceInfo serviceInfo = new KubernetesNmServiceInfo(
deploymentId,
......@@ -108,11 +122,9 @@ public class KubernetesManager implements ContainerOrchestrator {
appDeployment.getDomain(),
appDeployment.getDescriptiveDeploymentId()
);
//verify cluster
if (remoteClusterManager.clusterExists(appDeployment.getRemoteClusterId())) {
serviceInfo.setRemoteCluster(remoteClusterManager.getCluster(appDeployment.getRemoteClusterId()));
if (Objects.nonNull(appDeployment.getRemoteClusterId())) {
serviceInfo.setRemoteCluster(remoteClusterManager.getClusterEntity(appDeployment.getRemoteClusterId()));
}
serviceInfo.setKubernetesTemplate(KubernetesTemplate.copy(appDeploymentSpec.getKubernetesTemplate()));
serviceInfo.setStorageVolumes(generateTemplateStorageVolumes(appDeploymentSpec.getStorageVolumes()));
serviceInfo.setAccessMethods(generateTemplateAccessMethods(appDeploymentSpec.getAccessMethods()));
......@@ -333,7 +345,8 @@ public class KubernetesManager implements ContainerOrchestrator {
KubernetesNmServiceInfo service = repositoryManager.loadService(deploymentId);
if (!janitorService.checkIfReady(
if (!kubernetesApiJanitorService.checkIfReady(
service.getRemoteCluster(),
getDeploymentIdForJanitorStatusCheck(
service.getDescriptiveDeploymentId().value(),
service.getKubernetesTemplate().getMainDeploymentName()),
......@@ -353,18 +366,20 @@ public class KubernetesManager implements ContainerOrchestrator {
private void retrieveOrUpdateInternalServiceIpAddress(KubernetesNmServiceInfo service) {
try {
Set<ServiceAccessMethod> accessMethods = service.getAccessMethods().stream()
.map(m -> {
if (m.isOfType(INTERNAL) && StringUtils.isEmpty(m.getUrl())) {
String lbServiceIp = janitorService.retrieveServiceIp(
buildServiceId(service.getDescriptiveDeploymentId(), m.getDeployParameters()),
service.getDomain());
String ipWithPortString = getIpAddressWithPort(lbServiceIp, m.getDeployParameters());
m.setUrl(getUserAtIpAddressUrl(ipWithPortString, m.getProtocol(), m.getDeployParameters()));
}
return m;
})
.collect(Collectors.toSet());
Set<ServiceAccessMethod> accessMethods = new HashSet<>();
service.getAccessMethods().forEach(m -> {
final ServiceAccessMethod copy = ServiceAccessMethod.copy(m);
log.info("access methods copy created");
if (m.isOfType(INTERNAL) && StringUtils.isEmpty(m.getUrl())) {
final String lbServiceIp = janitorService.retrieveServiceIp(
buildServiceId(service.getDescriptiveDeploymentId(), m.getDeployParameters()),
service.getDomain());
final String ipWithPortString = getIpAddressWithPort(lbServiceIp, m.getDeployParameters());
log.info("setting url to: {}", getUserAtIpAddressUrl(ipWithPortString, m.getProtocol(), m.getDeployParameters()));
copy.setUrl(getUserAtIpAddressUrl(ipWithPortString, m.getProtocol(), m.getDeployParameters()));
}
accessMethods.add(copy);
});
repositoryManager.updateKServiceAccessMethods(accessMethods);
} catch (JanitorResponseException je) {
log.error("Could not retrieve IP for {}", service.getDescriptiveDeploymentId());
......@@ -405,21 +420,21 @@ public class KubernetesManager implements ContainerOrchestrator {
private void retrieveOrUpdateLocalServiceName(KubernetesNmServiceInfo service) {
try {
Set<ServiceAccessMethod> accessMethods = service.getAccessMethods().stream()
.map(m -> {
if (m.isOfType(LOCAL) && StringUtils.isEmpty(m.getUrl())) {
Identifier serviceName = buildServiceId(service.getDescriptiveDeploymentId(), m.getDeployParameters());
janitorService.checkServiceExists(serviceName, service.getDomain());
String username = m.getDeployParameters().get(HelmChartIngressVariable.ACCESS_USER);
m.setUrl(username != null && !username.isEmpty() ?
username + "@" + serviceName.value() : serviceName.value());
if (m.getDeployParameters().containsKey(HelmChartIngressVariable.K8S_SERVICE_PORT)) {
m.setUrl(m.getUrl() + " (port: " + m.getDeployParameters().get(HelmChartIngressVariable.K8S_SERVICE_PORT) + ")");
}
}
return m;
})
.collect(Collectors.toSet());
Set<ServiceAccessMethod> accessMethods = new HashSet<>();
service.getAccessMethods().forEach(m -> {
final ServiceAccessMethod copy = ServiceAccessMethod.copy(m);
if (m.isOfType(LOCAL) && StringUtils.isEmpty(m.getUrl())) {
final Identifier serviceName = buildServiceId(service.getDescriptiveDeploymentId(), m.getDeployParameters());
janitorService.checkServiceExists(serviceName, service.getDomain());
String username = m.getDeployParameters().get(HelmChartIngressVariable.ACCESS_USER);
copy.setUrl(username != null && !username.isEmpty() ?
username + "@" + serviceName.value() : serviceName.value());
if (m.getDeployParameters().containsKey(HelmChartIngressVariable.K8S_SERVICE_PORT)) {
copy.setUrl(copy.getUrl() + " (port: " + m.getDeployParameters().get(HelmChartIngressVariable.K8S_SERVICE_PORT) + ")");
}
}
accessMethods.add(copy);
});
repositoryManager.updateKServiceAccessMethods(accessMethods);
} catch (JanitorResponseException je) {
log.error("Could not retrieve service name for {}", service.getDescriptiveDeploymentId());
......
......@@ -52,7 +52,7 @@ public class KubernetesRepositoryManager extends NmServiceRepositoryManager<Kube
@Transactional(propagation = Propagation.REQUIRES_NEW)
public void updateKServiceAccessMethods(Set<ServiceAccessMethod> serviceAccessMethods) {
serviceAccessMethods.forEach(m -> accessMethodRepository.save(m));
serviceAccessMethods.forEach(accessMethodRepository::save);
}
}
......@@ -3,7 +3,7 @@ package net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes.c
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.geant.nmaas.externalservices.kubernetes.KubernetesClusterNamespaceService;
import net.geant.nmaas.kubernetes.KubernetesApiService;
import net.geant.nmaas.kubernetes.KubernetesApiClientService;
import net.geant.nmaas.kubernetes.KubernetesClientSetupException;
import net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes.KServiceOperationsManager;
import net.geant.nmaas.nmservice.deployment.containerorchestrators.kubernetes.KubernetesRepositoryManager;
......@@ -25,7 +25,7 @@ public class DefaultKServiceOperationsManager implements KServiceOperationsManag
private final KubernetesClusterNamespaceService namespaceService;
private final KubernetesRepositoryManager repositoryManager;
private final KubernetesApiService kubernetesApiService;
private final KubernetesApiClientService kubernetesApiClientService;
@Override
@Loggable(LogLevel.INFO)
......@@ -43,7 +43,7 @@ public class DefaultKServiceOperationsManager implements KServiceOperationsManag
Stream.of(serviceInfo.getDescriptiveDeploymentId().getValue(), serviceInfo.getKubernetesTemplate().getMainDeploymentName())
.filter(Objects::nonNull)
.collect(Collectors.joining("-"));
kubernetesApiService.scaleDeployment(serviceInfo.getRemoteCluster(), namespace, kubernetesDeploymentName, replicas);
kubernetesApiClientService.scaleDeployment(serviceInfo.getRemoteCluster(), namespace, kubernetesDeploymentName, replicas);
} catch (KubernetesClientSetupException e) {
log.error(e.getMessage());
}
......
......@@ -25,7 +25,7 @@ import java.util.Map;
import static net.geant.nmaas.orchestration.entities.AppAccessMethod.ConditionType.DEPLOYMENT_PARAMETER;
/**
* This class represents single access method to NMAAS service
* This class represents single access method to a nmaas service
*/
@Getter
@Setter
......
......@@ -128,6 +128,7 @@ public class JanitorService {
return Arrays.asList(ConnectivityState.CONNECTING, ConnectivityState.IDLE, ConnectivityState.READY).contains(this.channel.getState(false));
}
@Deprecated
public boolean checkIfReady(Identifier deploymentId, String domain) {
log.trace("Checking if deployment {} in domain {} is ready", deploymentId.value(), domain);
ReadinessServiceGrpc.ReadinessServiceBlockingStub stub = ReadinessServiceGrpc.newBlockingStub(channel);
......@@ -139,6 +140,7 @@ public class JanitorService {
};
}
@Deprecated
public String retrieveServiceIp(Identifier serviceId, String domain) {
log.info("Retrieving service IP for {} in domain {}", serviceId.value(), domain);
InformationServiceGrpc.InformationServiceBlockingStub stub = InformationServiceGrpc.newBlockingStub(channel);
......
......@@ -2,7 +2,7 @@ package net.geant.nmaas.orchestration.tasks.app;
import lombok.AllArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.geant.nmaas.externalservices.kubernetes.RemoteClusterManager;
import net.geant.nmaas.externalservices.kubernetes.RemoteClusterManagementService;
import net.geant.nmaas.nmservice.deployment.NmServiceDeploymentProvider;
import net.geant.nmaas.orchestration.Identifier;
import net.geant.nmaas.orchestration.entities.AppDeployment;
......@@ -31,7 +31,7 @@ public class AppRequestVerificationTask {
private AppDeploymentRepository repository;
private ApplicationRepository appRepository;
private RemoteClusterManager remoteClusterManager;
private RemoteClusterManagementService remoteClusterManager;
@EventListener
@Loggable(LogLevel.INFO)
......
......@@ -2,7 +2,7 @@ package net.geant.nmaas.scheduling;
import com.google.common.base.Strings;
import lombok.extern.slf4j.Slf4j;
import net.geant.nmaas.externalservices.kubernetes.ClusterMonitoringJob;
import net.geant.nmaas.externalservices.kubernetes.RemoteClusterMonitoringJob;
import net.geant.nmaas.portal.service.ConfigurationManager;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
......@@ -18,17 +18,17 @@ public class ClusterHealthCheckScheduleInit implements InitializingBean {
public static final String CLUSTER_HEALTH_CHECK = "ClusterHealthCheck";
private final ClusterMonitoringJob clusterMonitoringJob;
private final RemoteClusterMonitoringJob remoteClusterMonitoringJob;
private final ScheduleManager scheduleManager;
private final ConfigurationManager configurationManager;
private final String healthCheckJobCron;
@Autowired
public ClusterHealthCheckScheduleInit(ClusterMonitoringJob clusterMonitoringJob,
public ClusterHealthCheckScheduleInit(RemoteClusterMonitoringJob remoteClusterMonitoringJob,
ScheduleManager scheduleManager,
ConfigurationManager configurationManager,
@Value("${nmaas.service.health-check.cron}") String healthCheckJobCron) {
this.clusterMonitoringJob = clusterMonitoringJob;
this.remoteClusterMonitoringJob = remoteClusterMonitoringJob;
this.scheduleManager = scheduleManager;
this.configurationManager = configurationManager;
this.healthCheckJobCron = healthCheckJobCron;
......@@ -40,13 +40,13 @@ public class ClusterHealthCheckScheduleInit implements InitializingBean {
final String healthCheckJobCronFromDb = configurationManager.getConfiguration().getHealthCheckJobCron();
if (!Strings.isNullOrEmpty(healthCheckJobCronFromDb)) {
log.debug("Scheduling cluster health check job based on cron loaded from the database");
this.scheduleManager.createJob(clusterMonitoringJob, CLUSTER_HEALTH_CHECK, healthCheckJobCronFromDb);
this.scheduleManager.createJob(remoteClusterMonitoringJob, CLUSTER_HEALTH_CHECK, healthCheckJobCronFromDb);
log.error("Adding new job for health check cluster ...");
} else if (Strings.isNullOrEmpty(healthCheckJobCron)) {
log.warn("Cluster health check cron expression not provided");
} else {
log.debug("Scheduling cluster health check job based on cron loaded from properties");
this.scheduleManager.createJob(clusterMonitoringJob, CLUSTER_HEALTH_CHECK, healthCheckJobCron);
this.scheduleManager.createJob(remoteClusterMonitoringJob, CLUSTER_HEALTH_CHECK, healthCheckJobCron);
}
}
......
......@@ -40,7 +40,6 @@ class ClusterServiceTest {
private final RemoteClusterManager remoteClusterManager = new RemoteClusterManager(
kClusterRepository, kClusterIngressManager, kClusterDeploymentManager, domainService, null, userService, modelMapper);
private Domain globalDomain;
private Domain specificDomain;
private KCluster cluster1;
......@@ -48,7 +47,6 @@ class ClusterServiceTest {
private KCluster cluster3;
private Principal mockPrincipal;
@BeforeEach
void setUp() {
globalDomain = new Domain("global", "global.example.com");
......@@ -78,7 +76,7 @@ class ClusterServiceTest {
when(kClusterRepository.findById(id)).thenReturn(Optional.of(remoteCluster));
when(userService.isAdmin(anyString())).thenReturn(true);
RemoteClusterView result = remoteClusterManager.getClusterView(id, mockPrincipal);
RemoteClusterView result = remoteClusterManager.getCluster(id, mockPrincipal);
assertEquals(remoteCluster.getName(), result.getName());
verify(kClusterRepository, times(1)).findById(id);
......@@ -86,7 +84,6 @@ class ClusterServiceTest {
verify(userService, never()).isUserAdminInAnyDomain(anyList(), anyString());
}
@Test
void getClusterView_validId_domainAdminUser_returnsRemoteClusterView() throws AccessDeniedException {
Long id = 1L;
......@@ -97,7 +94,7 @@ class ClusterServiceTest {
when(userService.isAdmin(anyString())).thenReturn(false);
when(userService.isUserAdminInAnyDomain(anyList(), anyString())).thenReturn(true);
RemoteClusterView result = remoteClusterManager.getClusterView(id, mockPrincipal);
RemoteClusterView result = remoteClusterManager.getCluster(id, mockPrincipal);
assertEquals(remoteCluster.getName(), result.getName());
verify(kClusterRepository, times(1)).findById(id);
......@@ -115,20 +112,19 @@ class ClusterServiceTest {
when(userService.isAdmin(anyString())).thenReturn(false);
when(userService.isUserAdminInAnyDomain(anyList(), anyString())).thenReturn(false);
assertThrows(IllegalArgumentException.class, () -> remoteClusterManager.getClusterView(id, mockPrincipal));
assertThrows(IllegalArgumentException.class, () -> remoteClusterManager.getCluster(id, mockPrincipal));
verify(kClusterRepository, times(1)).findById(id);
verify(userService, times(1)).isAdmin(mockPrincipal.getName());
verify(userService, times(1)).isUserAdminInAnyDomain(remoteCluster.getDomains(), mockPrincipal.getName());
}
@Test
void getClusterView_invalidId_throwsException() {
Long id = 100L;
when(kClusterRepository.findById(id)).thenReturn(Optional.empty());
assertThrows(IllegalArgumentException.class, () -> remoteClusterManager.getClusterView(id, mockPrincipal));
assertThrows(IllegalArgumentException.class, () -> remoteClusterManager.getCluster(id, mockPrincipal));
verify(kClusterRepository, times(1)).findById(id);
}
......@@ -139,45 +135,12 @@ class ClusterServiceTest {
when(kClusterRepository.findAll()).thenReturn(List.of(cluster1, cluster2));
List<RemoteClusterView> result = remoteClusterManager.getAllClusterView();
List<RemoteClusterView> result = remoteClusterManager.getAllClusters();
assertEquals(2, result.size());
verify(kClusterRepository, times(1)).findAll();
}
// @Test
// void saveCluster_validInput_savesCluster() throws IOException, NoSuchAlgorithmException {
// MultipartFile multipartFile = mock(MultipartFile.class);
// ClusterManager clusterManager = ClusterManager.builder().name("Cluster").description("Description").build();
//
// when(clusterManagerRepository.save(any(ClusterManager.class))).thenReturn(clusterManager);
//
// // Load config.yaml from test/resources directory
// ClassLoader classLoader = getClass().getClassLoader();
// try (var inputStream = classLoader.getResourceAsStream("test/resources/config.yaml")) {
// when(multipartFile.getInputStream()).thenReturn(inputStream);
//
// ClusterManagerView result = clusterService.saveCluster(clusterManager, multipartFile);
// assertEquals(clusterManager.getName(), result.getName());
// verify(clusterManagerRepository, times(1)).save(clusterManager);
// }
// }
//
// @Test
// void updateCluster_validInput_updatesCluster() {
// Long id = 1L;
// ClusterManager existingCluster = ClusterManager.builder().id(id).name("OldName").build();
// ClusterManagerView updatedView = ClusterManagerView.builder().id(id).name("NewName").build();
//
// when(clusterManagerRepository.findById(id)).thenReturn(Optional.of(existingCluster));
// when(clusterManagerRepository.save(any(ClusterManager.class))).thenReturn(existingCluster);
//
// ClusterManagerView result = clusterService.updateCluster(updatedView, id);
//
// assertEquals("NewName", result.getName());
// verify(clusterManagerRepository, times(1)).save(any(ClusterManager.class));
// }
@Test
void shouldReturnAllClustersWhenDomainIdIsGlobalDomainId() {
// Given
......@@ -195,7 +158,6 @@ class ClusterServiceTest {
assertTrue(result.stream().anyMatch(v -> v.getId().equals(cluster1.getId())));
assertTrue(result.stream().anyMatch(v -> v.getId().equals(cluster2.getId())));
assertTrue(result.stream().anyMatch(v -> v.getId().equals(cluster3.getId())));
}
@Test
......@@ -214,7 +176,6 @@ class ClusterServiceTest {
assertEquals(2, result.size());
assertTrue(result.stream().anyMatch(v -> v.getId().equals(cluster1.getId())));
assertTrue(result.stream().anyMatch(v -> v.getId().equals(cluster2.getId())));
}
@Test
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment