Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -104,8 +104,8 @@ dependencies {
implementation 'io.fabric8:kubernetes-client:6.10.0'
implementation 'io.fabric8:kubernetes-model-metrics:6.10.0'

// implementation 'com.marcnuri.helm-java:helm-java:0.0.13'
implementation 'com.marcnuri.helm-java:helm-java:0.0.9'
implementation 'com.marcnuri.helm-java:helm-java:0.0.13'
// implementation 'com.marcnuri.helm-java:helm-java:0.0.9'

implementation 'com.github.docker-java:docker-java:3.3.0'
implementation 'com.github.docker-java:docker-java-transport-httpclient5:3.3.0'
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package kr.co.mcmp.externalrepo;

import kr.co.mcmp.externalrepo.model.ArtifactHubPackage;
import kr.co.mcmp.externalrepo.model.ArtifactHubRespository;
import kr.co.mcmp.externalrepo.model.ArtifactHubRepository;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
Expand All @@ -13,7 +13,7 @@ public interface ArtifactHubInteface {

@GetMapping(value="/api/v1/repositories/search")
//https://artifacthub.io/api/v1/repositories/search?offset=0&limit=5&kind=0&name=argo
List<ArtifactHubRespository> searchRepository(@RequestParam("name") String helm);
List<ArtifactHubRepository> searchRepository(@RequestParam("name") String helm);

@GetMapping(value="/api/v1/packages/search")
ArtifactHubPackage searchPackage(@RequestParam("ts_query_web") String helm, @RequestParam(required=false, value="kind", defaultValue="0") String kind);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
package kr.co.mcmp.externalrepo;

import kr.co.mcmp.externalrepo.model.ArtifactHubPackage;
import kr.co.mcmp.externalrepo.model.ArtifactHubRespository;
import kr.co.mcmp.externalrepo.model.ArtifactHubRepository;
import kr.co.mcmp.externalrepo.model.DockerHubCatalog;

import java.util.List;


public interface ExternalRepoInterface {

List<ArtifactHubRespository> searchArtifactHubRepository(String keyword);
List<ArtifactHubRepository> searchArtifactHubRepository(String keyword);

ArtifactHubPackage searchArtifactHubPackage(String keyword);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package kr.co.mcmp.externalrepo;

import kr.co.mcmp.externalrepo.model.ArtifactHubPackage;
import kr.co.mcmp.externalrepo.model.ArtifactHubRespository;
import kr.co.mcmp.externalrepo.model.ArtifactHubRepository;
import kr.co.mcmp.externalrepo.model.DockerHubCatalog;
import lombok.extern.slf4j.Slf4j;

Expand All @@ -20,7 +20,7 @@ public class ExternalRepoService {
@Autowired
private DockerHubInterface dockerInt;

public List<ArtifactHubRespository> searchArtifactHubRepository(String keyword){
public List<ArtifactHubRepository> searchArtifactHubRepository(String keyword){
return artfInt.searchRepository(keyword);

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import lombok.Data;

@Data
public class ArtifactHubRespository {
public class ArtifactHubRepository {

public String repository_id;
public String name;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,9 +65,7 @@ private List<DeploymentHistory> getActiveDeployments() {
.max(Comparator.comparing(DeploymentHistory::getExecutedAt))
.orElse(null)))
.filter(Objects::nonNull)
.filter(d -> ("RUN".equalsIgnoreCase(d.getActionType().name())
|| "INSTALL".equalsIgnoreCase(d.getActionType().name()))
&& "SUCCESS".equalsIgnoreCase(d.getStatus()))
.filter(d -> ("RUN".equalsIgnoreCase(d.getActionType().name()) || "INSTALL".equalsIgnoreCase(d.getActionType().name())) && "SUCCESS".equalsIgnoreCase(d.getStatus()))
.collect(Collectors.toList());
}

Expand Down Expand Up @@ -102,17 +100,14 @@ private void updateContainerHealth(DeploymentHistory deployment) {

private boolean isThresholdExceeded(SoftwareCatalog catalog, ContainerHealthInfo healthInfo) {
if (healthInfo.getCpuUsage() != null && healthInfo.getMemoryUsage() != null) {
boolean cpuExceeded = catalog.getCpuThreshold() != null
&& healthInfo.getCpuUsage() > catalog.getCpuThreshold();
boolean memoryExceeded = catalog.getMemoryThreshold() != null
&& healthInfo.getMemoryUsage() > catalog.getMemoryThreshold();
boolean cpuExceeded = catalog.getCpuThreshold() != null && healthInfo.getCpuUsage() > catalog.getCpuThreshold();
boolean memoryExceeded = catalog.getMemoryThreshold() != null && healthInfo.getMemoryUsage() > catalog.getMemoryThreshold();
return cpuExceeded || memoryExceeded;
}
return false;
}

private void updateApplicationStatus(ApplicationStatus status, DeploymentHistory deployment,
ContainerHealthInfo healthInfo) {
private void updateApplicationStatus(ApplicationStatus status, DeploymentHistory deployment, ContainerHealthInfo healthInfo) {
status.setCatalog(deployment.getCatalog());
status.setStatus(healthInfo.getStatus());
status.setDeploymentType(deployment.getDeploymentType());
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,22 @@
package kr.co.mcmp.softwarecatalog.kubernetes.service;

import java.io.IOException;
import java.io.StringWriter;
import java.net.URI;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;

import org.springframework.stereotype.Service;
import org.yaml.snakeyaml.DumperOptions;
import org.yaml.snakeyaml.Yaml;

import com.marcnuri.helm.Helm;
import com.marcnuri.helm.InstallCommand;
import com.marcnuri.helm.Release;

import io.fabric8.kubernetes.client.Config;
import io.fabric8.kubernetes.client.KubernetesClient;
import kr.co.mcmp.ape.cbtumblebug.api.CbtumblebugRestApi;
import kr.co.mcmp.ape.cbtumblebug.dto.K8sClusterDto;
import kr.co.mcmp.softwarecatalog.SoftwareCatalog;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -24,23 +25,46 @@
@Slf4j
@RequiredArgsConstructor
public class HelmChartService {
private final CbtumblebugRestApi api;

public Release deployHelmChart(KubernetesClient client, String namespace, SoftwareCatalog catalog,
String clusterName) {
Path tempConfigFile = null;
K8sClusterDto dto = api.getK8sClusterByName(namespace, clusterName);


public String convertConfigToYaml(Config config) {
// SnakeYAML 설정
DumperOptions options = new DumperOptions();
options.setDefaultFlowStyle(DumperOptions.FlowStyle.BLOCK); // 블록 스타일 (가독성이 좋음)
Yaml yaml = new Yaml(options);

// Config 객체를 YAML로 직렬화
StringWriter writer = new StringWriter();
yaml.dump(config, writer);

return writer.toString();
}
public Release deployHelmChart(KubernetesClient client, String namespace, SoftwareCatalog catalog) {
Path tempKubeconfigPath = null;

try {
// Helm repository 추가
addHelmRepository(catalog);
String kubeconfig = dto.getCspViewK8sClusterDetail().getAccessInfo().getKubeconfig();
tempConfigFile = createTempKubeconfigFile(kubeconfig);

Config kubeConfig = client.getConfiguration();
log.info("===========================kubeconfig");
log.info(kubeConfig.toString()); // Config 객체 로그 확인
log.info("===========================================");

// Config 객체를 YAML로 변환
String kubeconfigYaml = convertConfigToYaml(kubeConfig); // Config를 YAML 형식으로 직렬화
log.info(kubeconfigYaml);
// 임시 kubeconfig 파일 생성
tempKubeconfigPath = createTempKubeconfigFile(kubeConfig.toString()); // YAML 파일로 저장

// Helm 설치 명령어 생성
InstallCommand installCommand = Helm.install(
catalog.getHelmChart().getRepositoryName() + "/" + catalog.getHelmChart().getChartName())
.withKubeConfig(tempConfigFile)
.withKubeConfig(tempKubeconfigPath) // Path 타입으로 kubeconfig 전달
.withName(catalog.getHelmChart().getChartName())
.withNamespace(namespace)
// .withVersion(catalog.getHelmChart().getChartVersion())
.withVersion(catalog.getHelmChart().getChartVersion())
.set("replicaCount", catalog.getMinReplicas())
.set("image.repository", catalog.getHelmChart().getImageRepository())
.set("image.tag", "latest")
Expand All @@ -49,81 +73,86 @@ public Release deployHelmChart(KubernetesClient client, String namespace, Softwa
.set("resources.requests.cpu", catalog.getMinCpu().toString())
.set("resources.requests.memory", catalog.getMinMemory() + "Mi")
.set("resources.limits.cpu", catalog.getRecommendedCpu().toString())
.set("resources.limits.memory", catalog.getRecommendedMemory() + "Mi")
.set("persistence.enabled", false)
.set("securityContext.enabled", false)
.set("serviceAccount.create", true)
// .with(300)
.waitReady();
.set("resources.limits.memory", catalog.getRecommendedMemory() + "Mi");

// HPA 설정 추가
if (Boolean.TRUE.equals(catalog.getHpaEnabled())) {
installCommand
.set("autoscaling.enabled", true)
.set("autoscaling.minReplicas", catalog.getMinReplicas())
.set("autoscaling.maxReplicas", catalog.getMaxReplicas())
.set("autoscaling.targetCPUUtilizationPercentage", catalog.getCpuThreshold())
.set("autoscaling.targetMemoryUtilizationPercentage", catalog.getMemoryThreshold());
.set("autoscaling.enabled", true)
.set("autoscaling.minReplicas", catalog.getMinReplicas())
.set("autoscaling.maxReplicas", catalog.getMaxReplicas())
.set("autoscaling.targetCPUUtilizationPercentage", catalog.getCpuThreshold().intValue())
.set("autoscaling.targetMemoryUtilizationPercentage", catalog.getMemoryThreshold().intValue());
}

// Helm 차트 설치 실행
Release result = installCommand.call();
log.info("Helm Chart '{}' 배포 완료 - namespace: {}",
catalog.getHelmChart().getChartName(),
namespace);

log.info("Helm Chart '{}' 버전 '{}'가 네임스페이스 '{}'에 배포됨 (HPA: {})",
catalog.getHelmChart().getChartName(),
"latest",
namespace,
catalog.getHpaEnabled());
return result;

} catch (Exception e) {
log.error("Helm Chart 배포 중 오류 발생", e);
throw new RuntimeException("Helm Chart 배포 실패", e);
} finally {
deleteTempFile(tempConfigFile);
// 임시 kubeconfig 파일 삭제
if (tempKubeconfigPath != null) {
try {
deleteTempFile(tempKubeconfigPath);
} catch (IOException e) {
log.warn("임시 kubeconfig 파일 삭제 실패: {}", tempKubeconfigPath, e);
}
}
}
}

public void uninstallHelmChart(String namespace, SoftwareCatalog catalog) {
try {
String result = Helm.uninstall(catalog.getHelmChart().getChartName())
.withNamespace(namespace)
.call();

boolean deleted = result != null && !result.isEmpty();

if (deleted) {
log.info("Helm Release '{}' 가 네임스페이스 '{}'에서 삭제됨",
catalog.getHelmChart().getChartName(), namespace);
} else {
log.warn("Helm Release '{}' 삭제 실패",
catalog.getHelmChart().getChartName());
}
} catch (Exception e) {
log.error("Helm Release 삭제 중 오류 발생", e);
throw new RuntimeException("Helm Release 삭제 실패", e);
}
}

private void addHelmRepository(SoftwareCatalog catalog) throws Exception {
Helm.repo().add()
.withName(catalog.getHelmChart().getRepositoryName())
.withUrl(URI.create(catalog.getHelmChart().getChartRepositoryUrl()))
.call();
.withName(catalog.getHelmChart().getRepositoryName())
.withUrl(URI.create(catalog.getHelmChart().getChartRepositoryUrl()))
.call();
Helm.repo().update();
}

// kubeconfig 형식이 유효한지 확인하는 메서드
private boolean isValidKubeconfig(String kubeconfig) {
return kubeconfig.contains("apiVersion") && kubeconfig.contains("kind");
}

// 임시 kubeconfig 파일 생성
private Path createTempKubeconfigFile(String kubeconfig) throws IOException {
Path tempFile = Files.createTempFile("kubeconfig", ".yaml");
Files.write(tempFile, kubeconfig.getBytes(StandardCharsets.UTF_8));
return tempFile;
}

private void deleteTempFile(Path tempFile) {
if (tempFile != null) {
try {
Files.deleteIfExists(tempFile);
} catch (IOException e) {
log.warn("임시 파일 삭제 실패: {}", tempFile, e);
}
}
}

public void uninstallHelmChart(String namespace, SoftwareCatalog catalog, String clusterName) {
Path tempConfigFile = null;
try {
K8sClusterDto dto = api.getK8sClusterByName(namespace, clusterName);
String kubeconfig = dto.getCspViewK8sClusterDetail().getAccessInfo().getKubeconfig();
tempConfigFile = createTempKubeconfigFile(kubeconfig);

String result = Helm.uninstall(catalog.getHelmChart().getChartName())
.withKubeConfig(tempConfigFile)
.withNamespace(namespace)
.call();

log.info("Helm Release '{}' 삭제 완료 - namespace: {}",
catalog.getHelmChart().getChartName(),
namespace);

} catch (Exception e) {
log.error("Helm Release 삭제 실패", e);
throw new RuntimeException("Helm Release 삭제 실패", e);
} finally {
deleteTempFile(tempConfigFile);
}
// 임시 kubeconfig 파일 삭제
private void deleteTempFile(Path tempFile) throws IOException {
Files.deleteIfExists(tempFile);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ public DeploymentHistory deployApplication(String namespace, String clusterName,
// KubernetesClient client = clientFactory.getClient(namespace, clusterName);
namespaceService.ensureNamespaceExists(client, namespace);

Release result = helmChartService.deployHelmChart(client, namespace, catalog, clusterName);
Release result = helmChartService.deployHelmChart(client, namespace, catalog);

String podStatus = KubernetesUtils.getPodStatus(client, namespace, catalog.getHelmChart().getChartName());
Integer servicePort = KubernetesUtils.getServicePort(client, namespace,
Expand Down Expand Up @@ -68,7 +68,7 @@ public DeploymentHistory stopApplication(String namespace, String clusterName, S
try {
KubernetesClient client = clientFactory.getClient(namespace, clusterName);

helmChartService.uninstallHelmChart(namespace, catalog, clusterName);
helmChartService.uninstallHelmChart(namespace, catalog);

String podStatus = KubernetesUtils.getPodStatus(client, namespace, catalog.getHelmChart().getChartName());
Integer servicePort = KubernetesUtils.getServicePort(client, namespace,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ public void stopApplication(String namespace, String clusterName, SoftwareCatalo

public void uninstallApplication(String namespace, String clusterName, SoftwareCatalog catalog, String username) {
try {
helmChartService.uninstallHelmChart(namespace, catalog,clusterName);
// helmChartService.uninstallHelmChart(namespace, catalog,clusterName);
} catch (Exception e) {
log.error("애플리케이션 제거 중 오류 발생", e);
throw new RuntimeException("애플리케이션 제거 실패", e);
Expand Down
4 changes: 2 additions & 2 deletions src/main/resources/import.sql
Original file line number Diff line number Diff line change
Expand Up @@ -335,8 +335,8 @@ VALUES
-- Insert into oss
INSERT INTO oss (oss_idx, oss_type_idx, oss_name, oss_desc, oss_url, oss_username, oss_password)
VALUES
(1, 1, 'NEXUS', 'Application Repository', 'http://mc-application-manager-sonatype-nexus:8081', admin, 123456);
(1, 1, 'NEXUS', 'Application Repository', 'http://mc-application-manager-sonatype-nexus:8081', 'admin', 'cZr3xQjjYfwMQNBO/ebJbQ==');
INSERT INTO oss (oss_idx, oss_type_idx, oss_name, oss_desc, oss_url, oss_username, oss_password)
VALUES
(2, 2, 'Ape', 'Application Provisioning Engine', 'http://mc-application-manager-jenkins:8080', admin, 123456);
(2, 2, 'Ape', 'Application Provisioning Engine', 'http://mc-application-manager-jenkins:8080', 'admin', 'cZr3xQjjYfwMQNBO/ebJbQ==');

Loading