From 7a5661e2d5a22a571755319d6fc0fc7fd5de6a41 Mon Sep 17 00:00:00 2001 From: pbccc Date: Fri, 23 Aug 2024 13:19:09 +0900 Subject: [PATCH 1/3] test --- Dockerfile | 3 + README.md | 5 +- build.gradle | 10 + docker-compose.yaml | 85 + docker-run.sh | 4 +- docs/func_tree.xlsx | Bin 0 -> 10785 bytes .../mcmp/ApplicationManagerApplication.java | 2 + .../component/CommonComponentController.java | 62 + .../CommonRepositoryController.java | 67 + .../kr/co/mcmp/catalog/CatalogController.java | 29 +- .../kr/co/mcmp/catalog/CatalogRefDTO.java | 4 +- .../kr/co/mcmp/catalog/CatalogRefEntity.java | 2 +- .../kr/co/mcmp/catalog/CatalogRepository.java | 4 +- .../kr/co/mcmp/catalog/CatalogService.java | 14 +- .../dto/oss/component/CommonComponent.java | 70 + .../oss/component/CommonUploadComponent.java | 40 + .../dto/oss/repository/CommonFormatType.java | 20 + .../dto/oss/repository/CommonRepository.java | 91 + .../externalrepo/ArtifactHubInteface.java | 18 +- .../externalrepo/ExternalRepoController.java | 24 +- .../externalrepo/ExternalRepoService.java | 23 +- src/main/java/kr/co/mcmp/manifest/K8S.java | 134 + .../kr/co/mcmp/manifest/K8SDeployService.java | 27 + .../mcmp/manifest/K8SDeployYamlGenerator.java | 321 +- .../mcmp/manifest/YamlGenerateController.java | 9 +- .../kr/co/mcmp/manifest/k8s/K8SDeployDTO.java | 10 +- src/main/java/kr/co/mcmp/oss/dto/OssDto.java | 4 + .../java/kr/co/mcmp/oss/dto/OssTypeDto.java | 4 + .../co/mcmp/oss/repository/OssRepository.java | 2 +- .../co/mcmp/oss/service/OssServiceImpl.java | 28 +- .../oss/component/CommonComponentFactory.java | 21 + .../oss/component/CommonComponentService.java | 17 + .../CommonModuleComponentService.java | 39 + .../nexus/NexusComponentAdapterClient.java | 152 + .../nexus/NexusComponentAdapterService.java | 59 + .../nexus/NexusComponentService.java | 56 + .../CommonModuleRepositoryService.java | 43 + .../repository/CommonRepositoryFactory.java | 21 + .../repository/CommonRepositoryService.java | 20 + .../nexus/NexusRepositoryAdapterClient.java | 156 + .../nexus/NexusRepositoryAdapterService.java | 39 + .../nexus/NexusRepositoryService.java | 53 + .../java/kr/co/mcmp/util/AES256Utils.java | 77 + .../java/kr/co/mcmp/util/Base64Utils.java | 50 + src/main/resources/application.yaml | 3 + src/main/resources/import.sql | 57 +- src/main/resources/static/favicon.ico | Bin 0 -> 13445 bytes ...software-catalog-list-entity-workflow.html | 2 +- .../tabler/software-catalog-list-entity.html | 19 +- .../static/tabler/software-catalog.html | 138 +- swagger.json | 9708 ++++++++--------- 51 files changed, 6563 insertions(+), 5283 deletions(-) create mode 100644 Dockerfile create mode 100644 docker-compose.yaml create mode 100644 docs/func_tree.xlsx create mode 100644 src/main/java/kr/co/mcmp/api/oss/component/CommonComponentController.java create mode 100644 src/main/java/kr/co/mcmp/api/oss/repository/CommonRepositoryController.java create mode 100644 src/main/java/kr/co/mcmp/dto/oss/component/CommonComponent.java create mode 100644 src/main/java/kr/co/mcmp/dto/oss/component/CommonUploadComponent.java create mode 100644 src/main/java/kr/co/mcmp/dto/oss/repository/CommonFormatType.java create mode 100644 src/main/java/kr/co/mcmp/dto/oss/repository/CommonRepository.java create mode 100644 src/main/java/kr/co/mcmp/manifest/K8S.java create mode 100644 src/main/java/kr/co/mcmp/manifest/K8SDeployService.java create mode 100644 src/main/java/kr/co/mcmp/service/oss/component/CommonComponentFactory.java create mode 100644 src/main/java/kr/co/mcmp/service/oss/component/CommonComponentService.java create mode 100644 src/main/java/kr/co/mcmp/service/oss/component/CommonModuleComponentService.java create mode 100644 src/main/java/kr/co/mcmp/service/oss/component/nexus/NexusComponentAdapterClient.java create mode 100644 src/main/java/kr/co/mcmp/service/oss/component/nexus/NexusComponentAdapterService.java create mode 100644 src/main/java/kr/co/mcmp/service/oss/component/nexus/NexusComponentService.java create mode 100644 src/main/java/kr/co/mcmp/service/oss/repository/CommonModuleRepositoryService.java create mode 100644 src/main/java/kr/co/mcmp/service/oss/repository/CommonRepositoryFactory.java create mode 100644 src/main/java/kr/co/mcmp/service/oss/repository/CommonRepositoryService.java create mode 100644 src/main/java/kr/co/mcmp/service/oss/repository/nexus/NexusRepositoryAdapterClient.java create mode 100644 src/main/java/kr/co/mcmp/service/oss/repository/nexus/NexusRepositoryAdapterService.java create mode 100644 src/main/java/kr/co/mcmp/service/oss/repository/nexus/NexusRepositoryService.java create mode 100644 src/main/java/kr/co/mcmp/util/AES256Utils.java create mode 100644 src/main/java/kr/co/mcmp/util/Base64Utils.java create mode 100644 src/main/resources/static/favicon.ico diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..48c8bec --- /dev/null +++ b/Dockerfile @@ -0,0 +1,3 @@ +FROM openjdk:17-slim AS prod +COPY ./build/libs/am.jar am.jar +ENTRYPOINT ["java", "-jar","am.jar"] diff --git a/README.md b/README.md index 747e1a1..c3718a6 100755 --- a/README.md +++ b/README.md @@ -12,18 +12,15 @@ v0.2.0(2024.08) - 애플리케이션 카탈로그 등록 및 내/외부(artifactHub, dockerHub등) 환경에서의 키워드검색 - workflow-manager를 연동한 멀티 클라우드 인프라에 애플리케이션 배포 기능(to VM) - workflow-manager를 연동한 배포 외 기타 기능 - - v.0.3.0(2024.10) - workflow-manager를 연동한 멀티 클라우드 인프라에 애플리케이션 배포 기능(to k8s) - k8s에 배포 시 필요한 일부 yaml generate 기능(deployment, service, pod, configmap 등) - repository 관련 제어(nexus 등) -- 기타 ## 목차 -1. [mc-application-manager 실행 및 개발 환경] +1. [mc-application-manager 실행 및 개발 환경] 2. [mc-application-manager실행 방법] 3. [mc-application-manager 소스 빌드 및 실행 방법 상세] 4. [mc-application-manager 기여 방법] diff --git a/build.gradle b/build.gradle index c1dc4f6..423c36f 100755 --- a/build.gradle +++ b/build.gradle @@ -33,6 +33,7 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.session:spring-session-core' implementation 'com.squareup.okhttp3:okhttp:4.10.0' + implementation 'org.springframework.boot:spring-boot-starter-validation' // https://mvnrepository.com/artifact/com.h2database/h2 // 시작 시 drop TABLE관련 버그로 1.4.200 -> 1.4.199 @@ -90,6 +91,15 @@ dependencies { testImplementation(platform("org.junit:junit-bom:5.9.1")) testImplementation("org.junit.jupiter:junit-jupiter") + + implementation group: 'org.apache.commons', name: 'commons-lang3', version: '3.12.0' + implementation 'org.apache.commons:commons-compress' + implementation 'io.kubernetes:client-java' + implementation 'io.kubernetes:client-java-api' + implementation 'io.kubernetes:client-java-api' + implementation 'io.kubernetes:client-java-extended' + + } tasks.test { diff --git a/docker-compose.yaml b/docker-compose.yaml new file mode 100644 index 0000000..2d9308e --- /dev/null +++ b/docker-compose.yaml @@ -0,0 +1,85 @@ +# temp docker-compose +networks: + internal_network: + internal: true + external_network: + driver: bridge + + # jenkins - for workflow manager + jenkins: + image: jenkins/jenkins:jdk17 + container_name: jenkins + platform: linux/amd64 + networks: + - internal_network + - external_network + ports: + - target: 8080 + published: 9800 + protocol: tcp + volumes: + - ~/:/var/jenkins_home # -v $HOME/mcmp/oss/jenkins:/var/jenkins_home + - /var/run/docker.sock:/var/run/docker.sock + - /usr/bin/docker:/usr/bin/docker # -v $(which docker):/usr/bin/docker + environment: + - PROJECT=mcmp + healthcheck: # for application-manager + test: [ "CMD", "curl", "-f", "http://localhost:1024/catalog/software" ] + interval: 1m + timeout: 5s + retries: 3 + start_period: 10s + + # sonatype nexus - for application manager + sonatype-nexus: + image: sonatype/nexus3:latest + container_name: nexus-repository + platform: linux/amd64 + networks: + - internal_network + - external_network + ports: + - target: 8081 + published: 8081 + protocol: tcp + - target: 5000 # container-repository + published: 5000 + protocol: tcp + volumes: + - ~/:/nexus-data/blobs/ + environment: + - PROJECT=mcmp + healthcheck: # for application-manager + test: [ "CMD", "curl", "-f", "http://localhost:1024/catalog/software" ] + interval: 1m + timeout: 5s + retries: 3 + start_period: 10s + + # application-manager + mc-application-manager: + image: pbccc/devops:0.2.5 + container_name: cb-mapui + build: + context: ./ + dockerfile: Dockerfile + networks: + - external_network + - external_network + ports: + - target: 18084 + published: 18084 + protocol: tcp + volumes: + - ~/:/nexus-data/blobs/ + environment: + - DDL_AUTO=create-drop + - DB_USER=application + - DB_PASS=application!23 + - SQL_DATA_INIT=always + healthcheck: # for cb-application-manager + test: ["CMD", "nc", "-vz", "localhost", "1324"] + interval: 1m + timeout: 5s + retries: 3 + start_period: 10s diff --git a/docker-run.sh b/docker-run.sh index e42aed5..37edc14 100644 --- a/docker-run.sh +++ b/docker-run.sh @@ -1,7 +1,5 @@ #!/bin/sh -#test - nowdate=$(date '+%Y%m%d%H%M%S') echo $nowdate @@ -46,3 +44,5 @@ docker run -d -p 18084:18084 \ echo "docker build image delete" docker rmi -f $APP_NAME + + diff --git a/docs/func_tree.xlsx b/docs/func_tree.xlsx new file mode 100644 index 0000000000000000000000000000000000000000..2b0ff14c577b38dd886c2f2055f5f37c896f0589 GIT binary patch literal 10785 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 getComponentList( + @Parameter(description = "모듈 타입", required = true, example = "nexus") @PathVariable("module") String module, + @Parameter(description = "레포지토리 이름", required = true) @PathVariable("name") String name) { + List componentList = moduleComponentService.getComponentList(module, name); + return ResponseEntity.ok(new ResponseWrapper<>(componentList)); + } + + @Operation(summary = "컴포넌트 상세 조회") + @GetMapping("/{module}/detail/{id}") + public ResponseEntity> getComponentDetailByName( + @Parameter(description = "모듈 타입", required = true, example = "nexus") @PathVariable("module") String module, + @Parameter(description = "컴포넌트 식별자", required = true) @PathVariable("id") String id) { + CommonComponent.ComponentDto componentDetailByName = moduleComponentService.getComponentDetailByName(module, id); + return ResponseEntity.ok(new ResponseWrapper<>(componentDetailByName)); + } + + @Operation(summary = "컴포넌트 삭제") + @DeleteMapping("/{module}/delete/{id}") + public ResponseEntity> deleteComponent( + @Parameter(description = "모듈 타입", required = true, example = "nexus") @PathVariable("module") String module, + @Parameter(description = "컴포넌트 식별자", required = true) @PathVariable("id") String id) { + moduleComponentService.deleteComponent(module, id); + return ResponseEntity.ok(new ResponseWrapper<>("Component delete completed")); + } + + @Operation(summary = "컴포넌트 등록") + @PostMapping("/{module}/create/{name}") + public ResponseEntity> createComponent( + @Parameter(description = "모듈 타입", required = true, example = "nexus") @PathVariable("module") String module, + @Parameter(description = "레포지토리 이름", required = true) @PathVariable("name") String name, + @RequestPart(value = "directory", required = false) String directory, + @RequestPart(value = "asset", required = false) List files + ) { + moduleComponentService.createComponent(module, name, directory, files); + return ResponseEntity.ok(new ResponseWrapper<>("Component create completed")); + } +} diff --git a/src/main/java/kr/co/mcmp/api/oss/repository/CommonRepositoryController.java b/src/main/java/kr/co/mcmp/api/oss/repository/CommonRepositoryController.java new file mode 100644 index 0000000..1026aa1 --- /dev/null +++ b/src/main/java/kr/co/mcmp/api/oss/repository/CommonRepositoryController.java @@ -0,0 +1,67 @@ +package kr.co.mcmp.api.oss.repository; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.tags.Tag; +import kr.co.mcmp.dto.oss.repository.CommonRepository; +import kr.co.mcmp.response.ResponseWrapper; +import kr.co.mcmp.service.oss.repository.CommonModuleRepositoryService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import javax.validation.Valid; +import java.util.List; + +@Tag(name = "CommonRepositoryController - 레포지토리 API 관련") +@RequestMapping("/oss/v1/repositories") +@RestController +@RequiredArgsConstructor +public class CommonRepositoryController { + + private final CommonModuleRepositoryService moduleRepositoryService; + + @Operation(summary = "레포지토리 목록 조회") + @GetMapping("/{module}/list") + public ResponseEntity>> getRepositoryList( + @Parameter(description = "모듈 타입", required = true, example = "nexus") @PathVariable("module") String module) { + List repositoryList = moduleRepositoryService.getRepositoryList(module); + return ResponseEntity.ok(new ResponseWrapper<>(repositoryList)); + } + + @Operation(summary = "레포지토리 상세 조회") + @GetMapping("/{module}/detail/{name}") + public ResponseEntity> getRepositoryDetailByName( + @Parameter(description = "모듈 타입", required = true, example = "nexus") @PathVariable("module") String module, + @Parameter(description = "레포지토리 이름", required = true) @PathVariable("name") String name) { + CommonRepository.RepositoryDto repositoryDetailByName = moduleRepositoryService.getRepositoryDetailByName(module, name); + return ResponseEntity.ok(new ResponseWrapper<>(repositoryDetailByName)); + } + + @Operation(summary = "레포지토리 등록") + @PostMapping("/{module}/create") + public ResponseEntity> createRepository( + @Parameter(description = "모듈 타입", required = true, example = "nexus") @PathVariable("module") String module, + @RequestBody @Valid CommonRepository.RepositoryDto repositoryDto) { + moduleRepositoryService.createRepository(module, repositoryDto); + return ResponseEntity.ok(new ResponseWrapper<>("Repository create completed")); + } + + @Operation(summary = "레포지토리 수정") + @PutMapping("/{module}/update") + public ResponseEntity> updateRepository( + @Parameter(description = "모듈 타입", required = true, example = "nexus") @PathVariable("module") String module, + @RequestBody @Valid CommonRepository.RepositoryDto repositoryDto) { + moduleRepositoryService.updateRepository(module, repositoryDto); + return ResponseEntity.ok(new ResponseWrapper<>("Repository update completed")); + } + + @Operation(summary = "레포지토리 삭제") + @DeleteMapping("/{module}/delete/{name}") + public ResponseEntity> deleteRepository( + @Parameter(description = "모듈 타입", required = true, example = "nexus") @PathVariable("module") String module, + @Parameter(description = "레포지토리 이름", required = true) @PathVariable("name") String name) { + moduleRepositoryService.deleteRepository(module, name); + return ResponseEntity.ok(new ResponseWrapper<>("Repository delete completed")); + } +} diff --git a/src/main/java/kr/co/mcmp/catalog/CatalogController.java b/src/main/java/kr/co/mcmp/catalog/CatalogController.java index 23caa35..7daf8ee 100644 --- a/src/main/java/kr/co/mcmp/catalog/CatalogController.java +++ b/src/main/java/kr/co/mcmp/catalog/CatalogController.java @@ -20,26 +20,25 @@ public class CatalogController { @Autowired CatalogService catalogService; - @ApiOperation(value="software catalog list(all)", notes="software catalog 리스트 불러오기") - @Operation(summary = "get software catalog list") - @GetMapping("/") - public List getCatalogList(){ - return catalogService.getCatalogList(); - } - // @ApiOperation(value="software catalog list(all)", notes="software catalog 리스트 불러오기") // @Operation(summary = "get software catalog list") // @GetMapping("/") -// public List getCatalogList(@RequestParam String title){ -// return catalogService.getCatalogList(title); +// public List getCatalogList(){ +// return catalogService.getCatalogList(); // } -// @Operation(summary = "search software catalog") -// @ApiOperation(value="software catalog list(keyword search)", notes="software catalog 검색") -// @GetMapping("/list/{keyword}") -// public List getCatalogList(@PathVariable(required = false) String keyword){ -// return catalogService.getCatalogListSearch(keyword); -// } + @ApiOperation(value="software catalog list(all)", notes="software catalog 리스트 불러오기") + @Operation(summary = "get software catalog list") + @GetMapping("/") + public List getCatalogList(@RequestParam String title){ + if(title != null && title.trim().equals("")){ + System.out.println("==================================non title search"); + return catalogService.getCatalogList(); + }else { + System.out.println("==================================" + title); + return catalogService.getCatalogListSearch(title); + } + } @Operation(summary = "software catalogd detail(and reference)") @ApiOperation(value="software catalog detail", notes="software catalog 내용 확인(연결된 정보들까지)") diff --git a/src/main/java/kr/co/mcmp/catalog/CatalogRefDTO.java b/src/main/java/kr/co/mcmp/catalog/CatalogRefDTO.java index 8aa23d6..a5d5628 100644 --- a/src/main/java/kr/co/mcmp/catalog/CatalogRefDTO.java +++ b/src/main/java/kr/co/mcmp/catalog/CatalogRefDTO.java @@ -9,7 +9,7 @@ public class CatalogRefDTO { private Integer catalogRefIdx; private Integer catalogIdx; - private Integer refernectIdx; + private Integer referncetIdx; private String referenceValue; // ref url, ref value, etc... private String referenceDescription; private String referenceType; // homepage, manifest, workflow, image, etc... @@ -17,7 +17,7 @@ public class CatalogRefDTO { public CatalogRefDTO(CatalogRefEntity crEntity){ this.catalogRefIdx = crEntity.getId(); this.catalogIdx = crEntity.getCatalogId(); - this.refernectIdx = crEntity.getRefIdx(); + this.referncetIdx = crEntity.getRefIdx(); this.referenceValue = crEntity.getRefValue(); this.referenceDescription = crEntity.getRefDesc(); this.referenceType = crEntity.getRefType(); diff --git a/src/main/java/kr/co/mcmp/catalog/CatalogRefEntity.java b/src/main/java/kr/co/mcmp/catalog/CatalogRefEntity.java index 71c3ef7..82bac3e 100644 --- a/src/main/java/kr/co/mcmp/catalog/CatalogRefEntity.java +++ b/src/main/java/kr/co/mcmp/catalog/CatalogRefEntity.java @@ -39,7 +39,7 @@ public class CatalogRefEntity { public CatalogRefEntity(CatalogRefDTO crDto){ this.catalogId = crDto.getCatalogIdx(); - this.refIdx = crDto.getRefernectIdx(); + this.refIdx = crDto.getReferncetIdx(); this.refValue = crDto.getReferenceValue(); this.refDesc = crDto.getReferenceDescription(); this.refType = crDto.getReferenceType(); diff --git a/src/main/java/kr/co/mcmp/catalog/CatalogRepository.java b/src/main/java/kr/co/mcmp/catalog/CatalogRepository.java index 1caa598..ade113f 100644 --- a/src/main/java/kr/co/mcmp/catalog/CatalogRepository.java +++ b/src/main/java/kr/co/mcmp/catalog/CatalogRepository.java @@ -3,11 +3,13 @@ import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; +import java.util.List; + @Repository public interface CatalogRepository extends JpaRepository { void deleteById(Integer catalogIdx); - //List findbyTitle(String title); + List findByTitleLikeIgnoreCase(String title); } diff --git a/src/main/java/kr/co/mcmp/catalog/CatalogService.java b/src/main/java/kr/co/mcmp/catalog/CatalogService.java index 46c3b2d..ee8c3b5 100644 --- a/src/main/java/kr/co/mcmp/catalog/CatalogService.java +++ b/src/main/java/kr/co/mcmp/catalog/CatalogService.java @@ -29,13 +29,13 @@ public List getCatalogList(){ } public List getCatalogListSearch(String keyword){ -// List lcEntity = catalogRepository.findbyTitle(keyword); -// List lcDto = new ArrayList<>(); -// for(CatalogEntity ce:lcEntity){ -// lcDto.add(new CatalogDTO(ce)); -// } -// return lcDto; - return null; + List lcEntity = catalogRepository.findByTitleLikeIgnoreCase("%" + keyword + "%"); + System.out.println("=========================" + lcEntity.size()); + List lcDto = new ArrayList<>(); + for(CatalogEntity ce:lcEntity){ + lcDto.add(new CatalogDTO(ce)); + } + return lcDto; } public CatalogDTO getCatalogDetail(Integer catalogIdx){ diff --git a/src/main/java/kr/co/mcmp/dto/oss/component/CommonComponent.java b/src/main/java/kr/co/mcmp/dto/oss/component/CommonComponent.java new file mode 100644 index 0000000..7cd01e2 --- /dev/null +++ b/src/main/java/kr/co/mcmp/dto/oss/component/CommonComponent.java @@ -0,0 +1,70 @@ +package kr.co.mcmp.dto.oss.component; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.validation.Valid; +import javax.validation.constraints.NotBlank; +import java.util.List; + +@Getter +public class CommonComponent { + + @Getter + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class ComponentDto { + + @Schema(title = "컴포넌트 식별자", required = true, example = "dGTzdKenZQCzMlY8Ne") + @NotBlank + private String id; + + @Schema(title = "컴포넌트가 속한 저장소", required = true, example = "repo") + @NotBlank + private String repository; + + @Schema(title = "컴포넌트 포맷 유형", required = true, example = "raw, helm, docker") + @NotBlank + private String format; + + @Schema(title = "컴포넌트가 속한 그룹", required = true, example = "hosted") + @NotBlank + private String group; + + @Schema(title = "컴포넌트 이름", required = true, example = "comp") + @NotBlank + private String name; + + @Valid + private List assets; + + @Getter + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class AssetsDto { + + @Schema(title = "다운로드 URL", required = true, example = "http://127.0.0.1:8080/repository/repo/comp.zip") + @NotBlank + private String downloadUrl; + + @Schema(title = "자원 식별자", required = true, example = "dGTzdKenZQCzMlY8Ne") + @NotBlank + private String id; + + @Schema(title = "컨텐츠 타입", required = true, example = "application/zip") + @NotBlank + private String contentType; + + @Schema(title = "파일 크기", required = true, example = "1000") + private Integer fileSize; + + @Schema(title = "업로드 날짜", required = true, example = "2024-01-01T00:00:00.188+00:00") + private String blobCreated; + } + } +} diff --git a/src/main/java/kr/co/mcmp/dto/oss/component/CommonUploadComponent.java b/src/main/java/kr/co/mcmp/dto/oss/component/CommonUploadComponent.java new file mode 100644 index 0000000..f1a56fa --- /dev/null +++ b/src/main/java/kr/co/mcmp/dto/oss/component/CommonUploadComponent.java @@ -0,0 +1,40 @@ +package kr.co.mcmp.dto.oss.component; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.springframework.web.multipart.MultipartFile; + +import javax.validation.Valid; +import javax.validation.constraints.NotBlank; +import java.util.List; + +@Getter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class CommonUploadComponent { + + @Schema(title = "디렉토리 이름", required = true, example = "test") + @NotBlank + private String directory; + + @Schema(title = "파일", required = true) + @Valid + private List asset; + + @Getter + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class FilesDto { + + @Schema(title = "파일") + private MultipartFile file; + + @Schema(title = "파일 이름", example = "test") + private String filename; + } +} diff --git a/src/main/java/kr/co/mcmp/dto/oss/repository/CommonFormatType.java b/src/main/java/kr/co/mcmp/dto/oss/repository/CommonFormatType.java new file mode 100644 index 0000000..bdda840 --- /dev/null +++ b/src/main/java/kr/co/mcmp/dto/oss/repository/CommonFormatType.java @@ -0,0 +1,20 @@ +package kr.co.mcmp.dto.oss.repository; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class CommonFormatType { + + @Schema(title = "포맷 유형", example = "raw, docker, helm") + private String format; + + @Schema(title = "타입 유형", example = "hosted") + private String type; +} diff --git a/src/main/java/kr/co/mcmp/dto/oss/repository/CommonRepository.java b/src/main/java/kr/co/mcmp/dto/oss/repository/CommonRepository.java new file mode 100644 index 0000000..7c026e3 --- /dev/null +++ b/src/main/java/kr/co/mcmp/dto/oss/repository/CommonRepository.java @@ -0,0 +1,91 @@ +package kr.co.mcmp.dto.oss.repository; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +import javax.validation.Valid; +import javax.validation.constraints.NotBlank; +import javax.validation.constraints.NotNull; + +@Getter +public class CommonRepository { + + @Getter + @Builder + @AllArgsConstructor + @NoArgsConstructor + public static class RepositoryDto { + + @Schema(title = "레포지토리 이름", required = true, example = "repo") + @NotBlank + private String name; + + @Schema(title = "레포지토리 포맷 유형", required = true, example = "raw, helm, docker") + @NotBlank + private String format; + + @Schema(title = "레포지토리 타입 유형", required = true, example = "hosted") + @NotBlank + private String type; + + @Schema(title = "레포지토리 접근 url", required = true, example = "등록: 빈값, 수정: 값") + @NotNull + private String url; + + @Schema(title = "레포지토리 사용자 접근 가능 여부", required = true) + @NotNull + private Boolean online; + + @Valid + private StorageDto storage; + + @Valid + private DockerDto docker; + + @Getter + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class StorageDto { + + @Schema(title = "아티팩트를 저장하는 물리적 저장소 이름", required = true, example = "default") + @NotBlank + private String blobStoreName; + + @Schema(title = "저장되는 아티팩트 유형 일치 여부 검증", required = true) + @NotNull + private Boolean strictContentTypeValidation; + + @Schema(title = "레포지토리 읽기/쓰기 설정", required = true, example = "allow, allow_once, deny") + @NotBlank + private String writePolicy; + } + + @Getter + @Builder + @NoArgsConstructor + @AllArgsConstructor + public static class DockerDto { + + @Schema(title = "도커 registry 버전 지원(false: v2 지원)", required = true) + @NotNull + private Boolean v1Enabled; + + @Schema(title = "도커 클라이언트가 레포지토리에 접근할 때 기본 인증 사용 여부", required = true) + @NotNull + private Boolean forceBasicAuth; + + @Schema(title = "도커 레포지토리에 접근할 때 사용할 http 포트", example = "8080") + private Integer httpPort; + + @Schema(title = "도커 레포지토리에 접근할 때 사용할 https 포트", example = "9090") + private Integer httpsPort; + + @Schema(title = "도커 레포지토리에 접근할 때 사용할 서브도메인", example = "/test") + private String subdomain; + } + } +} diff --git a/src/main/java/kr/co/mcmp/externalrepo/ArtifactHubInteface.java b/src/main/java/kr/co/mcmp/externalrepo/ArtifactHubInteface.java index 7642ad4..6b83610 100644 --- a/src/main/java/kr/co/mcmp/externalrepo/ArtifactHubInteface.java +++ b/src/main/java/kr/co/mcmp/externalrepo/ArtifactHubInteface.java @@ -1,16 +1,22 @@ package kr.co.mcmp.externalrepo; +import kr.co.mcmp.externalrepo.model.ArtifactHubPackage; +import kr.co.mcmp.externalrepo.model.ArtifactHubRespository; import org.springframework.cloud.openfeign.FeignClient; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; + +import java.util.List; @FeignClient(name = "artifactHubClient", url = "https://artifacthub.io") 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 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); + @GetMapping(value="/api/v1/repositories/search") + //https://artifacthub.io/api/v1/repositories/search?offset=0&limit=5&kind=0&name=argo + List 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); } diff --git a/src/main/java/kr/co/mcmp/externalrepo/ExternalRepoController.java b/src/main/java/kr/co/mcmp/externalrepo/ExternalRepoController.java index d64fdb8..79e27a3 100644 --- a/src/main/java/kr/co/mcmp/externalrepo/ExternalRepoController.java +++ b/src/main/java/kr/co/mcmp/externalrepo/ExternalRepoController.java @@ -27,24 +27,22 @@ public class ExternalRepoController { @Operation(summary = "dockerHub catalog 조회(image 조회)") @GetMapping("/dockerhub/{keyword}") public ResponseWrapper getDockerHubList(@PathVariable String keyword){ -// logger.info("testString: {}", keyword); -// if(keyword != null) { -// return new ResponseWrapper<>(outSvc.searchDockerHubCatalog(keyword)); -// }else{ -// return null; -// } - return null; + logger.info("testString: {}", keyword); + if(keyword != null) { + return new ResponseWrapper<>(outSvc.searchDockerHubCatalog(keyword)); + }else{ + return null; + } } @Operation(summary = "artifactHub package 목록 조회(helm 조회)") @GetMapping("/artifacthub/{keyword}") public ResponseWrapper getArtifactHubList(@PathVariable String keyword){ -// if(keyword != null) { -// return new ResponseWrapper<>(outSvc.searchArtifactHubPackage(keyword)); -// }else{ -// return null; -// } - return null; + if(keyword != null) { + return new ResponseWrapper<>(outSvc.searchArtifactHubPackage(keyword)); + }else{ + return null; + } } diff --git a/src/main/java/kr/co/mcmp/externalrepo/ExternalRepoService.java b/src/main/java/kr/co/mcmp/externalrepo/ExternalRepoService.java index 431bec9..71ea3b8 100644 --- a/src/main/java/kr/co/mcmp/externalrepo/ExternalRepoService.java +++ b/src/main/java/kr/co/mcmp/externalrepo/ExternalRepoService.java @@ -3,6 +3,7 @@ import kr.co.mcmp.externalrepo.model.ArtifactHubPackage; import kr.co.mcmp.externalrepo.model.ArtifactHubRespository; import kr.co.mcmp.externalrepo.model.DockerHubCatalog; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import java.util.List; @@ -10,30 +11,28 @@ @Service public class ExternalRepoService { -// @Autowired -// private ArtifactHubInteface artfInt; + @Autowired + private ArtifactHubInteface artfInt; -// @Autowired -// private DockerHubInterface dockerInt; + @Autowired + private DockerHubInterface dockerInt; public List searchArtifactHubRepository(String keyword){ - //return artfInt.searchRepository(keyword); - return null; + return artfInt.searchRepository(keyword); + } public ArtifactHubPackage searchArtifactHubPackage(String keyword){ - //return artfInt.searchPackage(keyword, "0"); - return null; + return artfInt.searchPackage(keyword, "0"); } public DockerHubNamespace searchDockerHubNamespace(String keyword){ - //return dockerInt.searchNamespace(keyword); - return null; + return dockerInt.searchNamespace(keyword); } public DockerHubCatalog searchDockerHubCatalog(String keyword){ - //return dockerInt.searchCatalog(keyword); - return null; + return dockerInt.searchCatalog(keyword); + //return null; } diff --git a/src/main/java/kr/co/mcmp/manifest/K8S.java b/src/main/java/kr/co/mcmp/manifest/K8S.java new file mode 100644 index 0000000..3e3ceec --- /dev/null +++ b/src/main/java/kr/co/mcmp/manifest/K8S.java @@ -0,0 +1,134 @@ +package kr.co.mcmp.manifest; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import io.kubernetes.client.util.Yaml; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import java.util.LinkedHashMap; +import java.util.Map; + +public class K8S { + + private static Logger logger = LoggerFactory.getLogger(K8S.class); + + public static final String podAntiAffinityKey = "devops.type"; + + public static enum Controller { + Pod("apps/v1", "dep"), + Deployment("apps/v1", "dep"), + DaemonSet("apps/v1", "dep"), + StatefulSet("apps/v1", "dep"), + CronJob("batch/v1beta1", "dep"), + Rollout("argoproj.io/v1alpha1", "dep"); + + String apiVersion; + + String postfix; + + private Controller(String apiVersion, String postfix) { + this.apiVersion = apiVersion; + this.postfix = postfix; + } + + public String getApiVersion() { + return this.apiVersion; + } + + public String getPostfix() { + return this.postfix; + } + + + } + + public static enum StrategyType { + RollingUpdate, + Recreate + } + +// public static enum VolumeType { +// HostPath, PVC +// } + + public static enum Kind { + Service("v1", "svc"), + Ingress("networking.k8s.io/v1beta1", "ing"), + ConfigMap("v1", "cfm"), + Secret("v1", "sec"), + TlsSecret("v1", "httptls"), + HorizontalPodAutoscaler("autoscaling/v2beta2", "hpa"), + HTTPProxy("projectcontour.io/v1", "htp"), + PersistentVolumeClaim("v1", "pvc") + ; + + + + String apiVersion; + + String postfix; + + private Kind(String apiVersion, String postfix) { + this.apiVersion = apiVersion; + this.postfix = postfix; + } + + public String getApiVersion() { + return this.apiVersion; + } + + public String getPostfix() { + return this.postfix; + } + + } + + + + + + + public static enum VolumeType2 { + DirectoryOrCreate, + File + } + + public static Map getIngressRewriteAnnotaions() { + + Map map = new LinkedHashMap<> (); + map.put("nginx.ingress.kubernetes.io/rewrite-target", "/$2"); + + return map; + + } + + + public static void printJson(String title, Object object) { + + if (object == null) { + logger.info("[{}] null", title); + return; + } + + try { + ObjectMapper mapper = new ObjectMapper(); + JsonNode jsonNode = mapper.convertValue(object, JsonNode.class); + logger.info("[{}] {}", title, mapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonNode)); + } catch (Exception e) { + logger.info("[{}] {}", title, e.getMessage()); + } + } + + + public static void printYaml(String title, Object object) { + + if (object == null) { + logger.info("[{}] null", title); + return; + } + + logger.info("[{}] {}", title, Yaml.dump(object)); + } + +} diff --git a/src/main/java/kr/co/mcmp/manifest/K8SDeployService.java b/src/main/java/kr/co/mcmp/manifest/K8SDeployService.java new file mode 100644 index 0000000..89ba960 --- /dev/null +++ b/src/main/java/kr/co/mcmp/manifest/K8SDeployService.java @@ -0,0 +1,27 @@ +package kr.co.mcmp.manifest; + +import kr.co.mcmp.manifest.k8s.*; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import java.io.IOException; + +@Service +public class K8SDeployService{ + + private static Logger logger = LoggerFactory.getLogger(K8SDeployService.class); + + @Autowired + private K8SDeployYamlGenerator deployYamlGenerator; + + + //yaml 미리보기 + public String getK8SDeployYaml(K8SDeployDTO deploy) throws IOException { + String deployYaml = deployYamlGenerator.generateDeployYaml(deploy); + return deployYaml; + } + + + +} diff --git a/src/main/java/kr/co/mcmp/manifest/K8SDeployYamlGenerator.java b/src/main/java/kr/co/mcmp/manifest/K8SDeployYamlGenerator.java index b2c580c..b455c30 100644 --- a/src/main/java/kr/co/mcmp/manifest/K8SDeployYamlGenerator.java +++ b/src/main/java/kr/co/mcmp/manifest/K8SDeployYamlGenerator.java @@ -1,30 +1,24 @@ package kr.co.mcmp.manifest; -//import io.kubernetes.client.common.KubernetesObject; -//import io.kubernetes.client.custom.IntOrString; -//import io.kubernetes.client.custom.Quantity; -//import io.kubernetes.client.openapi.models.*; -//import io.kubernetes.client.util.Yaml; -//import kr.co.mcmp.devops.dto.k8s.*; -//import kr.co.mcmp.devops.model.httpproxy.*; -//import kr.co.mcmp.devops.model.rollout.BlueGreen; -//import kr.co.mcmp.devops.model.rollout.Canary; -//import kr.co.mcmp.devops.model.rollout.Pause; -//import kr.co.mcmp.devops.model.rollout.Rollout; -//import kr.co.mcmp.devops.model.rollout.RolloutSpec; -//import kr.co.mcmp.devops.model.rollout.RolloutStrategy; -// -//import org.apache.commons.lang.StringUtils; -// -//import java.util.ArrayList; -//import java.util.HashMap; -//import java.util.List; -//import java.util.Map; - -//@Component +import io.kubernetes.client.common.KubernetesObject; +import io.kubernetes.client.custom.IntOrString; +import io.kubernetes.client.custom.Quantity; +import io.kubernetes.client.openapi.models.*; +import io.kubernetes.client.util.Yaml; +import kr.co.mcmp.manifest.k8s.*; + +import org.apache.commons.lang3.StringUtils; // lang -> lang3 +//import org.springframework.util.StringUtils; + +import org.springframework.stereotype.Component; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Component public class K8SDeployYamlGenerator { -/* - private static Logger logger = LoggerFactory.getLogger(K8SDeployYamlGenerator.class); public String generateDeployYaml(K8SDeployDTO deploy) { @@ -57,13 +51,8 @@ public String generateDeployYaml(K8SDeployDTO deploy) { case CronJob: appendYaml(buffer, getCronJob(deploy)); break; - case Rollout: - Rollout rollout = getRollout(deploy); - appendYaml(buffer, rollout); - if (BlueGreen.name.equals(deploy.getStrategyType())) { - appendYaml(buffer, getPreviewService(deploy)); - } - appendYaml(buffer, getAutoscaler(deploy, rollout)); + case Pod: + appendYaml(buffer, getPod(deploy)); break; } @@ -75,15 +64,12 @@ public String generateDeployYaml(K8SDeployDTO deploy) { // httprpoxy appendYaml(buffer, getTlsSecret(deploy)); - appendYaml(buffer, getHTTPProxy(deploy)); // ingress kbcard ingress 사용하지 않음 // appendYaml(buffer, getIngress(deploy)); String yaml = buffer.toString(); - logger.info("[generateYaml]{}", yaml); - - + //default Label 삭제 removeDefaultLabel(deploy.getLabels()); @@ -145,8 +131,7 @@ private V1Deployment getDeployment(K8SDeployDTO deploy) { deployment.setKind(deploy.getController()); // metadata - V1ObjectMeta metadata = getControllerMetadata(deploy.getController(), deploy.getName(), deploy.getNamespace(), - deploy.getLabels()); + V1ObjectMeta metadata = getControllerMetadata(deploy.getController(), deploy.getName(), deploy.getNamespace(), deploy.getLabels()); deployment.setMetadata(metadata); // spec @@ -172,114 +157,30 @@ private V1Deployment getDeployment(K8SDeployDTO deploy) { return deployment; } - // Rollout - private Rollout getRollout(K8SDeployDTO deploy) { - Rollout rollout = new Rollout(); - rollout.setApiVersion(K8S.Controller.Rollout.getApiVersion()); - rollout.setKind(deploy.getController()); + private V1Pod getPod(K8SDeployDTO deploy) { + + V1Pod pod = new V1Pod(); + pod.setApiVersion(K8S.Controller.Pod.getApiVersion()); + pod.setKind(deploy.getController()); // metadata - V1ObjectMeta metadata = getControllerMetadata(deploy.getController(), deploy.getName(), deploy.getNamespace(), - deploy.getLabels()); - rollout.setMetadata(metadata); + V1ObjectMeta metadata = getControllerMetadata(deploy.getController(), deploy.getName(), deploy.getNamespace(), deploy.getLabels()); + pod.setMetadata(metadata); // spec - RolloutSpec rolloutSpec = new RolloutSpec(); - rollout.setSpec(rolloutSpec); - - // spec.selector - V1LabelSelector labelSelector = new V1LabelSelector(); - labelSelector.setMatchLabels(deploy.getLabels()); - rolloutSpec.setSelector(labelSelector); + V1PodSpec podSpec = new V1PodSpec(); + V1Container container = new V1Container(); + container.setName(deploy.getName().replace("KIND", "pod")); + container.setImage(deploy.getImage()); + //pod.setSpec(podSpec); + podSpec.addContainersItem(container); - // spec.replicas - Integer replicas = (deploy.getReplicas() == null || deploy.getReplicas() == 0) ? 1 : deploy.getReplicas(); - rolloutSpec.setReplicas(replicas); + return pod; + } - // spec.strategy - RolloutStrategy strategy = new RolloutStrategy(); - - switch(deploy.getStrategyType()) { - case BlueGreen.name: - BlueGreen blueGreen = new BlueGreen(); - String activeServiceName = getServiceName(deploy); - String previewServiceName = String.format("%s-preview", activeServiceName); - blueGreen.setActiveService(activeServiceName); - blueGreen.setPreviewService(previewServiceName); - blueGreen.setAutoPromotionEnabled(true); - // blueGreen.setAutoPromotionSeconds(replicas); - strategy.setBlueGreen(blueGreen); - break; - case Canary.name: - Canary canary = getCanary(deploy, false); - strategy.setCanary(canary); - break; - } - rolloutSpec.setStrategy(strategy); - // template(pod) - rolloutSpec.setTemplate(getPodTemplateSpec(deploy)); - return rollout; - } - - - private Canary getCanary(K8SDeployDTO deploy, boolean promote) { - - Canary canary = new Canary(); - Map stepMap = null; - - if (promote) { - - //canary.setMaxSurge("100%"); - canary.setMaxUnavailable(0); - - //50% - stepMap = new HashMap<> (1); - stepMap.put("setWeight", 50); - canary.addStep(stepMap); - - //5sec - stepMap = new HashMap<> (1); - stepMap.put("pause", new Pause(5)); - canary.addStep(stepMap); - - //75% - stepMap = new HashMap<> (1); - stepMap.put("setWeight", 75); - canary.addStep(stepMap); - - //5sec - stepMap = new HashMap<> (1); - stepMap.put("pause", new Pause(5)); - canary.addStep(stepMap); - - //100 - stepMap = new HashMap<> (1); - stepMap.put("setWeight", 100); - canary.addStep(stepMap); - - } else { - - //canary.setMaxSurge("25%"); - canary.setMaxUnavailable(0); - - //순서중요!! Object Model 만들어 Yaml dump시 알파벳 오더링 규칙에 따라 순서가 뒤바뀜 - //int setWeight = Math.floorDiv(100, deploy.getReplicas()); - stepMap = new HashMap<> (1); - stepMap.put("setWeight", 25); - canary.addStep(stepMap); - - //suspend - stepMap = new HashMap<> (1); - stepMap.put("pause", new Pause()); - canary.addStep(stepMap); - - } - - return canary; - } // DaemonSet private V1DaemonSet getDaemonSet(K8SDeployDTO deploy) { @@ -289,8 +190,7 @@ private V1DaemonSet getDaemonSet(K8SDeployDTO deploy) { daemonSet.setKind(deploy.getController()); // metadata - V1ObjectMeta metadata = getControllerMetadata(deploy.getController(), deploy.getName(), deploy.getNamespace(), - deploy.getLabels()); + V1ObjectMeta metadata = getControllerMetadata(deploy.getController(), deploy.getName(), deploy.getNamespace(), deploy.getLabels()); daemonSet.setMetadata(metadata); // spec @@ -321,8 +221,7 @@ private V1StatefulSet getStatefulSet(K8SDeployDTO deploy) { statefulSet.setKind(deploy.getController()); // metadata - V1ObjectMeta metadata = getControllerMetadata(deploy.getController(), deploy.getName(), deploy.getNamespace(), - deploy.getLabels()); + V1ObjectMeta metadata = getControllerMetadata(deploy.getController(), deploy.getName(), deploy.getNamespace(), deploy.getLabels()); statefulSet.setMetadata(metadata); // spec @@ -361,8 +260,7 @@ private V1beta1CronJob getCronJob(K8SDeployDTO deploy) { cronJob.setKind(deploy.getController()); // metadata - V1ObjectMeta metadata = getControllerMetadata(deploy.getController(), deploy.getName(), deploy.getNamespace(), - deploy.getLabels()); + V1ObjectMeta metadata = getControllerMetadata(deploy.getController(), deploy.getName(), deploy.getNamespace(), deploy.getLabels()); cronJob.setMetadata(metadata); // spec @@ -581,7 +479,7 @@ private V1PodTemplateSpec getPodTemplateSpec(K8SDeployDTO deploy) { // template.spec.volume setHostPathVolumes(podSpec, deploy.getHostPathVolumes()); setPVCVolumes(podSpec, deploy.getPvcVolumes()); - setAzureFileVolumes(podSpec, deploy.getAzureFileVolumes()); + //setAzureFileVolumes(podSpec, deploy.getAzureFileVolumes()); // template.spec.affinity switch (K8S.Controller.valueOf(deploy.getController())) { @@ -655,26 +553,6 @@ private void setPVCVolumes(V1PodSpec podSpec, List volumesList) { } } - // AzureFileVolumes - private void setAzureFileVolumes(V1PodSpec podSpec, List volumesList) { - - if (volumesList == null || volumesList.size() == 0) { - return; - } - - for (K8SVolume k8sVolume : volumesList) { - V1Volume volume = new V1Volume(); - volume.setName(k8sVolume.getName()); - - V1AzureFileVolumeSource azureFileVolumeSource = new V1AzureFileVolumeSource(); - azureFileVolumeSource.setSecretName(k8sVolume.getSecretName()); - azureFileVolumeSource.setShareName(k8sVolume.getShareName()); - volume.setAzureFile(azureFileVolumeSource); - - podSpec.addVolumesItem(volume); - } - } - private void setResource(V1Container container, K8SResource resource) { if (resource == null) @@ -752,8 +630,6 @@ private V1Affinity getPodAntiAffinity(K8SDeployDTO deploy) { podAntiAffinity.addRequiredDuringSchedulingIgnoredDuringExecutionItem(term); return affinity; - - } // ConfigMap @@ -827,10 +703,6 @@ private V1Secret getTlsSecret(K8SDeployDTO deploy) { // byte[] crtBuf = Base64.encodeBase64(proxyInfo.getTlsCrt().trim().getBytes()); // byte[] keyBuf = Base64.encodeBase64(proxyInfo.getTlsKey().trim().getBytes()); -// logger.info("SECRET-CRT\n{}<---\n----------------------------------------------", proxyInfo.getTlsCrt()); -// logger.info("SECRET-CRT\n{}<---\n----------------------------------------------", new String(crtBuf)); -// logger.info("SECRET-KEY\n{}<---\n----------------------------------------------", proxyInfo.getTlsKey()); -// logger.info("SECRET-KEY\n{}<---\n----------------------------------------------", new String(keyBuf)); deploy.getProxyInfo().setTlsSecretName(secret.getMetadata().getName()); @@ -964,70 +836,7 @@ private V1Service getPreviewService(K8SDeployDTO deploy) { private final String KBC_HttpProxy_LoadBalancerPolicy = "Cookie"; - // HttpProxy - private HTTPProxy getHTTPProxy(K8SDeployDTO deploy) { - K8SProxyInfo proxyInfo = deploy.getProxyInfo(); - if (proxyInfo == null) - return null; - if (StringUtils.isBlank(proxyInfo.getDomainName())) - return null; - - HTTPProxy httpProxy = new HTTPProxy(); - httpProxy.setApiVersion(K8S.Kind.HTTPProxy.getApiVersion()); - httpProxy.setKind(K8S.Kind.HTTPProxy.name()); - - String name = getName(K8S.Kind.HTTPProxy.getPostfix(), deploy.getName()); - //String p = (proxyInfo.getTlsYn().equals("Y")) ? "443" : "80"; - //name = String.format("%s-%s", name, p); - - httpProxy.setMetadata(getMetadata(name, deploy.getNamespace())); - - // spec - HTTPProxySpec spec = new HTTPProxySpec(); - httpProxy.setSpec(spec); - - // virtualhost - Virtualhost virtualhost = new Virtualhost(); - spec.setVirtualhost(virtualhost); - // doamin - virtualhost.setFqdn(proxyInfo.getDomainName()); - // tls - if (proxyInfo.getTlsYn().equals("Y")) { - Tls tls = new Tls(); - tls.setSecretName(proxyInfo.getTlsSecretName()); - virtualhost.setTls(tls); - } - - List routeList = new ArrayList(); - spec.setRoutes(routeList); - - Route route = new Route(); - routeList.add(route); - - // LoadBalancerPolicy Cookie - LoadBalancerPolicy loadBalancerPolicy = new LoadBalancerPolicy(); - loadBalancerPolicy.setStrategy(KBC_HttpProxy_LoadBalancerPolicy); - route.setLoadBalancerPolicy(loadBalancerPolicy); - - List list = new ArrayList(); - route.setServices(list); - - // Service - List portList = deploy.getPorts(); - for (K8SPort port : portList) { - RouteService service = new RouteService(); - service.setName(getServiceName(deploy)); - service.setPort(port.getPort()); -// if (StringUtils.isNotBlank(port.getProtocol())) { -// service.setProtocol(port.getProtocol()); -// } - - list.add(service); - } - - return httpProxy; - } private V2beta2HorizontalPodAutoscaler getAutoscaler(K8SDeployDTO deploy, KubernetesObject object) { @@ -1139,52 +948,6 @@ private V1PersistentVolumeClaim getPvc(K8SDeployDTO deploy, K8SVolume volume) { return pvc; } - // Ingress - /** - * private ExtensionsV1beta1Ingress getIngress(K8SDeployDTO deploy) { - * - * if (deploy.getPorts() == null || deploy.getPorts().isEmpty()) { return null; - * } - * - * List paths = new ArrayList<> (); for - * (K8SPort port : deploy.getPorts()) { - * - * if (port.getIngressPath() == null || port.getIngressPath().isEmpty()) { - * continue; } - * - * //spec.rule.paths.path ExtensionsV1beta1HTTPIngressPath path = new - * ExtensionsV1beta1HTTPIngressPath(); String ingressPath = - * (deploy.getIngressPathRewriteYn().equals("Y")) ? String.format("%s(/|$)(.*)", - * port.getIngressPath()) : port.getIngressPath(); path.setPath(ingressPath); - * //spec.rule.paths.backend ExtensionsV1beta1IngressBackend backend = new - * ExtensionsV1beta1IngressBackend(); - * backend.setServiceName(getName(K8S.Kind.Service.getPostfix(), - * deploy.getName())); backend.setServicePort(new IntOrString(port.getPort())); - * path.setBackend(backend); - * - * paths.add(path); } - * - * if (paths.size() == 0) { return null; } - * - * ExtensionsV1beta1Ingress ingress = new ExtensionsV1beta1Ingress(); - * ingress.apiVersion(K8S.Kind.Ingress.getApiVersion()); - * ingress.setKind(K8S.Kind.Ingress.name()); - * - * //metadata V1ObjectMeta metadata = getMetadata(K8S.Kind.Ingress.getPostfix(), - * deploy.getName()); metadata.setNamespace(deploy.getNamespace()); - * //metadata.annotation if (deploy.getIngressPathRewriteYn().equals("Y")) { - * metadata.setAnnotations(K8S.getIngressRewriteAnnotaions()); } - * ingress.setMetadata(metadata); - * - * //spec ExtensionsV1beta1IngressSpec ingressSpec = new - * ExtensionsV1beta1IngressSpec(); ingress.setSpec(ingressSpec); - * - * //spec.rules List rules = new ArrayList<>(1); - * ExtensionsV1beta1IngressRule rule = new ExtensionsV1beta1IngressRule(); - * rule.setHttp(new ExtensionsV1beta1HTTPIngressRuleValue()); - * rule.getHttp().setPaths(paths); rules.add(rule); ingressSpec.setRules(rules); - * - * return ingress; } - **/ + } diff --git a/src/main/java/kr/co/mcmp/manifest/YamlGenerateController.java b/src/main/java/kr/co/mcmp/manifest/YamlGenerateController.java index d430d7e..01cf456 100644 --- a/src/main/java/kr/co/mcmp/manifest/YamlGenerateController.java +++ b/src/main/java/kr/co/mcmp/manifest/YamlGenerateController.java @@ -2,6 +2,7 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; +import kr.co.mcmp.manifest.k8s.K8SDeployDTO; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; @@ -21,25 +22,25 @@ public class YamlGenerateController { @Operation(summary = "yaml generate for pod") @PostMapping("/pod") - public String generatePodYaml(){ + public String generatePodYaml(K8SDeployDTO k8sDto){ return null; } @Operation(summary = "yaml generate for deployment") @PostMapping("/deployment") - public String generateDeploymentYaml(){ + public String generateDeploymentYaml(K8SDeployDTO k8sDto){ return null; } @Operation(summary = "yaml generate for service") @PostMapping("/service") - public String generateServiceYaml(){ + public String generateServiceYaml(K8SDeployDTO k8sDto){ return null; } @Operation(summary = "yaml generate for configmap") @PostMapping("/configmap") - public String generateConfigmapYaml(){ + public String generateConfigmapYaml(K8SDeployDTO k8sDto){ return null; } diff --git a/src/main/java/kr/co/mcmp/manifest/k8s/K8SDeployDTO.java b/src/main/java/kr/co/mcmp/manifest/k8s/K8SDeployDTO.java index af26aa2..911bafc 100644 --- a/src/main/java/kr/co/mcmp/manifest/k8s/K8SDeployDTO.java +++ b/src/main/java/kr/co/mcmp/manifest/k8s/K8SDeployDTO.java @@ -5,13 +5,21 @@ import java.util.List; import java.util.Map; -public class K8SDeployDTO extends K8SDeployBaseDTO implements Serializable { +public class K8SDeployDTO extends DeployDTO implements Serializable { private static final long serialVersionUID = 5433248174008755823L; @NotNull private String namespace = "default"; + private String image; + public String getImage() { + return image; + } + public void setImage(String image) { + this.image = image; + } + @NotNull private String controller; diff --git a/src/main/java/kr/co/mcmp/oss/dto/OssDto.java b/src/main/java/kr/co/mcmp/oss/dto/OssDto.java index ed13e3a..2db9270 100644 --- a/src/main/java/kr/co/mcmp/oss/dto/OssDto.java +++ b/src/main/java/kr/co/mcmp/oss/dto/OssDto.java @@ -1,12 +1,16 @@ package kr.co.mcmp.oss.dto; import kr.co.mcmp.oss.entity.Oss; +import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; +import lombok.NoArgsConstructor; @Getter @Builder +@NoArgsConstructor +@AllArgsConstructor public class OssDto { private Long ossIdx; diff --git a/src/main/java/kr/co/mcmp/oss/dto/OssTypeDto.java b/src/main/java/kr/co/mcmp/oss/dto/OssTypeDto.java index a026995..c7fa9eb 100644 --- a/src/main/java/kr/co/mcmp/oss/dto/OssTypeDto.java +++ b/src/main/java/kr/co/mcmp/oss/dto/OssTypeDto.java @@ -1,12 +1,16 @@ package kr.co.mcmp.oss.dto; import kr.co.mcmp.oss.entity.OssType; +import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; +import lombok.NoArgsConstructor; @Getter @Builder +@NoArgsConstructor +@AllArgsConstructor public class OssTypeDto { private Long ossTypeIdx; diff --git a/src/main/java/kr/co/mcmp/oss/repository/OssRepository.java b/src/main/java/kr/co/mcmp/oss/repository/OssRepository.java index d3b07f2..f3486a0 100644 --- a/src/main/java/kr/co/mcmp/oss/repository/OssRepository.java +++ b/src/main/java/kr/co/mcmp/oss/repository/OssRepository.java @@ -11,7 +11,7 @@ @Repository public interface OssRepository extends JpaRepository { List findAll(); - List findByOssName(String ossName); + Oss findByOssName(String ossName); @Query("SELECT o FROM Oss o WHERE o.ossType.ossTypeIdx IN :ossTypeIdxs") List findByOssTypeIdxIn(@Param("ossTypeIdxs") List ossTypeIdxs); Boolean existsByOssNameAndOssUrlAndOssUsername(String ossName, String ossUrl, String ossUsername); diff --git a/src/main/java/kr/co/mcmp/oss/service/OssServiceImpl.java b/src/main/java/kr/co/mcmp/oss/service/OssServiceImpl.java index e443789..62bb707 100644 --- a/src/main/java/kr/co/mcmp/oss/service/OssServiceImpl.java +++ b/src/main/java/kr/co/mcmp/oss/service/OssServiceImpl.java @@ -6,8 +6,9 @@ import kr.co.mcmp.oss.nexus.service.NexusService; import kr.co.mcmp.oss.repository.OssRepository; import kr.co.mcmp.oss.repository.OssTypeRepository; -import kr.co.mcmp.util.AES256Util; -import kr.co.mcmp.util.Base64Util; +import kr.co.mcmp.util.AES256Utils; + +import kr.co.mcmp.util.Base64Utils; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; import org.apache.commons.lang3.StringUtils; @@ -175,6 +176,21 @@ public OssDto detailOss(Long ossIdx) { return OssDto.withDetailDecryptPassword(oss, encodingBase64String(decryptAesString(oss.getOssPassword()))); } + public OssDto detailOssByOssName(String ossName) { + Oss oss = ossRepository.findByOssName(ossName); + String pwd = oss.getOssPassword(); + String decodePwd = Base64Utils.base64Decoding(pwd); + return OssDto.builder() + .ossIdx(oss.getOssIdx()) + .ossTypeIdx(oss.getOssType().getOssTypeIdx()) + .ossName(oss.getOssName()) + .ossDesc(oss.getOssDesc()) + .ossUrl(oss.getOssUrl()) + .ossUsername(oss.getOssUsername()) + .ossPassword(decodePwd) + .build(); + } + // /** // * OSS 정보 상세 조회 // * @param ossCd @@ -204,7 +220,7 @@ public Boolean isOssInfoDuplicated(OssDto ossDto) { */ public String encryptBase64String(String str) { if ( StringUtils.isNotBlank(str) ) { - return Base64Util.base64Encoding(AES256Util.decryptOssPassword(str)); + return Base64Utils.base64Encoding(AES256Utils.encrypt(str)); } else { return null; @@ -218,7 +234,7 @@ public String encryptBase64String(String str) { */ public String encryptAesString(String str) { if ( StringUtils.isNotBlank(str) ) { - return AES256Util.encryptOssPassword(Base64Util.base64Decoding(str)); + return AES256Utils.encrypt(Base64Utils.base64Decoding(str)); } else { return null; @@ -231,7 +247,7 @@ public String encryptAesString(String str) { */ public String encodingBase64String(String str) { if ( StringUtils.isNotBlank(str) ) { - return Base64Util.base64Encoding(str); + return Base64Utils.base64Encoding(str); } else { return null; @@ -246,7 +262,7 @@ public String encodingBase64String(String str) { public String decryptAesString(String encryptedStr) { if (StringUtils.isNotBlank(encryptedStr)) { // AES256으로 암호화된 문자열을 복호화 - String decrypted = AES256Util.decryptOssPassword(encryptedStr); + String decrypted = AES256Utils.decrypt(encryptedStr); // 복호화된 문자열을 Base64로 인코딩 return decrypted; } else { diff --git a/src/main/java/kr/co/mcmp/service/oss/component/CommonComponentFactory.java b/src/main/java/kr/co/mcmp/service/oss/component/CommonComponentFactory.java new file mode 100644 index 0000000..37229fc --- /dev/null +++ b/src/main/java/kr/co/mcmp/service/oss/component/CommonComponentFactory.java @@ -0,0 +1,21 @@ +package kr.co.mcmp.service.oss.component; + +import kr.co.mcmp.service.oss.component.nexus.NexusComponentService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class CommonComponentFactory { + + private final NexusComponentService nexusComponentService; + + public CommonComponentService generatedComponentService(String module) { + switch (module) { + case "nexus": + return nexusComponentService; + default: + throw new IllegalArgumentException("Unsupported module: " + module); + } + } +} diff --git a/src/main/java/kr/co/mcmp/service/oss/component/CommonComponentService.java b/src/main/java/kr/co/mcmp/service/oss/component/CommonComponentService.java new file mode 100644 index 0000000..f0358f9 --- /dev/null +++ b/src/main/java/kr/co/mcmp/service/oss/component/CommonComponentService.java @@ -0,0 +1,17 @@ +package kr.co.mcmp.service.oss.component; + +import kr.co.mcmp.dto.oss.component.CommonComponent; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; + +public interface CommonComponentService { + + List getComponentList(String name); + + CommonComponent.ComponentDto getComponentDetailByName(String id); + + void deleteComponent(String id); + + void createComponent(String name, String directory, List files); +} diff --git a/src/main/java/kr/co/mcmp/service/oss/component/CommonModuleComponentService.java b/src/main/java/kr/co/mcmp/service/oss/component/CommonModuleComponentService.java new file mode 100644 index 0000000..b255060 --- /dev/null +++ b/src/main/java/kr/co/mcmp/service/oss/component/CommonModuleComponentService.java @@ -0,0 +1,39 @@ +package kr.co.mcmp.service.oss.component; + +import kr.co.mcmp.dto.oss.component.CommonComponent; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class CommonModuleComponentService { + + private final CommonComponentFactory componentFactory; + + public List getComponentList(String module, String name) { + CommonComponentService componentService = getComponentService(module); + return componentService.getComponentList(name); + } + + public CommonComponent.ComponentDto getComponentDetailByName(String module, String id) { + CommonComponentService componentService = getComponentService(module); + return componentService.getComponentDetailByName(id); + } + + public void deleteComponent(String module, String id) { + CommonComponentService componentService = getComponentService(module); + componentService.deleteComponent(id); + } + + public void createComponent(String module, String name, String directory, List files) { + CommonComponentService componentService = getComponentService(module); + componentService.createComponent(name, directory, files); + } + + private CommonComponentService getComponentService(String module) { + return componentFactory.generatedComponentService(module); + } +} diff --git a/src/main/java/kr/co/mcmp/service/oss/component/nexus/NexusComponentAdapterClient.java b/src/main/java/kr/co/mcmp/service/oss/component/nexus/NexusComponentAdapterClient.java new file mode 100644 index 0000000..22e23a3 --- /dev/null +++ b/src/main/java/kr/co/mcmp/service/oss/component/nexus/NexusComponentAdapterClient.java @@ -0,0 +1,152 @@ +package kr.co.mcmp.service.oss.component.nexus; + +import com.fasterxml.jackson.core.type.TypeReference; +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import kr.co.mcmp.config.oss.RestTemplateProvider; +import kr.co.mcmp.dto.oss.component.CommonComponent; +import kr.co.mcmp.exception.NexusClientException; +import kr.co.mcmp.oss.dto.OssDto; +import kr.co.mcmp.oss.service.OssServiceImpl; +import kr.co.mcmp.util.Base64Util; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.*; +import org.springframework.stereotype.Service; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +import java.util.Collections; +import java.util.List; +import java.util.Map; + +@Service +public class NexusComponentAdapterClient { + + private static final String GET_COMP_LIST = "/v1/components"; + private static final String GET_COMP_DETAIL = "/v1/components/{id}"; + private static final String DELETE_COMP_DELETE = "/v1/components/{id}"; + private static final String POST_COMP_CREATE = "/v1/components"; + + private String nexusId = ""; + private String nexusPwd = ""; + private String baseUrl = ""; + private String authorization = "Authorization"; + + @Autowired private ObjectMapper mapper; + @Autowired private OssServiceImpl ossService; + + private void getOssInfo() { + try { + OssDto nexus = ossService.detailOssByOssName("NEXUS"); + this.nexusId = nexus.getOssUsername(); + this.nexusPwd = nexus.getOssPassword(); + this.baseUrl = nexus.getOssUrl(); + } catch (Exception e) { + throw new IllegalArgumentException ("DB Nexus 계정 정보가 없습니다."); + } + } + + public List getComponentList(String name) { + getOssInfo(); + String url = UriComponentsBuilder.fromHttpUrl(baseUrl) + .path(GET_COMP_LIST) + .queryParam("repository", name) + .toUriString(); + + HttpEntity request = getRequest(null); + Map response = exchange(url, HttpMethod.GET, request, new ParameterizedTypeReference>() {}); + return mapper.convertValue(response.get("items"), new TypeReference>() {}); + } + + public CommonComponent.ComponentDto getComponentDetailByName(String id) { + getOssInfo(); + String url = UriComponentsBuilder.fromHttpUrl(baseUrl) + .path(GET_COMP_DETAIL) + .buildAndExpand(id) + .toUriString(); + + HttpEntity request = getRequest(null); + Map response = exchange(url, HttpMethod.GET, request, new ParameterizedTypeReference>() {}); + return mapper.convertValue(response, new TypeReference() {}); + } + + public Object deleteComponent(String id) { + getOssInfo(); + String url = UriComponentsBuilder.fromHttpUrl(baseUrl) + .path(DELETE_COMP_DELETE) + .buildAndExpand(id) + .toUriString(); + + HttpEntity request = getRequest(null); + return exchange(url, HttpMethod.DELETE, request, new ParameterizedTypeReference() {}); + } + + public Object createComponent(String name, MultiValueMap uploadMap) { + getOssInfo(); + String url = UriComponentsBuilder.fromHttpUrl(baseUrl) + .path(POST_COMP_CREATE) + .queryParam("repository", name) + .toUriString(); + + HttpEntity> request = getUploadRequest(uploadMap); + return exchange(url, HttpMethod.POST, request, new ParameterizedTypeReference() {}); + } + + private HttpEntity getRequest(T body) { + String basicToken = createToken(); + HttpHeaders headers = getHeaders(basicToken); + return new HttpEntity<>(body, headers); + } + + private HttpHeaders getHeaders(String basicToken) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); + headers.set(authorization, basicToken); + return headers; + } + + private HttpEntity getUploadRequest(T body) { + String basicToken = createToken(); + HttpHeaders headers = getUploadHeaders(basicToken); + return new HttpEntity<>(body, headers); + } + + private HttpHeaders getUploadHeaders(String basicToken) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.MULTIPART_FORM_DATA); + headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); + headers.set(authorization, basicToken); + return headers; + } + + private String createToken() { + String auth = nexusId + ":" + nexusPwd; + return "Basic " + Base64Util.base64Encoding(auth); + } + + private T exchange(String url, HttpMethod method, HttpEntity requestEntity, ParameterizedTypeReference responseType) { + RestTemplate template = RestTemplateProvider.get(); + try { + ResponseEntity response = template.exchange(url, method, requestEntity, responseType); + return response.getBody(); + } catch (HttpClientErrorException e) { + String errorMessage = e.getResponseBodyAsString(); + throw new NexusClientException(parseErrorMessage(errorMessage)); + } + } + + private String parseErrorMessage(String errorMessage) { + ObjectMapper mapper = new ObjectMapper(); + try { + JsonNode rootNode = mapper.readTree(errorMessage); + JsonNode messageNode = rootNode.path("message"); + return messageNode.asText().replace("\\\"", "\"").replace("\"", ""); + } catch (Exception e) { + return "Message Parsing Error"; + } + } +} diff --git a/src/main/java/kr/co/mcmp/service/oss/component/nexus/NexusComponentAdapterService.java b/src/main/java/kr/co/mcmp/service/oss/component/nexus/NexusComponentAdapterService.java new file mode 100644 index 0000000..948a9ca --- /dev/null +++ b/src/main/java/kr/co/mcmp/service/oss/component/nexus/NexusComponentAdapterService.java @@ -0,0 +1,59 @@ +package kr.co.mcmp.service.oss.component.nexus; + +import kr.co.mcmp.dto.oss.component.CommonComponent; +import kr.co.mcmp.dto.oss.component.CommonUploadComponent; +import kr.co.mcmp.dto.oss.repository.CommonRepository; +import kr.co.mcmp.service.oss.repository.nexus.NexusRepositoryAdapterService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class NexusComponentAdapterService { + + private final NexusComponentAdapterClient componentAdapterClient; + private final NexusRepositoryAdapterService nexusRepositoryAdapterService; + + public List getComponentList(String name) { + return componentAdapterClient.getComponentList(name); + } + + public CommonComponent.ComponentDto getComponentDetailByName(String id) { + return componentAdapterClient.getComponentDetailByName(id); + } + + public void deleteComponent(String id) { + componentAdapterClient.deleteComponent(id); + } + + public void createComponent(String name, CommonUploadComponent uploadComponent) { + CommonRepository.RepositoryDto repositoryByName = nexusRepositoryAdapterService.getRepositoryByName(name); + String format = repositoryByName.getFormat(); + + MultiValueMap uploadComponentMap = getUploadComponentMap(uploadComponent, format); + + componentAdapterClient.createComponent(name, uploadComponentMap); + } + + private static MultiValueMap getUploadComponentMap(CommonUploadComponent uploadComponent, String format) { + MultiValueMap uploadComponentMap = new LinkedMultiValueMap<>(); + + if ("raw".equals(format)) { + uploadComponentMap.add(format + ".directory", uploadComponent.getDirectory()); + + for (int i = 0; i < uploadComponent.getAsset().size(); i ++) { + CommonUploadComponent.FilesDto filesDto = uploadComponent.getAsset().get(i); + uploadComponentMap.add(format + ".asset" + (i + 1), filesDto.getFile().getResource()); + uploadComponentMap.add(format + ".asset" + (i + 1) + ".filename", filesDto.getFilename()); + } + } else if ("docker".equals(format) || "helm".equals(format)) { + CommonUploadComponent.FilesDto filesDto = uploadComponent.getAsset().get(0); + uploadComponentMap.add(format + ".asset", filesDto.getFile().getResource()); + } + return uploadComponentMap; + } +} diff --git a/src/main/java/kr/co/mcmp/service/oss/component/nexus/NexusComponentService.java b/src/main/java/kr/co/mcmp/service/oss/component/nexus/NexusComponentService.java new file mode 100644 index 0000000..6125bd7 --- /dev/null +++ b/src/main/java/kr/co/mcmp/service/oss/component/nexus/NexusComponentService.java @@ -0,0 +1,56 @@ +package kr.co.mcmp.service.oss.component.nexus; + +import kr.co.mcmp.dto.oss.component.CommonComponent; +import kr.co.mcmp.dto.oss.component.CommonUploadComponent; +import kr.co.mcmp.service.oss.component.CommonComponentService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; +import org.springframework.web.multipart.MultipartFile; + +import java.util.ArrayList; +import java.util.List; + +@Service +@RequiredArgsConstructor +public class NexusComponentService implements CommonComponentService { + + private final NexusComponentAdapterService componentAdapterService; + + @Override + public List getComponentList(String name) { + return componentAdapterService.getComponentList(name); + } + + @Override + public CommonComponent.ComponentDto getComponentDetailByName(String id) { + return componentAdapterService.getComponentDetailByName(id); + } + + @Override + public void deleteComponent(String id) { + componentAdapterService.deleteComponent(id); + } + + @Override + public void createComponent(String name, String directory, List files) { + + List uploadFileList = new ArrayList<>(); + + for (MultipartFile file : files) { + CommonUploadComponent.FilesDto filesDto = CommonUploadComponent.FilesDto.builder() + .file(file) + .filename(file.getOriginalFilename()) + .build(); + + uploadFileList.add(filesDto); + } + + CommonUploadComponent uploadComponent = CommonUploadComponent.builder() + .directory(directory) + .asset(uploadFileList) + .build(); + + componentAdapterService.createComponent(name, uploadComponent); + } +} + diff --git a/src/main/java/kr/co/mcmp/service/oss/repository/CommonModuleRepositoryService.java b/src/main/java/kr/co/mcmp/service/oss/repository/CommonModuleRepositoryService.java new file mode 100644 index 0000000..020df3e --- /dev/null +++ b/src/main/java/kr/co/mcmp/service/oss/repository/CommonModuleRepositoryService.java @@ -0,0 +1,43 @@ +package kr.co.mcmp.service.oss.repository; + +import kr.co.mcmp.dto.oss.repository.CommonRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class CommonModuleRepositoryService { + + private final CommonRepositoryFactory repositoryFactory; + + public List getRepositoryList(String module) { + CommonRepositoryService repositoryService = getRepositoryService(module); + return repositoryService.getRepositoryList(); + } + + public CommonRepository.RepositoryDto getRepositoryDetailByName(String module, String name) { + CommonRepositoryService repositoryService = getRepositoryService(module); + return repositoryService.getRepositoryDetailByName(name); + } + + public void createRepository(String module, CommonRepository.RepositoryDto repositoryDto) { + CommonRepositoryService repositoryService = getRepositoryService(module); + repositoryService.createRepository(repositoryDto); + } + + public void updateRepository(String module, CommonRepository.RepositoryDto repositoryDto) { + CommonRepositoryService repositoryService = getRepositoryService(module); + repositoryService.updateRepository(repositoryDto); + } + + public void deleteRepository(String module, String name) { + CommonRepositoryService repositoryService = getRepositoryService(module); + repositoryService.deleteRepository(name); + } + + private CommonRepositoryService getRepositoryService(String module) { + return repositoryFactory.generatedRepositoryService(module); + } +} diff --git a/src/main/java/kr/co/mcmp/service/oss/repository/CommonRepositoryFactory.java b/src/main/java/kr/co/mcmp/service/oss/repository/CommonRepositoryFactory.java new file mode 100644 index 0000000..8e2ac99 --- /dev/null +++ b/src/main/java/kr/co/mcmp/service/oss/repository/CommonRepositoryFactory.java @@ -0,0 +1,21 @@ +package kr.co.mcmp.service.oss.repository; + +import kr.co.mcmp.service.oss.repository.nexus.NexusRepositoryService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class CommonRepositoryFactory { + + private final NexusRepositoryService nexusRepositoryService; + + public CommonRepositoryService generatedRepositoryService(String module) { + switch (module) { + case "nexus": + return nexusRepositoryService; + default: + throw new IllegalArgumentException("Unsupported module: " + module); + } + } +} diff --git a/src/main/java/kr/co/mcmp/service/oss/repository/CommonRepositoryService.java b/src/main/java/kr/co/mcmp/service/oss/repository/CommonRepositoryService.java new file mode 100644 index 0000000..5e0ab91 --- /dev/null +++ b/src/main/java/kr/co/mcmp/service/oss/repository/CommonRepositoryService.java @@ -0,0 +1,20 @@ +package kr.co.mcmp.service.oss.repository; + +import kr.co.mcmp.dto.oss.repository.CommonRepository; + +import java.util.List; + +public interface CommonRepositoryService { + + List getRepositoryList(); + + CommonRepository.RepositoryDto getRepositoryByName(String name); + + CommonRepository.RepositoryDto getRepositoryDetailByName(String name); + + void createRepository(CommonRepository.RepositoryDto repositoryDto); + + void updateRepository(CommonRepository.RepositoryDto repositoryDto); + + void deleteRepository(String name); +} diff --git a/src/main/java/kr/co/mcmp/service/oss/repository/nexus/NexusRepositoryAdapterClient.java b/src/main/java/kr/co/mcmp/service/oss/repository/nexus/NexusRepositoryAdapterClient.java new file mode 100644 index 0000000..4012838 --- /dev/null +++ b/src/main/java/kr/co/mcmp/service/oss/repository/nexus/NexusRepositoryAdapterClient.java @@ -0,0 +1,156 @@ +package kr.co.mcmp.service.oss.repository.nexus; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.ObjectMapper; +import kr.co.mcmp.config.oss.RestTemplateProvider; +import kr.co.mcmp.dto.oss.repository.CommonFormatType; +import kr.co.mcmp.dto.oss.repository.CommonRepository; +import kr.co.mcmp.exception.NexusClientException; +import kr.co.mcmp.oss.dto.OssDto; +import kr.co.mcmp.oss.service.OssServiceImpl; +import kr.co.mcmp.util.Base64Util; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.ParameterizedTypeReference; +import org.springframework.http.*; +import org.springframework.stereotype.Service; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.util.UriComponentsBuilder; + +import java.util.Collections; +import java.util.List; + +@Service +public class NexusRepositoryAdapterClient { + + private static final String GET_REPO_LIST = "/v1/repositories"; + private static final String GET_REPO_BY_NAME = "/v1/repositories/{repositoryName}"; + private static final String GET_REPO_DETAIL = "/v1/repositories/{format}/{type}/{repositoryName}"; + private static final String POST_REPO_CREATE = "/v1/repositories/{format}/{type}"; + private static final String PUT_REPO_UPDATE = "/v1/repositories/{format}/{type}/{repositoryName}"; + private static final String DELETE_REPO_DELETE = "/v1/repositories/{repositoryName}"; + + private String nexusId = ""; + private String nexusPwd = ""; + private String baseUrl = ""; + private String authorization = "Authorization"; + + @Autowired private OssServiceImpl ossService; + + private void getOssInfo() { + try { + OssDto nexus = ossService.detailOssByOssName("NEXUS"); + this.nexusId = nexus.getOssUsername(); + this.nexusPwd = nexus.getOssPassword(); + this.baseUrl = nexus.getOssUrl(); + } catch (Exception e) { + throw new IllegalArgumentException ("DB Nexus 계정 정보가 없습니다."); + } + } + + public List getRepositoryList() { + getOssInfo(); + String url = UriComponentsBuilder.fromHttpUrl(baseUrl) + .path(GET_REPO_LIST) + .toUriString(); + + HttpEntity request = getRequest(null); + return exchange(url, HttpMethod.GET, request, new ParameterizedTypeReference>() {}); + } + + public CommonRepository.RepositoryDto getRepositoryByName(String name) { + getOssInfo(); + String url = UriComponentsBuilder.fromHttpUrl(baseUrl) + .path(GET_REPO_BY_NAME) + .buildAndExpand(name) + .toUriString(); + + HttpEntity request = getRequest(null); + return exchange(url, HttpMethod.GET, request, new ParameterizedTypeReference() {}); + } + + public CommonRepository.RepositoryDto getRepositoryDetailByName(CommonFormatType formatType, String name) { + getOssInfo(); + String url = UriComponentsBuilder.fromHttpUrl(baseUrl) + .path(GET_REPO_DETAIL) + .buildAndExpand(formatType.getFormat(), formatType.getType(), name) + .toUriString(); + + HttpEntity request = getRequest(null); + return exchange(url, HttpMethod.GET, request, new ParameterizedTypeReference() {}); + } + + public Object createRepository(CommonRepository.RepositoryDto repositoryDto) { + getOssInfo(); + String url = UriComponentsBuilder.fromHttpUrl(baseUrl) + .path(POST_REPO_CREATE) + .buildAndExpand(repositoryDto.getFormat(), repositoryDto.getType()) + .toUriString(); + + HttpEntity request = getRequest(repositoryDto); + return exchange(url, HttpMethod.POST, request, new ParameterizedTypeReference() {}); + } + + public Object updateRepository(CommonRepository.RepositoryDto repositoryDto) { + getOssInfo(); + String url = UriComponentsBuilder.fromHttpUrl(baseUrl) + .path(PUT_REPO_UPDATE) + .buildAndExpand(repositoryDto.getFormat(), repositoryDto.getType(), repositoryDto.getName()) + .toUriString(); + + HttpEntity request = getRequest(repositoryDto); + return exchange(url, HttpMethod.PUT, request, new ParameterizedTypeReference() {}); + } + + public Object deleteRepository(String name) { + getOssInfo(); + String url = UriComponentsBuilder.fromHttpUrl(baseUrl) + .path(DELETE_REPO_DELETE) + .buildAndExpand(name) + .toUriString(); + + HttpEntity request = getRequest(null); + return exchange(url, HttpMethod.DELETE, request, new ParameterizedTypeReference() {}); + } + + private HttpEntity getRequest(T body) { + String basicToken = createToken(); + HttpHeaders headers = getHeaders(basicToken); + return new HttpEntity<>(body, headers); + } + + private HttpHeaders getHeaders(String basicToken) { + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); + headers.set(authorization, basicToken); + return headers; + } + + private String createToken() { + String auth = nexusId + ":" + nexusPwd; + return "Basic " + Base64Util.base64Encoding(auth); + } + + private T exchange(String url, HttpMethod method, HttpEntity requestEntity, ParameterizedTypeReference responseType) { + RestTemplate template = RestTemplateProvider.get(); + try { + ResponseEntity response = template.exchange(url, method, requestEntity, responseType); + return response.getBody(); + } catch (HttpClientErrorException e) { + String errorMessage = e.getResponseBodyAsString(); + throw new NexusClientException(parseErrorMessage(errorMessage)); + } + } + + private String parseErrorMessage(String errorMessage) { + ObjectMapper mapper = new ObjectMapper(); + try { + JsonNode rootNode = mapper.readTree(errorMessage); + JsonNode messageNode = rootNode.path("message"); + return messageNode.asText().replace("\\\"", "\"").replace("\"", ""); + } catch (Exception e) { + return "Message Parsing Error"; + } + } +} diff --git a/src/main/java/kr/co/mcmp/service/oss/repository/nexus/NexusRepositoryAdapterService.java b/src/main/java/kr/co/mcmp/service/oss/repository/nexus/NexusRepositoryAdapterService.java new file mode 100644 index 0000000..cc9cfbf --- /dev/null +++ b/src/main/java/kr/co/mcmp/service/oss/repository/nexus/NexusRepositoryAdapterService.java @@ -0,0 +1,39 @@ +package kr.co.mcmp.service.oss.repository.nexus; + +import kr.co.mcmp.dto.oss.repository.CommonFormatType; +import kr.co.mcmp.dto.oss.repository.CommonRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class NexusRepositoryAdapterService { + + private final NexusRepositoryAdapterClient repositoryAdapterClient; + + public List getRepositoryList() { + return repositoryAdapterClient.getRepositoryList(); + } + + public CommonRepository.RepositoryDto getRepositoryByName(String name) { + return repositoryAdapterClient.getRepositoryByName(name); + } + + public CommonRepository.RepositoryDto getRepositoryDetailByName(CommonFormatType formatType, String name) { + return repositoryAdapterClient.getRepositoryDetailByName(formatType, name); + } + + public void createRepository(CommonRepository.RepositoryDto repositoryDto) { + repositoryAdapterClient.createRepository(repositoryDto); + } + + public void updateRepository(CommonRepository.RepositoryDto repositoryDto) { + repositoryAdapterClient.updateRepository(repositoryDto); + } + + public void deleteRepository(String name) { + repositoryAdapterClient.deleteRepository(name); + } +} diff --git a/src/main/java/kr/co/mcmp/service/oss/repository/nexus/NexusRepositoryService.java b/src/main/java/kr/co/mcmp/service/oss/repository/nexus/NexusRepositoryService.java new file mode 100644 index 0000000..9cd3020 --- /dev/null +++ b/src/main/java/kr/co/mcmp/service/oss/repository/nexus/NexusRepositoryService.java @@ -0,0 +1,53 @@ +package kr.co.mcmp.service.oss.repository.nexus; + +import kr.co.mcmp.dto.oss.repository.CommonFormatType; +import kr.co.mcmp.dto.oss.repository.CommonRepository; +import kr.co.mcmp.service.oss.repository.CommonRepositoryService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; + +@Service +@RequiredArgsConstructor +public class NexusRepositoryService implements CommonRepositoryService { + + private final NexusRepositoryAdapterService repositoryAdapterService; + + @Override + public List getRepositoryList() { + return repositoryAdapterService.getRepositoryList(); + } + + @Override + public CommonRepository.RepositoryDto getRepositoryByName(String name) { + return repositoryAdapterService.getRepositoryByName(name); + } + + @Override + public CommonRepository.RepositoryDto getRepositoryDetailByName(String name) { + CommonRepository.RepositoryDto repositoryByName = getRepositoryByName(name); + + CommonFormatType formatType = CommonFormatType.builder() + .format(repositoryByName.getFormat()) + .type(repositoryByName.getType()) + .build(); + + return repositoryAdapterService.getRepositoryDetailByName(formatType, name); + } + + @Override + public void createRepository(CommonRepository.RepositoryDto repositoryDto) { + repositoryAdapterService.createRepository(repositoryDto); + } + + @Override + public void updateRepository(CommonRepository.RepositoryDto repositoryDto) { + repositoryAdapterService.updateRepository(repositoryDto); + } + + @Override + public void deleteRepository(String name) { + repositoryAdapterService.deleteRepository(name); + } +} diff --git a/src/main/java/kr/co/mcmp/util/AES256Utils.java b/src/main/java/kr/co/mcmp/util/AES256Utils.java new file mode 100644 index 0000000..5097f4d --- /dev/null +++ b/src/main/java/kr/co/mcmp/util/AES256Utils.java @@ -0,0 +1,77 @@ +package kr.co.mcmp.util; + +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.codec.binary.Base64; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +import javax.crypto.Cipher; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; +import java.io.UnsupportedEncodingException; +import java.security.GeneralSecurityException; + +@Component +@Slf4j +public class AES256Utils { + + private static String authAesKey; + + @Value("${aes.key}") + public void setAuthAesKey(String aesKey) { + authAesKey = aesKey; + } + + public static String encrypt(String str) { + String enStr = null; + + try { + String iv = authAesKey.substring(0, 16); + byte[] keyBytes = new byte[16]; + byte[] b = authAesKey.getBytes("UTF-8"); + int len = b.length; + if (len > keyBytes.length) { + len = keyBytes.length; + } + System.arraycopy(b, 0, keyBytes, 0, len); + SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES"); + + Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding"); + c.init(Cipher.ENCRYPT_MODE, keySpec, new IvParameterSpec(iv.getBytes())); + byte[] encrypted = c.doFinal(str.getBytes("UTF-8")); + enStr = new String(Base64.encodeBase64(encrypted)); + + } catch (GeneralSecurityException | UnsupportedEncodingException e ) { + log.error("[AES256Utils] encrypt error >>>>>>>>>>>>>>>>>> ", e); + return null; + } + + return enStr; + } + + public static String decrypt(String str){ + String deStr = null; + + try { + String iv = authAesKey.substring(0, 16); + byte[] keyBytes = new byte[16]; + byte[] b = authAesKey.getBytes("UTF-8"); + int len = b.length; + if (len > keyBytes.length) { + len = keyBytes.length; + } + System.arraycopy(b, 0, keyBytes, 0, len); + SecretKeySpec keySpec = new SecretKeySpec(keyBytes, "AES"); + + Cipher c = Cipher.getInstance("AES/CBC/PKCS5Padding"); + c.init(Cipher.DECRYPT_MODE, keySpec, new IvParameterSpec(iv.getBytes())); + byte[] byteStr = Base64.decodeBase64(str.getBytes()); + deStr = new String(c.doFinal(byteStr), "UTF-8"); + }catch (GeneralSecurityException | UnsupportedEncodingException e ) { + log.error("[AES256Utils] decrypt error >>>>>>>>>>>>>>>>>> ", e); + return null; + } + + return deStr; + } +} diff --git a/src/main/java/kr/co/mcmp/util/Base64Utils.java b/src/main/java/kr/co/mcmp/util/Base64Utils.java new file mode 100644 index 0000000..8293a14 --- /dev/null +++ b/src/main/java/kr/co/mcmp/util/Base64Utils.java @@ -0,0 +1,50 @@ +package kr.co.mcmp.util; + +import java.io.UnsupportedEncodingException; +import java.util.Base64; +import java.util.Base64.Decoder; +import java.util.Base64.Encoder; + +public class Base64Utils { + /** + * Base64 Decoding. + * @param encodedString + * @return + */ + public static String base64Decoding(String encodedString) { + return base64Decoding(encodedString, "UTF-8"); + } + + public static String base64Decoding(String encodedString, String charset) { + Decoder decoder = Base64.getDecoder(); + byte[] decodedBytes1 = decoder.decode(encodedString.getBytes()); + String decodedString = null; + try { + decodedString = new String(decodedBytes1, charset); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + return decodedString; + } + + /** + * Base64 encoding. + * @param encodedString + * @return + */ + public static String base64Encoding(String text) { + return base64Encoding(text, "UTF-8"); + } + + public static String base64Encoding(String text, String charset) { + String encodedString = null; + Encoder encoder = Base64.getEncoder(); + try { + byte[] targetByte = text.getBytes(charset); + encodedString = encoder.encodeToString(targetByte); + } catch (UnsupportedEncodingException e) { + e.printStackTrace(); + } + return encodedString; + } +} diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml index e11bf82..b0ce6d4 100755 --- a/src/main/resources/application.yaml +++ b/src/main/resources/application.yaml @@ -59,3 +59,6 @@ spring: max-request-size: 500MB object: object-mapper-pretty-print: true + +aes: + key: fb1755281b0ca6184a0ee644e6477ee7 diff --git a/src/main/resources/import.sql b/src/main/resources/import.sql index 1bd5b3e..391a099 100644 --- a/src/main/resources/import.sql +++ b/src/main/resources/import.sql @@ -24,24 +24,67 @@ VALUES INSERT INTO SOFTWARE_CATALOG_REF(CATALOG_ID, REF_IDX, REF_VALUE, REF_DESC, REF_TYPE) VALUES - ((SELECT ID FROM SOFTWARE_CATALOG WHERE TITLE = 'APACHE TOMCAT'), 0, 'https://tomcat.apache.org/', '', 'HOMEPAGE'); + ((SELECT ID FROM SOFTWARE_CATALOG WHERE TITLE = 'APACHE TOMCAT'), 0, 'https://tomcat.apache.org/', '', 'HOMEPAGE'), + ((SELECT ID FROM SOFTWARE_CATALOG WHERE TITLE = 'APACHE TOMCAT'), 0, 'apache', '', 'TAG'), + ((SELECT ID FROM SOFTWARE_CATALOG WHERE TITLE = 'APACHE TOMCAT'), 0, 'oss', '', 'TAG'), + ((SELECT ID FROM SOFTWARE_CATALOG WHERE TITLE = 'APACHE TOMCAT'), 0, 'server', '', 'TAG'); INSERT INTO SOFTWARE_CATALOG_REF(CATALOG_ID, REF_IDX, REF_VALUE, REF_DESC, REF_TYPE) VALUES - ((SELECT ID FROM SOFTWARE_CATALOG WHERE TITLE = 'REDIS'), 0, 'https://redis.io/', '', 'HOMEPAGE'); + ((SELECT ID FROM SOFTWARE_CATALOG WHERE TITLE = 'REDIS'), 0, 'https://redis.io/', '', 'HOMEPAGE'), + ((SELECT ID FROM SOFTWARE_CATALOG WHERE TITLE = 'REDIS'), 0, 'NoSQL', '', 'TAG'), + ((SELECT ID FROM SOFTWARE_CATALOG WHERE TITLE = 'REDIS'), 0, 'oss', '', 'TAG'), + ((SELECT ID FROM SOFTWARE_CATALOG WHERE TITLE = 'REDIS'), 0, 'inMemoryDB', '', 'TAG'); INSERT INTO SOFTWARE_CATALOG_REF(CATALOG_ID, REF_IDX, REF_VALUE, REF_DESC, REF_TYPE) VALUES - ((SELECT ID FROM SOFTWARE_CATALOG WHERE TITLE = 'NGINX'), 0, 'https://nginx.org/en/', '', 'HOMEPAGE'); + ((SELECT ID FROM SOFTWARE_CATALOG WHERE TITLE = 'NGINX'), 0, 'https://nginx.org/en/', '', 'HOMEPAGE'), + ((SELECT ID FROM SOFTWARE_CATALOG WHERE TITLE = 'NGINX'), 0, 'apache', '', 'TAG'), + ((SELECT ID FROM SOFTWARE_CATALOG WHERE TITLE = 'NGINX'), 0, 'oss', '', 'TAG'), + ((SELECT ID FROM SOFTWARE_CATALOG WHERE TITLE = 'NGINX'), 0, 'proxy', '', 'TAG'), + ((SELECT ID FROM SOFTWARE_CATALOG WHERE TITLE = 'NGINX'), 0, 'web', '', 'TAG'), + ((SELECT ID FROM SOFTWARE_CATALOG WHERE TITLE = 'NGINX'), 0, 'frontend', '', 'TAG'), + ((SELECT ID FROM SOFTWARE_CATALOG WHERE TITLE = 'NGINX'), 0, 'server', '', 'TAG'); INSERT INTO SOFTWARE_CATALOG_REF(CATALOG_ID, REF_IDX, REF_VALUE, REF_DESC, REF_TYPE) VALUES - ((SELECT ID FROM SOFTWARE_CATALOG WHERE TITLE = 'APACHE'), 0, 'https://httpd.apache.org/', '', 'HOMEPAGE'); + ((SELECT ID FROM SOFTWARE_CATALOG WHERE TITLE = 'APACHE'), 0, 'https://httpd.apache.org/', '', 'HOMEPAGE'), + ((SELECT ID FROM SOFTWARE_CATALOG WHERE TITLE = 'APACHE'), 0, 'web', '', 'TAG'), + ((SELECT ID FROM SOFTWARE_CATALOG WHERE TITLE = 'APACHE'), 0, 'oss', '', 'TAG'), + ((SELECT ID FROM SOFTWARE_CATALOG WHERE TITLE = 'APACHE'), 0, 'frontend', '', 'TAG'), + ((SELECT ID FROM SOFTWARE_CATALOG WHERE TITLE = 'APACHE'), 0, 'webserver', '', 'TAG'), + ((SELECT ID FROM SOFTWARE_CATALOG WHERE TITLE = 'APACHE'), 0, 'httpd', '', 'TAG'); INSERT INTO SOFTWARE_CATALOG_REF(CATALOG_ID, REF_IDX, REF_VALUE, REF_DESC, REF_TYPE) VALUES - ((SELECT ID FROM SOFTWARE_CATALOG WHERE TITLE = 'NEXUS'), 0, 'https://www.sonatype.com/products/sonatype-nexus-repository', '', 'HOMEPAGE'); + ((SELECT ID FROM SOFTWARE_CATALOG WHERE TITLE = 'NEXUS'), 0, 'https://www.sonatype.com/products/sonatype-nexus-repository', '', 'HOMEPAGE'), + ((SELECT ID FROM SOFTWARE_CATALOG WHERE TITLE = 'NEXUS'), 0, 'repository', '', 'TAG'), + ((SELECT ID FROM SOFTWARE_CATALOG WHERE TITLE = 'NEXUS'), 0, 'oss', '', 'TAG'), + ((SELECT ID FROM SOFTWARE_CATALOG WHERE TITLE = 'NEXUS'), 0, 'license', '', 'TAG'); INSERT INTO SOFTWARE_CATALOG_REF(CATALOG_ID, REF_IDX, REF_VALUE, REF_DESC, REF_TYPE) VALUES - ((SELECT ID FROM SOFTWARE_CATALOG WHERE TITLE = 'MARIA DB'), 0, 'https://mariadb.org/', '', 'HOMEPAGE'); + ((SELECT ID FROM SOFTWARE_CATALOG WHERE TITLE = 'MARIA DB'), 0, 'https://mariadb.org/', '', 'HOMEPAGE'), + ((SELECT ID FROM SOFTWARE_CATALOG WHERE TITLE = 'MARIA DB'), 0, 'RDBMS', '', 'TAG'), + ((SELECT ID FROM SOFTWARE_CATALOG WHERE TITLE = 'MARIA DB'), 0, 'oss', '', 'TAG'), + ((SELECT ID FROM SOFTWARE_CATALOG WHERE TITLE = 'MARIA DB'), 0, 'database', '', 'TAG'); INSERT INTO SOFTWARE_CATALOG_REF(CATALOG_ID, REF_IDX, REF_VALUE, REF_DESC, REF_TYPE) VALUES - ((SELECT ID FROM SOFTWARE_CATALOG WHERE TITLE = 'GRAFANA'), 0, 'https://grafana.com/', '', 'HOMEPAGE'); + ((SELECT ID FROM SOFTWARE_CATALOG WHERE TITLE = 'GRAFANA'), 0, 'https://grafana.com/', '', 'HOMEPAGE'), + ((SELECT ID FROM SOFTWARE_CATALOG WHERE TITLE = 'GRAFANA'), 0, 'view', '', 'TAG'), + ((SELECT ID FROM SOFTWARE_CATALOG WHERE TITLE = 'GRAFANA'), 0, 'observer', '', 'TAG'), + ((SELECT ID FROM SOFTWARE_CATALOG WHERE TITLE = 'GRAFANA'), 0, 'oss', '', 'TAG'); + +-- Insert into oss_type +INSERT INTO oss_type (oss_type_idx, oss_type_name, oss_type_desc) +VALUES + (1, 'NEXUS', 'init'); +INSERT INTO oss_type (oss_type_idx, oss_type_name, oss_type_desc) +VALUES + (2, 'WORKFLOWMANAGER', 'init'); + +-- Insert into oss +INSERT INTO oss (oss_idx, oss_type_idx, oss_name, oss_desc, oss_url, oss_username, oss_password) +VALUES + (1, 1, 'Sample NEXUS', 'Sample Description', 'http://sample.com', 'root', null); +INSERT INTO oss (oss_idx, oss_type_idx, oss_name, oss_desc, oss_url, oss_username, oss_password) +VALUES + (2, 2, 'Sample WorkflowManager', 'Sample Description', 'http://sample.com', null, null); + diff --git a/src/main/resources/static/favicon.ico b/src/main/resources/static/favicon.ico new file mode 100644 index 0000000000000000000000000000000000000000..8e29c719333fe995faa784ce077c976e1cc2b812 GIT binary patch literal 13445 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/static/tabler/software-catalog-list-entity-workflow.html b/src/main/resources/static/tabler/software-catalog-list-entity-workflow.html index 47be9dc..0b122cb 100644 --- a/src/main/resources/static/tabler/software-catalog-list-entity-workflow.html +++ b/src/main/resources/static/tabler/software-catalog-list-entity-workflow.html @@ -1,3 +1,3 @@
  • {{workflowTitle}} - +
  • \ No newline at end of file diff --git a/src/main/resources/static/tabler/software-catalog-list-entity.html b/src/main/resources/static/tabler/software-catalog-list-entity.html index 49de7b4..548a4ba 100644 --- a/src/main/resources/static/tabler/software-catalog-list-entity.html +++ b/src/main/resources/static/tabler/software-catalog-list-entity.html @@ -7,7 +7,7 @@
    Górą ty
    -
    +
    {{title}}
    {{summary}} @@ -36,7 +36,7 @@
    -