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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 13 additions & 13 deletions src/main/java/com/blockcloud/controller/TerraformController.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.blockcloud.controller;

import com.blockcloud.dto.RequestDto.TerraformApplyRequestDto;
import com.blockcloud.dto.RequestDto.TerraformDestroyRequestDto;

import com.blockcloud.dto.RequestDto.TerraformPlanRequestDto;
import com.blockcloud.dto.RequestDto.TerraformValidateRequestDto;
import com.blockcloud.dto.ResponseDto.DeploymentListResponseDto;
Expand Down Expand Up @@ -35,7 +35,6 @@ public class TerraformController {
/**
* Terraform 코드를 검증합니다.
*
* @param projectId 프로젝트 ID
* @param requestDto Terraform 코드 검증 요청
* @return 검증 결과가 담긴 응답 객체
*/
Expand All @@ -45,9 +44,8 @@ public class TerraformController {
)
@PostMapping("/validate")
public ResponseDto<TerraformValidateResponseDto> validateTerraform(
@Parameter(description = "프로젝트 ID", required = true) @PathVariable Long projectId,
@Valid @RequestBody TerraformValidateRequestDto requestDto) {
return ResponseDto.ok(terraformService.validateTerraform(projectId, requestDto));
return ResponseDto.ok(terraformService.validateTerraform(requestDto));
}

/**
Expand Down Expand Up @@ -90,26 +88,28 @@ public ResponseDto<TerraformApplyResponseDto> applyTerraform(
}

/**
* Terraform 코드를 실행하여 인프라를 삭제합니다.
* 특정 배포의 인프라를 삭제합니다.
*
* @param projectId 프로젝트 ID
* @param requestDto Terraform 삭제 요청
* @param deploymentId 배포 ID
* @param authentication 인증 정보
* @return 삭제 시작 정보가 담긴 응답 객체
* @return 삭제 결과가 담긴 응답 객체
*/
@Operation(
summary = "Terraform 인프라 삭제",
description = "Terraform 코드를 실행하여 생성된 인프라를 삭제합니다. 배포는 비동기로 실행되며, 삭제 ID를 반환합니다."
summary = "특정 배포 인프라 삭제",
description = "특정 배포의 인프라를 삭제합니다. 원본 배포의 Terraform 코드를 사용하여 삭제를 수행합니다."
)
@PostMapping("/destroy")
public ResponseDto<TerraformDestroyResponseDto> destroyTerraform(
@DeleteMapping("/deployments/{deploymentId}/destroy")
public ResponseDto<TerraformDestroyResponseDto> destroyTerraformByDeployment(
@Parameter(description = "프로젝트 ID", required = true) @PathVariable Long projectId,
@Valid @RequestBody TerraformDestroyRequestDto requestDto,
@Parameter(description = "배포 ID", required = true) @PathVariable Long deploymentId,
Authentication authentication) {
CustomUserDetails userDetails = (CustomUserDetails) authentication.getPrincipal();
return ResponseDto.ok(terraformService.destroyTerraform(projectId, requestDto, userDetails.getUsername()));
return ResponseDto.ok(terraformService.destroyTerraformByDeployment(projectId, deploymentId, userDetails.getUsername()));
}



/**
* 특정 배포의 상태를 조회합니다.
*
Expand Down
49 changes: 20 additions & 29 deletions src/main/java/com/blockcloud/service/TerraformService.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
import com.blockcloud.domain.project.Project;
import com.blockcloud.domain.project.ProjectRepository;
import com.blockcloud.dto.RequestDto.TerraformApplyRequestDto;
import com.blockcloud.dto.RequestDto.TerraformDestroyRequestDto;

import com.blockcloud.dto.RequestDto.TerraformPlanRequestDto;
import com.blockcloud.dto.RequestDto.TerraformValidateRequestDto;
import com.blockcloud.dto.ResponseDto.DeploymentListResponseDto;
Expand Down Expand Up @@ -40,10 +40,7 @@ public class TerraformService {
/**
* Terraform 코드를 검증합니다.
*/
public TerraformValidateResponseDto validateTerraform(Long projectId, TerraformValidateRequestDto requestDto) {
Project project = projectRepository.findById(projectId)
.orElseThrow(() -> new CommonException(ErrorCode.NOT_FOUND_PROJECT));

public TerraformValidateResponseDto validateTerraform(TerraformValidateRequestDto requestDto) {
TerraformExecutor.TerraformExecutionResult result = terraformExecutor.validate(requestDto.getTerraformCode());

return TerraformValidateResponseDto.builder()
Expand Down Expand Up @@ -72,9 +69,6 @@ public TerraformPlanResponseDto planTerraform(Long projectId, TerraformPlanReque
.build();
}

/**
* Terraform 코드를 적용하여 배포를 시작합니다.
*/
@Transactional
public TerraformApplyResponseDto applyTerraform(Long projectId, TerraformApplyRequestDto requestDto, String username) {
Project project = projectRepository.findById(projectId)
Expand Down Expand Up @@ -109,46 +103,48 @@ public TerraformApplyResponseDto applyTerraform(Long projectId, TerraformApplyRe
.build();
}

/**
* Terraform 코드를 실행하여 인프라를 삭제합니다.
*/
@Transactional
public TerraformDestroyResponseDto destroyTerraform(Long projectId, TerraformDestroyRequestDto requestDto, String username) {
public TerraformDestroyResponseDto destroyTerraformByDeployment(Long projectId, Long deploymentId, String username) {
Project project = projectRepository.findById(projectId)
.orElseThrow(() -> new CommonException(ErrorCode.NOT_FOUND_PROJECT));

// 배포 이력 생성 (삭제용)
Deployment deployment = Deployment.builder()
Deployment deployment = deploymentRepository.findById(deploymentId)
.orElseThrow(() -> new CommonException(ErrorCode.NOT_FOUND_DEPLOYMENT));

// 프로젝트에 속한 배포인지 확인
if (!deployment.getProject().getId().equals(projectId)) {
throw new CommonException(ErrorCode.ACCESS_DENIED);
}

// 삭제용 배포 이력 생성
Deployment destroyDeployment = Deployment.builder()
.project(project)
.status(DeploymentStatus.PENDING)
.message("인프라 삭제 대기 중")
.terraformCode(requestDto.getTerraformCode())
.terraformCode(deployment.getTerraformCode())
.startedAt(LocalDateTime.now())
.build();

Deployment savedDeployment = deploymentRepository.save(deployment);
Deployment savedDestroyDeployment = deploymentRepository.save(destroyDeployment);

// 비동기로 삭제 실행
CompletableFuture.runAsync(() -> {
try {
executeTerraformDestroy(savedDeployment.getId(), projectId, requestDto.getTerraformCode());
executeTerraformDestroy(savedDestroyDeployment.getId(), projectId, deployment.getTerraformCode());
} catch (Exception e) {
log.error("Terraform destroy failed for deployment {}: {}", savedDeployment.getId(), e.getMessage());
updateDeploymentStatus(savedDeployment.getId(), DeploymentStatus.FAILED, "삭제 실패: " + e.getMessage());
log.error("Terraform destroy failed for deployment {}: {}", savedDestroyDeployment.getId(), e.getMessage());
updateDeploymentStatus(savedDestroyDeployment.getId(), DeploymentStatus.FAILED, "삭제 실패: " + e.getMessage());
}
});

return TerraformDestroyResponseDto.builder()
.deploymentId(savedDeployment.getId())
.deploymentId(savedDestroyDeployment.getId())
.status("PENDING")
.message("인프라 삭제가 시작되었습니다.")
.startedAt(savedDeployment.getStartedAt())
.startedAt(savedDestroyDeployment.getStartedAt())
.build();
}

/**
* 배포 상태를 조회합니다.
*/
public DeploymentStatusResponseDto getDeploymentStatus(Long projectId, Long deploymentId) {
Project project = projectRepository.findById(projectId)
.orElseThrow(() -> new CommonException(ErrorCode.NOT_FOUND_PROJECT));
Expand All @@ -171,9 +167,6 @@ public DeploymentStatusResponseDto getDeploymentStatus(Long projectId, Long depl
.build();
}

/**
* 프로젝트의 배포 이력을 조회합니다.
*/
public DeploymentListResponseDto getDeploymentHistory(Long projectId, Long lastId, int size) {
Project project = projectRepository.findById(projectId)
.orElseThrow(() -> new CommonException(ErrorCode.NOT_FOUND_PROJECT));
Expand Down Expand Up @@ -203,8 +196,6 @@ public DeploymentListResponseDto getDeploymentHistory(Long projectId, Long lastI
.build();
}

// Private helper methods

private void executeTerraformApply(Long deploymentId, Long projectId, String terraformCode) {
try {
// 상태를 RUNNING으로 업데이트
Expand Down