diff --git a/src/integrationTest/java/net/geant/nmaas/externalservices/kubernetes/KubernetesClusterControllerIntTest.java b/src/integrationTest/java/net/geant/nmaas/externalservices/kubernetes/KubernetesClusterControllerIntTest.java index 9ca59982176993b4fe71e3f3bd1c1e47a44e076e..6b4e7aec59c54baa29a1bc1879ef83e6e34dab61 100644 --- a/src/integrationTest/java/net/geant/nmaas/externalservices/kubernetes/KubernetesClusterControllerIntTest.java +++ b/src/integrationTest/java/net/geant/nmaas/externalservices/kubernetes/KubernetesClusterControllerIntTest.java @@ -22,7 +22,7 @@ public class KubernetesClusterControllerIntTest { private static final String KUBERNETES_CLUSTER_JSON = "{" + - "\"ingress\":{" + + "\"ingress\":{\"id\":null" + "\"controllerConfigOption\":\"USE_EXISTING\"," + "\"supportedIngressClass\":\"ingress-class\"," + "\"publicIngressClass\":\"public\"," + @@ -36,7 +36,7 @@ public class KubernetesClusterControllerIntTest { "\"issuerOrWildcardName\":\"test-issuer\"," + "\"ingressPerDomain\":true" + "}," + - "\"deployment\":{" + + "\"deployment\":{\"id\":null" + "\"namespaceConfigOption\":\"USE_DOMAIN_NAMESPACE\"," + "\"defaultNamespace\":\"test-namespace\"," + "\"defaultStorageClass\":\"storageClass\"," + diff --git a/src/main/java/net/geant/nmaas/externalservices/kubernetes/ClusterManagerController.java b/src/main/java/net/geant/nmaas/externalservices/kubernetes/ClusterManagerController.java new file mode 100644 index 0000000000000000000000000000000000000000..7113cff3ae556aa03b96d98fc81b8f967da453fe --- /dev/null +++ b/src/main/java/net/geant/nmaas/externalservices/kubernetes/ClusterManagerController.java @@ -0,0 +1,70 @@ +package net.geant.nmaas.externalservices.kubernetes; + +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.model.ClusterManager; +import net.geant.nmaas.externalservices.kubernetes.model.ClusterManagerView; +import net.geant.nmaas.externalservices.kubernetes.model.KClusterView; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PatchMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +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.RequestPart; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; + +@RestController +@RequestMapping(value = "/api/management/cluster") +@RequiredArgsConstructor +@Slf4j +public class ClusterManagerController { + + private final ClusterService clusterService; + + + @PreAuthorize("hasRole('ROLE_SYSTEM_ADMIN') || hasRole('ROLE_OPERATOR')") + @GetMapping("/{id}") + public ClusterManagerView getKubernetesCluster(@PathVariable Long id) { + return clusterService.getClusterView(id); + } + + @PreAuthorize("hasRole('ROLE_SYSTEM_ADMIN') || hasRole('ROLE_OPERATOR')") + @GetMapping("/all") + public List<ClusterManagerView> getAllKubernetesCluster() { + return clusterService.getAllClusterView(); + } + + @PreAuthorize("hasRole('ROLE_SYSTEM_ADMIN') || hasRole('ROLE_OPERATOR')") + @PostMapping + public ClusterManagerView createKKubernetesCluster(@RequestPart("file") MultipartFile file, @RequestPart("data") String viewString) { + + ObjectMapper objectMapper = new ObjectMapper(); + ClusterManagerView cluster = null; + try { + cluster = objectMapper.readValue(viewString, ClusterManagerView.class); + log.error("Cluster created"); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } + + return clusterService.readClusterFile(cluster, file); + } + + @PreAuthorize("hasRole('ROLE_SYSTEM_ADMIN') || hasRole('ROLE_OPERATOR')") + @PutMapping("/{id}") + public ClusterManagerView updateKubernetesCluster(@PathVariable Long id, @RequestBody ClusterManagerView view) { + return clusterService.updateCluster(view, id); + } + +} diff --git a/src/main/java/net/geant/nmaas/externalservices/kubernetes/ClusterManagerRepository.java b/src/main/java/net/geant/nmaas/externalservices/kubernetes/ClusterManagerRepository.java new file mode 100644 index 0000000000000000000000000000000000000000..4279b19ddaf11ea9c89569fb4fbe0de2a67820f6 --- /dev/null +++ b/src/main/java/net/geant/nmaas/externalservices/kubernetes/ClusterManagerRepository.java @@ -0,0 +1,9 @@ +package net.geant.nmaas.externalservices.kubernetes; + +import net.geant.nmaas.externalservices.kubernetes.model.ClusterManager; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; + +@Repository +public interface ClusterManagerRepository extends JpaRepository<ClusterManager, Long> { +} diff --git a/src/main/java/net/geant/nmaas/externalservices/kubernetes/ClusterService.java b/src/main/java/net/geant/nmaas/externalservices/kubernetes/ClusterService.java new file mode 100644 index 0000000000000000000000000000000000000000..213c3687089085b6e41c5a241009853f5465daf4 --- /dev/null +++ b/src/main/java/net/geant/nmaas/externalservices/kubernetes/ClusterService.java @@ -0,0 +1,218 @@ +package net.geant.nmaas.externalservices.kubernetes; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.dataformat.yaml.YAMLFactory; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import net.geant.nmaas.externalservices.kubernetes.model.ClusterConfigView; +import net.geant.nmaas.externalservices.kubernetes.model.ClusterManager; +import net.geant.nmaas.externalservices.kubernetes.model.ClusterManagerView; +import net.geant.nmaas.externalservices.kubernetes.model.KClusterDeployment; +import net.geant.nmaas.externalservices.kubernetes.model.KClusterIngress; +import net.geant.nmaas.externalservices.kubernetes.model.KClusterView; +import org.modelmapper.ModelMapper; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.io.ByteArrayInputStream; +import java.io.File; +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; +import java.nio.file.StandardCopyOption; +import java.security.DigestInputStream; +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; +import java.time.OffsetDateTime; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +@Service +@Slf4j +@RequiredArgsConstructor +public class ClusterService { + + private final ClusterManagerRepository clusterManagerRepository; + private final ModelMapper modelMapper = new ModelMapper(); + + private final KubernetesClusterIngressManager kClusterIngressManager; + + private final KubernetesClusterDeploymentManager kClusterDeploymentManager; + + + public ClusterManagerView getClusterView(Long id) { + Optional<ClusterManager> cluster = clusterManagerRepository.findById(id); + + if (cluster.isPresent()) { + return modelMapper.map(cluster.get(), ClusterManagerView.class); + } else { + throw new IllegalArgumentException("Cluster not found"); + } + } + + public List<ClusterManagerView> getAllClusterView() { + List<ClusterManager> clusters = clusterManagerRepository.findAll(); + + return clusters.stream().map(c -> modelMapper.map(c, ClusterManagerView.class)).collect(Collectors.toList()); + } + + public ClusterManager getCluster(Long id) { + Optional<ClusterManager> cluster = clusterManagerRepository.findById(id); + + if (cluster.isPresent()) { + return cluster.get(); + } else { + throw new IllegalArgumentException("Cluster not found"); + } + } + + public File getFileFromCluster(Long id) { + ClusterManager cluster = getCluster(id); + + return new File(cluster.getPathConfigFile()); + } + + public static String saveFileToTmp(MultipartFile file) throws IOException, NoSuchAlgorithmException { + String hash = computeSHA256(file); + + Path tmpDir = Paths.get(System.getProperty("java.io.tmpdir")); + Path filePath = tmpDir.resolve(hash + ".yaml"); + + Files.copy(file.getInputStream(), filePath, StandardCopyOption.REPLACE_EXISTING); + + return filePath.toString(); + } + + public ClusterManagerView saveCluster(ClusterManager entity, MultipartFile file) throws IOException, NoSuchAlgorithmException { + checkRequest(entity); + + String savedPath = saveFileToTmp(file); + log.debug("Filed saved in: " + savedPath); + entity.setPathConfigFile(savedPath); + + ClusterManager cluster = this.clusterManagerRepository.save(entity); + log.debug("Cluster saved: {}", cluster.toString()); + return modelMapper.map(cluster, ClusterManagerView.class); + + } + + public ClusterManagerView readClusterFile(ClusterManagerView view, MultipartFile file) { + checkRequest(view); + + ObjectMapper yamlMapper = new ObjectMapper(new YAMLFactory()); + + try { + ClusterConfigView configView = yamlMapper.readValue(file.getInputStream(), ClusterConfigView.class); + + log.error("Mapped {}", configView.toString()); + + if (configView.getClusters().isEmpty()) { + log.error("No clusters info provided in configuration file"); + } else if (configView.getClusters().size() == 1) { + log.error("One cluster provided, create view and return "); + KClusterDeployment deployment = modelMapper.map(kClusterDeploymentManager.getKClusterDeploymentView(), KClusterDeployment.class); + KClusterIngress ingress = modelMapper.map(kClusterIngressManager.getKClusterIngressView(), KClusterIngress.class); + return saveCluster(ClusterManager.builder() + .name(view.getName()) + .description(view.getDescription()) + .creationDate(OffsetDateTime.now()) + .modificationDate(OffsetDateTime.now()) + .codename(configView.getClusters().stream().findFirst().get().getName()) + .clusterConfigFile(file.toString()) + .deployment(deployment) + .ingress(ingress) + .build(), + file); + + } else { + log.error("More than 1 cluster provided, not implemented yet"); + } + + } catch (IOException e) { + throw new RuntimeException(e); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + + return null; + } + + public ClusterManagerView updateCluster(ClusterManagerView cluster, Long id ) { + Optional<ClusterManager> entity = clusterManagerRepository.findById(id); + + + if (entity.isPresent()) { + checkRequest(entity.get(), cluster, id); + if(entity.get().getId().equals(id) && entity.get().getId().equals(cluster.getId())) { + ClusterManager updated = entity.get(); + updated.setName(cluster.getName()); + updated.setDescription(cluster.getDescription()); + updated.setCodename(cluster.getCodename()); + updated.setModificationDate(OffsetDateTime.now()); + updated.setIngress( modelMapper.map(cluster.getIngress(), KClusterIngress.class) ); + + updated.setDeployment( modelMapper.map(cluster.getDeployment(), KClusterDeployment.class) ); + + updated = clusterManagerRepository.save(updated); + //TODO : implement file update logic + return modelMapper.map( updated, ClusterManagerView.class); + + } + } + + + throw new IllegalArgumentException("Cluster with id: " + id+ " is missing. Can not update."); + } + + private void checkRequest(ClusterManagerView view) { + if (view.getName() == null) { + throw new IllegalArgumentException("Name of the cluster is null"); + } + if (view.getDescription() == null) { + throw new IllegalArgumentException("Description of the cluster is null"); + } + } + + private void checkRequest(ClusterManager entity) { + if (entity.getName() == null) { + throw new IllegalArgumentException("Name of the cluster is null"); + } + if (entity.getCodename() == null) { + throw new IllegalArgumentException("Codename of the cluster is null"); + } + if (entity.getDescription() == null) { + throw new IllegalArgumentException("Description of the cluster is null"); + } + } + + private static String computeSHA256(MultipartFile file) throws IOException, NoSuchAlgorithmException { + MessageDigest digest = MessageDigest.getInstance("SHA-256"); + try (InputStream is = file.getInputStream(); + DigestInputStream dis = new DigestInputStream(is, digest)) { + while (dis.read() != -1) { + } + } + + StringBuilder hexString = new StringBuilder(); + for (byte b : digest.digest()) { + hexString.append(String.format("%02x", b)); + } + return hexString.toString(); + } + + private void checkRequest(ClusterManager entity, ClusterManagerView view, Long id) { + if (view.getName() == null) { + throw new IllegalArgumentException("Name of the cluster is null"); + } + if (view.getCodename() == null) { + throw new IllegalArgumentException("Codename of the cluster is null"); + } + + } + + +} diff --git a/src/main/java/net/geant/nmaas/externalservices/kubernetes/KubernetesClusterController.java b/src/main/java/net/geant/nmaas/externalservices/kubernetes/KubernetesClusterController.java index 55fb4ae3b5a8258e3738c581850355ed4b286b3d..40fabaea73d56d20788b06e0ff5a495442d17c08 100644 --- a/src/main/java/net/geant/nmaas/externalservices/kubernetes/KubernetesClusterController.java +++ b/src/main/java/net/geant/nmaas/externalservices/kubernetes/KubernetesClusterController.java @@ -1,6 +1,7 @@ package net.geant.nmaas.externalservices.kubernetes; import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; import net.geant.nmaas.externalservices.kubernetes.model.KClusterView; import org.springframework.security.access.prepost.PreAuthorize; import org.springframework.transaction.annotation.Propagation; diff --git a/src/main/java/net/geant/nmaas/externalservices/kubernetes/model/ClusterConfigView.java b/src/main/java/net/geant/nmaas/externalservices/kubernetes/model/ClusterConfigView.java new file mode 100644 index 0000000000000000000000000000000000000000..91910be14c7d4511ba95b130a68e54a157f867e2 --- /dev/null +++ b/src/main/java/net/geant/nmaas/externalservices/kubernetes/model/ClusterConfigView.java @@ -0,0 +1,134 @@ +package net.geant.nmaas.externalservices.kubernetes.model; + +import com.fasterxml.jackson.annotation.JsonProperty; +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.util.List; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class ClusterConfigView { + + private String apiVersion; + private String kind; + + @JsonProperty("current-context") + private String currentContext; + + private List<ClusterEntry> clusters; + private List<ContextEntry> contexts; + private ClusterPreferences preferences; + private List<UserEntry> users; + + @Override + public String toString() { + StringBuilder sb = new StringBuilder(); + sb.append("ClusterConfigView {").append("\n") + .append(" apiVersion: ").append(apiVersion).append("\n") + .append(" kind: ").append(kind).append("\n") + .append(" currentContext: ").append(currentContext).append("\n") + .append(" clusters: ").append(clusters).append("\n") + .append(" contexts: ").append(contexts).append("\n") + .append(" preferences: ").append(preferences).append("\n") + .append(" users: ").append(users).append("\n") + .append("}"); + return sb.toString(); + } + @Getter + @Setter + @AllArgsConstructor + @NoArgsConstructor + public static class ClusterEntry { + private Cluster cluster; + private String name; + + @Override + public String toString() { + return "\n ClusterEntry { " + + "name: '" + name + "', " + + "cluster: " + cluster + " }"; + } + } + + + @Getter + @Setter + @AllArgsConstructor + @NoArgsConstructor + public static class Cluster { + @JsonProperty("certificate-authority-data") + private String certificateAuthorityData; + private String server; + + @Override + public String toString() { + return "{ server: '" + server + "', certificateAuthorityData: " + certificateAuthorityData + " }"; + } + } + + @Getter + @Setter + @AllArgsConstructor + @NoArgsConstructor + public static class ContextEntry { + private Context context; + private String name; + + @Override + public String toString() { + return "\n ContextEntry { name: '" + name + "', context: " + context + " }"; + } + } + + @Getter + @Setter + @AllArgsConstructor + @NoArgsConstructor + public static class Context { + private String cluster; + private String user; + + @Override + public String toString() { + return "{ cluster: '" + cluster + "', user: '" + user + "' }"; + } + } + + @Getter + @Setter + @AllArgsConstructor + public static class ClusterPreferences { + } + + @Getter + @Setter + @AllArgsConstructor + @NoArgsConstructor + public static class UserEntry { + private String name; + private UserToken user; + + @Override + public String toString() { + return "\n UserEntry { name: '" + name + "', user: " + user + " }"; + } + } + + @Getter + @Setter + @AllArgsConstructor + @NoArgsConstructor + public static class UserToken { + private String token; + + @Override + public String toString() { + return "{ token: " + token + " }"; + } + } +} diff --git a/src/main/java/net/geant/nmaas/externalservices/kubernetes/model/ClusterManager.java b/src/main/java/net/geant/nmaas/externalservices/kubernetes/model/ClusterManager.java new file mode 100644 index 0000000000000000000000000000000000000000..c80bf1f332a622d55922b559f01aeb5936170a78 --- /dev/null +++ b/src/main/java/net/geant/nmaas/externalservices/kubernetes/model/ClusterManager.java @@ -0,0 +1,77 @@ +package net.geant.nmaas.externalservices.kubernetes.model; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Column; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToOne; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.EqualsAndHashCode; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.time.OffsetDateTime; + +@Entity +@NoArgsConstructor +@AllArgsConstructor +@Getter +@Setter +@Builder +public class ClusterManager { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false) + private String name; + + @Column(nullable = false) + private String codename; + + @Column(nullable = false) + private String description; + + @Column(nullable = false) + private OffsetDateTime creationDate; + + @Column(nullable = false) + private OffsetDateTime modificationDate; + + @Column(nullable = false) + private String clusterConfigFile; + + @Column(nullable = false) + private String pathConfigFile; + + @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true) + @JoinColumn(name = "ingress_id", referencedColumnName = "id") + private KClusterIngress ingress; + + @OneToOne(cascade = CascadeType.ALL, orphanRemoval = true) + @JoinColumn(name = "deployment_id", referencedColumnName = "id") + private KClusterDeployment deployment; + + @Override + public String toString() { + return "ClusterManager{" + + "id=" + id + + ", name='" + name + '\'' + + ", codename='" + codename + '\'' + + ", description='" + description + '\'' + + ", creationDate=" + creationDate + + ", modificationDate=" + modificationDate + + ", clusterConfigFile='" + clusterConfigFile + '\'' + + ", pathConfigFile='" + pathConfigFile + '\'' + + ", ingress=" + (ingress != null ? ingress.getId() : "null") + + ", deployment=" + (deployment != null ? deployment.getId() : "null") + + '}'; + } + +} diff --git a/src/main/java/net/geant/nmaas/externalservices/kubernetes/model/ClusterManagerView.java b/src/main/java/net/geant/nmaas/externalservices/kubernetes/model/ClusterManagerView.java new file mode 100644 index 0000000000000000000000000000000000000000..cda78bd3c09481716fd606b4de011e247890a9c1 --- /dev/null +++ b/src/main/java/net/geant/nmaas/externalservices/kubernetes/model/ClusterManagerView.java @@ -0,0 +1,37 @@ +package net.geant.nmaas.externalservices.kubernetes.model; + + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +import java.time.OffsetDateTime; + +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +@Builder +public class ClusterManagerView { + + private Long id; + + private String name; + + private String codename; + + private String description; + + private OffsetDateTime creationDate; + + private OffsetDateTime modificationDate; + + private String pathConfigFile; + + private KClusterView.KClusterIngressView ingress; + + private KClusterView.KClusterDeploymentView deployment; + +} diff --git a/src/main/java/net/geant/nmaas/externalservices/kubernetes/model/KClusterDeployment.java b/src/main/java/net/geant/nmaas/externalservices/kubernetes/model/KClusterDeployment.java new file mode 100644 index 0000000000000000000000000000000000000000..c5cee559950464bfe1d12808830d046c0dd6bc6d --- /dev/null +++ b/src/main/java/net/geant/nmaas/externalservices/kubernetes/model/KClusterDeployment.java @@ -0,0 +1,47 @@ +package net.geant.nmaas.externalservices.kubernetes.model; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.OneToOne; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Entity +@Setter +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class KClusterDeployment { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private NamespaceConfigOption namespaceConfigOption; + + private String defaultNamespace; + + private String defaultStorageClass; + + private String smtpServerHostname; + + private Integer smtpServerPort; + + private String smtpServerUsername; + + private String smtpServerPassword; + + private String smtpFromDefaultDomain; + + private Boolean forceDedicatedWorkers; + + @OneToOne(mappedBy = "deployment", cascade = CascadeType.ALL) + private ClusterManager clusterManager; +} diff --git a/src/main/java/net/geant/nmaas/externalservices/kubernetes/model/KClusterIngress.java b/src/main/java/net/geant/nmaas/externalservices/kubernetes/model/KClusterIngress.java new file mode 100644 index 0000000000000000000000000000000000000000..1c6c5d5eb651df43e67b9d33b9552873c0f38acc --- /dev/null +++ b/src/main/java/net/geant/nmaas/externalservices/kubernetes/model/KClusterIngress.java @@ -0,0 +1,54 @@ +package net.geant.nmaas.externalservices.kubernetes.model; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.Id; +import jakarta.persistence.OneToOne; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Entity +@Setter +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class KClusterIngress { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private IngressControllerConfigOption controllerConfigOption; + + private String supportedIngressClass; + + private String publicIngressClass; + + private String controllerChartName; + + private String controllerChartArchive; + + private IngressResourceConfigOption resourceConfigOption; + + private String externalServiceDomain; + + private String publicServiceDomain; + + private Boolean tlsSupported; + + private IngressCertificateConfigOption certificateConfigOption; + + private String issuerOrWildcardName; + + private Boolean ingressPerDomain; + + + @OneToOne(mappedBy = "ingress", cascade = CascadeType.ALL) + private ClusterManager clusterManager; +} diff --git a/src/main/java/net/geant/nmaas/externalservices/kubernetes/model/KClusterView.java b/src/main/java/net/geant/nmaas/externalservices/kubernetes/model/KClusterView.java index ce62c2088f4c1f1bc10682dca44ec692bffd63a1..cfe1084e148faead01e45ca8c292ab3f8977959d 100644 --- a/src/main/java/net/geant/nmaas/externalservices/kubernetes/model/KClusterView.java +++ b/src/main/java/net/geant/nmaas/externalservices/kubernetes/model/KClusterView.java @@ -19,6 +19,8 @@ public class KClusterView { @Setter public static class KClusterDeploymentView { + private Long id; + private NamespaceConfigOption namespaceConfigOption; private String defaultNamespace; @@ -44,6 +46,8 @@ public class KClusterView { @Setter public static class KClusterIngressView { + private Long id; + private IngressControllerConfigOption controllerConfigOption; private String supportedIngressClass; diff --git a/src/test/java/net/geant/nmaas/externalservices/kubernetes/ClusterServiceTest.java b/src/test/java/net/geant/nmaas/externalservices/kubernetes/ClusterServiceTest.java new file mode 100644 index 0000000000000000000000000000000000000000..b3172e49f35b85452292a684ce99c12b83fe423a --- /dev/null +++ b/src/test/java/net/geant/nmaas/externalservices/kubernetes/ClusterServiceTest.java @@ -0,0 +1,110 @@ +package net.geant.nmaas.externalservices.kubernetes; + +import net.geant.nmaas.externalservices.kubernetes.model.ClusterManager; +import net.geant.nmaas.externalservices.kubernetes.model.ClusterManagerView; +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 org.springframework.web.multipart.MultipartFile; + +import java.io.IOException; +import java.security.NoSuchAlgorithmException; +import java.util.List; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.*; +import static org.mockito.Mockito.*; + +class ClusterServiceTest { + + @Mock + private ClusterManagerRepository clusterManagerRepository; + + @Mock + private KubernetesClusterIngressManager kClusterIngressManager; + + @Mock + private KubernetesClusterDeploymentManager kClusterDeploymentManager; + + @InjectMocks + private ClusterService clusterService; + + private ModelMapper modelMapper = new ModelMapper(); + + @BeforeEach + void setUp() { + MockitoAnnotations.openMocks(this); + } + + @Test + void getClusterView_validId_returnsClusterManagerView() { + Long id = 1L; + ClusterManager clusterManager = ClusterManager.builder().id(id).name("Cluster").description("Description").build(); + ClusterManagerView clusterManagerView = modelMapper.map(clusterManager, ClusterManagerView.class); + + when(clusterManagerRepository.findById(id)).thenReturn(Optional.of(clusterManager)); + + ClusterManagerView result = clusterService.getClusterView(id); + + assertEquals(clusterManagerView.getName(), result.getName()); + verify(clusterManagerRepository, times(1)).findById(id); + } + + @Test + void getClusterView_invalidId_throwsException() { + Long id = 100L; + when(clusterManagerRepository.findById(id)).thenReturn(Optional.empty()); + + assertThrows(IllegalArgumentException.class, () -> clusterService.getClusterView(id)); + verify(clusterManagerRepository, times(1)).findById(id); + } + + @Test + void getAllClusterView_returnsClusterManagerViews() { + ClusterManager cluster1 = ClusterManager.builder().id(1L).name("Cluster1").build(); + ClusterManager cluster2 = ClusterManager.builder().id(2L).name("Cluster2").build(); + + when(clusterManagerRepository.findAll()).thenReturn(List.of(cluster1, cluster2)); + + List<ClusterManagerView> result = clusterService.getAllClusterView(); + + assertEquals(2, result.size()); + verify(clusterManagerRepository, 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)); +// } +} \ No newline at end of file diff --git a/src/test/resources/config.yaml b/src/test/resources/config.yaml new file mode 100644 index 0000000000000000000000000000000000000000..eb5f83c1d43ab3ac763a1ddd5bd93c2af5247fc0 --- /dev/null +++ b/src/test/resources/config.yaml @@ -0,0 +1,18 @@ +apiVersion: v1 +clusters: +- cluster: + certificate-authority-data: <ca-data-here> + server: https://your-k8s-cluster.com + name: <cluster-name> +contexts: +- context: + cluster: <cluster-name> + user: <cluster-name-user> + name: <cluster-name> +current-context: <cluster-name> +kind: Config +preferences: {} +users: +- name: <cluster-name-user> + user: + token: <secret-token-here> diff --git a/src/test/shell/data/i18n/de.json b/src/test/shell/data/i18n/de.json index 4cce919a9cac8382e13b9c588fabc3776f579988..5fc50ea200a54bc366aadf9296e4a8aea42a5fed 100644 --- a/src/test/shell/data/i18n/de.json +++ b/src/test/shell/data/i18n/de.json @@ -169,7 +169,16 @@ "SMTP_SERVER_HOSTNAME": "Hostname des SMTP-Servers", "SMTP_SERVER_PORT": "SMTP-Serverport", "SMTP_SERVER_USERNAME": "SMTP-Server Benutzername", - "SMTP_SERVER_PASSWORD": "SMTP-Server Passwort" + "SMTP_SERVER_PASSWORD": "SMTP-Server Passwort", + "TITLE_GENERAL" : "General cluster information", + "GENERAL" : "Basic information", + "NAME" : "Cluster name", + "CODENAME" : "Cluster codename", + "ID" : "Cluster id", + "DESCRIPTION" : "Description", + "CREATION_DATE" : "Creation date", + "MODIFICATION_DATE" : "Modification date", + "PATH_TO_CONFIG" : "Path to config file" }, "GITLAB": { "TITLE": "GitLab Konfiguration", diff --git a/src/test/shell/data/i18n/en.json b/src/test/shell/data/i18n/en.json index 8dd9565922a6b8de7d1174ac468ed59421334413..c9e5d44aae23b702ff7c478537b000c1f57c4764 100644 --- a/src/test/shell/data/i18n/en.json +++ b/src/test/shell/data/i18n/en.json @@ -169,7 +169,16 @@ "SMTP_SERVER_HOSTNAME": "SMTP server hostname", "SMTP_SERVER_PORT": "SMTP server port", "SMTP_SERVER_USERNAME": "SMTP server username", - "SMTP_SERVER_PASSWORD": "SMTP server password" + "SMTP_SERVER_PASSWORD": "SMTP server password", + "TITLE_GENERAL" : "General cluster information", + "GENERAL" : "Basic information", + "NAME" : "Cluster name", + "CODENAME" : "Cluster codename", + "ID" : "Cluster id", + "DESCRIPTION" : "Description", + "CREATION_DATE" : "Creation date", + "MODIFICATION_DATE" : "Modification date", + "PATH_TO_CONFIG" : "Path to config file" }, "GITLAB": { "TITLE": "GitLab configuration", diff --git a/src/test/shell/data/i18n/fr.json b/src/test/shell/data/i18n/fr.json index ae72dda76bce23267c94aad13a36c485484d7a6d..62466230380e4afedddeb1c29f834f8c42013f0a 100644 --- a/src/test/shell/data/i18n/fr.json +++ b/src/test/shell/data/i18n/fr.json @@ -169,7 +169,16 @@ "SMTP_SERVER_HOSTNAME": "Nom d'hôte du serveur SMTP", "SMTP_SERVER_PORT": "Port du serveur SMTP", "SMTP_SERVER_USERNAME": "Nom d'utilisateur du serveur SMTP", - "SMTP_SERVER_PASSWORD": "Mot de passe du serveur SMTP" + "SMTP_SERVER_PASSWORD": "Mot de passe du serveur SMTP", + "TITLE_GENERAL" : "General cluster information", + "GENERAL" : "Basic information", + "NAME" : "Cluster name", + "CODENAME" : "Cluster codename", + "ID" : "Cluster id", + "DESCRIPTION" : "Description", + "CREATION_DATE" : "Creation date", + "MODIFICATION_DATE" : "Modification date", + "PATH_TO_CONFIG" : "Path to config file" }, "GITLAB": { "TITLE": "Configuration de GitLab", diff --git a/src/test/shell/data/i18n/pl.json b/src/test/shell/data/i18n/pl.json index 756a41814e2570e2e2a4d1ea75913d20b405b9d8..2ebaa565967cb11a01ce8852a589307c34542530 100644 --- a/src/test/shell/data/i18n/pl.json +++ b/src/test/shell/data/i18n/pl.json @@ -169,7 +169,16 @@ "SMTP_SERVER_HOSTNAME": "Nazwa serwera SMTP", "SMTP_SERVER_PORT": "Port serwera SMTP", "SMTP_SERVER_USERNAME": "Nazwa użytkownika serwera SMTP", - "SMTP_SERVER_PASSWORD": "Hasło użytkownika serwera SMTP" + "SMTP_SERVER_PASSWORD": "Hasło użytkownika serwera SMTP", + "TITLE_GENERAL" : "Podstawowe ustawienia klastra", + "GENERAL" : "Ustawienia klastra", + "NAME" : "Nazwa klastra", + "CODENAME" : "Nazwa kodowa klastra", + "ID" : "Identyfikator", + "DESCRIPTION" : "Opis", + "CREATION_DATE" : "Data utworzenia", + "MODIFICATION_DATE" : "Data modyfikacji", + "PATH_TO_CONFIG" : "Ścieżka do pliku konfiguracyjnego" }, "GITLAB": { "TITLE": "Konfiguracja GitLab",