diff --git a/src/main/java/com/aolda/itda/exception/ErrorCode.java b/src/main/java/com/aolda/itda/exception/ErrorCode.java index 2fc313bcb11256fdda7fc523a6eaf0ff6240c0a1..443f3a10f7c7a2a5ac2f876187a2d88a2e911fe9 100644 --- a/src/main/java/com/aolda/itda/exception/ErrorCode.java +++ b/src/main/java/com/aolda/itda/exception/ErrorCode.java @@ -14,13 +14,20 @@ public enum ErrorCode { // Token INVALID_TOKEN(HttpStatus.BAD_REQUEST, "잘못된 토큰입니다"), - //Forwarding + // Forwarding FAIL_CREATE_CONF(HttpStatus.BAD_REQUEST, "Conf 파일을 생성하지 못했습니다"), FAIL_UPDATE_CONF(HttpStatus.BAD_REQUEST, "Conf 파일을 수정하지 못했습니다"), NOT_FOUND_FORWARDING(HttpStatus.BAD_REQUEST, "포트포워딩 파일이 존재하지 않습니다"), INVALID_CONF_INPUT(HttpStatus.BAD_REQUEST, "잘못된 입력이 존재합니다"), DUPLICATED_INSTANCE_INFO(HttpStatus.BAD_REQUEST, "중복된 인스턴스 IP와 포트입니다"), - DUPLICATED_SERVER_PORT(HttpStatus.BAD_REQUEST, "중복된 서버 포트입니다"); + DUPLICATED_SERVER_PORT(HttpStatus.BAD_REQUEST, "중복된 서버 포트입니다"), + + // Nginx + FAIL_NGINX_CONF_TEST(HttpStatus.BAD_REQUEST, "Conf 파일 테스트에 실패했습니다"), + FAIL_NGINX_CONF_RELOAD(HttpStatus.BAD_REQUEST, "Nginx 재시작에 실패했습니다"), + + FAIL_DELETE_CONF(HttpStatus.BAD_REQUEST, "Conf 파일을 삭제하지 못했습니다"), + FAIL_ROLL_BACK(HttpStatus.BAD_REQUEST, "롤백 실패"); private final HttpStatus status; private final String message; diff --git a/src/main/java/com/aolda/itda/service/forwarding/ForwardingService.java b/src/main/java/com/aolda/itda/service/forwarding/ForwardingService.java index f1d2e465314c149c8345f8ef2275455d6fb91417..4bfbce4a7f8229704c062afabb2bb7909bf75375 100644 --- a/src/main/java/com/aolda/itda/service/forwarding/ForwardingService.java +++ b/src/main/java/com/aolda/itda/service/forwarding/ForwardingService.java @@ -7,12 +7,20 @@ import com.aolda.itda.exception.CustomException; import com.aolda.itda.exception.ErrorCode; import com.aolda.itda.repository.forwarding.ForwardingRepository; import com.aolda.itda.template.ForwardingTemplate; +import com.fasterxml.jackson.databind.ObjectMapper; import jakarta.validation.ConstraintViolation; import jakarta.validation.Validation; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpEntity; +import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.client.HttpClientErrorException; +import org.springframework.web.client.HttpServerErrorException; +import org.springframework.web.client.RestClientException; +import org.springframework.web.client.RestTemplate; import java.io.BufferedWriter; import java.io.File; @@ -21,16 +29,20 @@ import java.io.IOException; import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; +import java.nio.file.StandardCopyOption; +import java.util.Map; @Service @Transactional @RequiredArgsConstructor +@Slf4j public class ForwardingService { @Value("${spring.server.base-ip}") private String serverBaseIp; private final ForwardingTemplate forwardingTemplate; private final ForwardingRepository forwardingRepository; + private final RestTemplate restTemplate = new RestTemplate(); /* 포트포워딩 정보 조회 */ public ForwardingDTO getForwarding(Long forwardingId) { @@ -101,12 +113,47 @@ public class ForwardingService { bw.close(); } catch (Exception e) { e.printStackTrace(); - if (file.exists()) { - file.delete(); + if (file.delete()) { + throw new CustomException(ErrorCode.FAIL_DELETE_CONF); } throw new CustomException(ErrorCode.FAIL_CREATE_CONF, "포트포워딩 Conf 파일을 작성하지 못했습니다"); } + /* nginx test */ + String url = "http://nginx:8081/nginx-api/test"; + try { + restTemplate.getForEntity(url, String.class); + } catch (HttpServerErrorException.InternalServerError e) { + log.error("[nginxApiException] {} : {}", e.getResponseBodyAsString(), e.getMessage()); + if (file.delete()) { + throw new CustomException(ErrorCode.FAIL_NGINX_CONF_TEST, "(롤백 실패)"); + } + throw new CustomException(ErrorCode.FAIL_NGINX_CONF_TEST); + } catch (Exception e) { + log.error("[RestClientException] {} : {}", "Nginx Conf Test (forwarding)", e.getMessage()); + if (file.delete()) { + throw new CustomException(ErrorCode.FAIL_NGINX_CONF_TEST, "(롤백 실패)"); + } + throw new CustomException(ErrorCode.FAIL_NGINX_CONF_TEST); + } + + /* nginx reload */ + url = "http://nginx:8081/nginx-api/reload"; + try { + restTemplate.getForEntity(url, String.class); + } catch (HttpServerErrorException.InternalServerError e) { + log.error("[nginxApiException] {} : {}", e.getResponseBodyAsString(), e.getMessage()); + if (file.delete()) { + throw new CustomException(ErrorCode.FAIL_NGINX_CONF_TEST, "(롤백 실패)"); + } + throw new CustomException(ErrorCode.FAIL_NGINX_CONF_RELOAD); + } catch (Exception e) { + log.error("[RestClientException] {} : {}", "Nginx Conf Reload (forwarding)", e.getMessage()); + if (file.delete()) { + throw new CustomException(ErrorCode.FAIL_NGINX_CONF_TEST, "(롤백 실패)"); + } + throw new CustomException(ErrorCode.FAIL_NGINX_CONF_RELOAD); + } } @@ -140,8 +187,13 @@ public class ForwardingService { throw new CustomException(ErrorCode.NOT_FOUND_FORWARDING, "Conf 파일이 존재하지 않아 수정할 수 없습니다"); } + Path backup; try { - BufferedWriter bw = new BufferedWriter(new FileWriter(file, true)); // 예외처리 필요 + backup = Files.createTempFile("temp_", ".tmp"); + Files.copy(Paths.get(confPath), backup, StandardCopyOption.REPLACE_EXISTING + , StandardCopyOption.COPY_ATTRIBUTES); + + BufferedWriter bw = new BufferedWriter(new FileWriter(file, false)); bw.write(content); bw.flush(); bw.close(); @@ -150,6 +202,59 @@ public class ForwardingService { throw new CustomException(ErrorCode.FAIL_UPDATE_CONF, "포트포워딩 Conf 파일을 수정하지 못했습니다"); } + /* nginx test */ + String url = "http://nginx:8081/nginx-api/test"; + try { + restTemplate.getForEntity(url, String.class); + } catch (HttpServerErrorException.InternalServerError e) { + log.error("[nginxApiException] {} : {}", e.getResponseBodyAsString(), e.getMessage()); + try { + Files.copy(backup, Paths.get(confPath), StandardCopyOption.REPLACE_EXISTING + , StandardCopyOption.COPY_ATTRIBUTES); + Files.delete(backup); + } catch (IOException e1) { + throw new CustomException(ErrorCode.FAIL_UPDATE_CONF, "(포트포워딩 Conf 파일 수정)"); + } + + throw new CustomException(ErrorCode.FAIL_NGINX_CONF_TEST); + } catch (RuntimeException e) { + log.error("[RestClientException] {} : {}", "Nginx Conf Test (forwarding)", e.getMessage()); + try { + Files.copy(backup, Paths.get(confPath), StandardCopyOption.REPLACE_EXISTING + , StandardCopyOption.COPY_ATTRIBUTES); + Files.delete(backup); + } catch (IOException e1) { + throw new CustomException(ErrorCode.FAIL_UPDATE_CONF, "(포트포워딩 Conf 파일 수정)"); + } + throw new CustomException(ErrorCode.FAIL_NGINX_CONF_TEST); + } + + /* nginx reload */ + url = "http://nginx:8081/nginx-api/reload"; + try { + restTemplate.getForEntity(url, String.class); + } catch (HttpServerErrorException.InternalServerError e) { + log.error("[nginxApiException] {} : {}", e.getResponseBodyAsString(), e.getMessage()); + try { + Files.copy(backup, Paths.get(confPath), StandardCopyOption.REPLACE_EXISTING + , StandardCopyOption.COPY_ATTRIBUTES); + Files.delete(backup); + } catch (IOException e1) { + throw new CustomException(ErrorCode.FAIL_UPDATE_CONF, "(포트포워딩 Conf 파일 수정)"); + } + throw new CustomException(ErrorCode.FAIL_NGINX_CONF_RELOAD); + } catch (RuntimeException e) { + log.error("[RestClientException] {} : {}", "Nginx Conf Reload (forwarding)", e.getMessage()); + try { + Files.copy(backup, Paths.get(confPath), StandardCopyOption.REPLACE_EXISTING + , StandardCopyOption.COPY_ATTRIBUTES); + Files.delete(backup); + } catch (IOException e1) { + throw new CustomException(ErrorCode.FAIL_UPDATE_CONF, "(포트포워딩 Conf 파일 수정)"); + } + throw new CustomException(ErrorCode.FAIL_NGINX_CONF_RELOAD); + } + /* DB 정보 수정 */ forwardingRepository.save(forwarding); } @@ -161,9 +266,55 @@ public class ForwardingService { /* 파일 삭제 */ String confPath = "/data/nginx/stream/" + forwarding.getForwardingId() + ".conf"; - File file = new File(confPath); - if (!file.delete()) { - throw new CustomException(ErrorCode.NOT_FOUND_FORWARDING, "Conf 파일이 존재하지 않아 삭제할 수 없습니다"); + String deletePath = confPath + ".deleted"; + try { + Files.move(Paths.get(confPath), Paths.get(deletePath)); + } catch (IOException e) { + throw new CustomException(ErrorCode.FAIL_DELETE_CONF); + } + + /* nginx test */ + String url = "http://nginx:8081/nginx-api/test"; + try { + restTemplate.getForEntity(url, String.class); + } catch (HttpServerErrorException.InternalServerError e) { + log.error("[nginxApiException] {} : {}", e.getResponseBodyAsString(), e.getMessage()); + try { + Files.move(Paths.get(deletePath), Paths.get(confPath)); + } catch (IOException e1) { + throw new CustomException(ErrorCode.FAIL_ROLL_BACK, "(포트포워딩 Conf 삭제)"); + } + throw new CustomException(ErrorCode.FAIL_NGINX_CONF_TEST); + } catch (Exception e) { + log.error("[RestClientException] {} : {}", "Nginx Conf Test (forwarding)", e.getMessage()); + try { + Files.move(Paths.get(deletePath), Paths.get(confPath)); + } catch (IOException e1) { + throw new CustomException(ErrorCode.FAIL_ROLL_BACK, "(포트포워딩 Conf 삭제)"); + } + throw new CustomException(ErrorCode.FAIL_NGINX_CONF_TEST); + } + + /* nginx reload */ + url = "http://nginx:8081/nginx-api/reload"; + try { + restTemplate.getForEntity(url, String.class); + } catch (HttpServerErrorException.InternalServerError e) { + log.error("[nginxApiException] {} : {}", e.getResponseBodyAsString(), e.getMessage()); + try { + Files.move(Paths.get(deletePath), Paths.get(confPath)); + } catch (IOException e1) { + throw new CustomException(ErrorCode.FAIL_ROLL_BACK, "(포트포워딩 Conf 삭제)"); + } + throw new CustomException(ErrorCode.FAIL_NGINX_CONF_RELOAD); + } catch (Exception e) { + log.error("[RestClientException] {} : {}", "Nginx Conf Reload (forwarding)", e.getMessage()); + try { + Files.move(Paths.get(deletePath), Paths.get(confPath)); + } catch (IOException e1) { + throw new CustomException(ErrorCode.FAIL_ROLL_BACK, "(포트포워딩 Conf 삭제)"); + } + throw new CustomException(ErrorCode.FAIL_NGINX_CONF_RELOAD); } /* DB */