From d31c56d331c3ec082f18552493ddbd01031d7cf2 Mon Sep 17 00:00:00 2001 From: bjkim Date: Tue, 23 Sep 2025 22:24:54 +0900 Subject: [PATCH] =?UTF-8?q?[REFACTOR]=20Kubeflow=20Run=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=20=EC=A1=B0=ED=9A=8C=20API=EB=A5=BC=20ExperimentsCont?= =?UTF-8?q?roller=EC=97=90=EC=84=9C=20=EB=B6=84=EB=A6=AC=ED=95=98=EC=97=AC?= =?UTF-8?q?=20KubeflowRunsController=EB=A1=9C=20=EC=9D=B4=EB=8F=99=20?= =?UTF-8?q?=EB=B0=8F=20=EA=B4=80=EB=A0=A8=20=EC=9A=94=EC=B2=AD=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EB=A1=9C=EC=A7=81=20=EB=A6=AC=ED=8C=A9=ED=86=A0?= =?UTF-8?q?=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controllers/ExperimentsController.java | 52 ---------- .../controllers/KubeflowRunsController.java | 95 +++++++++++++++++++ 2 files changed, 95 insertions(+), 52 deletions(-) create mode 100644 src/main/java/kr/re/etri/autoflow/controllers/KubeflowRunsController.java diff --git a/src/main/java/kr/re/etri/autoflow/controllers/ExperimentsController.java b/src/main/java/kr/re/etri/autoflow/controllers/ExperimentsController.java index b793802..d90b28c 100644 --- a/src/main/java/kr/re/etri/autoflow/controllers/ExperimentsController.java +++ b/src/main/java/kr/re/etri/autoflow/controllers/ExperimentsController.java @@ -209,58 +209,6 @@ public class ExperimentsController { .doOnError(e -> log.error("Experiment 등록 실패", e)); } - @Operation(summary = "Kubeflow Run 목록 조회", description = "Kubeflow API를 호출하여 Run 목록을 조회합니다.") - @ApiResponses({ - @ApiResponse(responseCode = "200", description = "Run 목록 조회 성공"), - @ApiResponse(responseCode = "400", description = "잘못된 요청"), - @ApiResponse(responseCode = "500", description = "서버 내부 오류") - }) - @GetMapping("/api/kubeflow/runs") - public Mono> listRuns( - @Parameter(description = "페이지 토큰 (다음 페이지 조회 시 사용)", example = "") - @RequestParam(value = "page_token", defaultValue = "") String pageToken, - - @Parameter(description = "페이지 크기", example = "10") - @RequestParam(value = "page_size", defaultValue = "10") int pageSize, - - @Parameter(description = "정렬 기준 (sort_by)", example = "created_at desc") - @RequestParam(value = "sort_by", defaultValue = "created_at desc") String sortBy, - - @Parameter(description = "필터 JSON 문자열 (URL 인코딩 필요)", example = "{\"predicates\":[{\"key\":\"storage_state\",\"operation\":\"NOT_EQUALS\",\"string_value\":\"ARCHIVED\"}]}") - @RequestParam(value = "filter", required = false) String filter - ) { - try { - // filter가 없으면 기본값 적용 - if (filter == null || filter.isBlank()) { - filter = "{\"predicates\":[{\"key\":\"storage_state\",\"operation\":\"NOT_EQUALS\",\"string_value\":\"ARCHIVED\"}]}"; - } - - String encodedFilter = URLEncoder.encode(filter, StandardCharsets.UTF_8); - - String uri = String.format( - "%s/apis/v2beta1/runs?page_token=%s&page_size=%d&sort_by=%s&filter=%s", - kubeflowBaseUrl, pageToken, pageSize, URLEncoder.encode(sortBy, StandardCharsets.UTF_8), encodedFilter - ); - - return webClientBuilder.build() - .get() - .uri(uri) - .accept(MediaType.APPLICATION_JSON) - .retrieve() - .bodyToMono(Map.class) - .map(ResponseEntity::ok) - .doOnError(e -> log.error("Kubeflow Runs 조회 실패", e)); - - } catch (Exception e) { - log.error("URI 생성 실패", e); - return Mono.just(ResponseEntity.status(500).body(Map.of( - "status", 500, - "error", "Internal Server Error", - "message", e.getMessage() - ))); - } - } - @Operation(summary = "Experiment 수정") @PutMapping("/{id}") public ResponseEntity updateExperiment( diff --git a/src/main/java/kr/re/etri/autoflow/controllers/KubeflowRunsController.java b/src/main/java/kr/re/etri/autoflow/controllers/KubeflowRunsController.java new file mode 100644 index 0000000..5d13c10 --- /dev/null +++ b/src/main/java/kr/re/etri/autoflow/controllers/KubeflowRunsController.java @@ -0,0 +1,95 @@ +package kr.re.etri.autoflow.controllers; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import jakarta.transaction.Transactional; +import kr.re.etri.autoflow.entity.ExperimentsEntity; +import kr.re.etri.autoflow.payload.request.ProjectBaseSearchRequest; +import kr.re.etri.autoflow.service.ExperimentsService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springdoc.core.annotations.ParameterObject; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.data.domain.Page; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; +import org.springframework.web.reactive.function.client.WebClient; +import reactor.core.publisher.Mono; + +import java.net.URLEncoder; +import java.nio.charset.StandardCharsets; +import java.time.Instant; +import java.time.OffsetDateTime; +import java.time.ZoneId; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Tag(name = "Experiments", description = "Kubeflow 및 MLflow Experiment API") +@RestController +@RequestMapping("/api/experiments") +@RequiredArgsConstructor +@Slf4j +public class KubeflowRunsController { + private final WebClient.Builder webClientBuilder; + @Value("${kubeflow.url}") + private String kubeflowBaseUrl; // 예: http://192.168.10.135:32473/ + + @Operation(summary = "Kubeflow Run 목록 조회", description = "Kubeflow API를 호출하여 Run 목록을 조회합니다.") + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "Run 목록 조회 성공"), + @ApiResponse(responseCode = "400", description = "잘못된 요청"), + @ApiResponse(responseCode = "500", description = "서버 내부 오류") + }) + @GetMapping("/runs") + public Mono> listRuns( + @Parameter(description = "페이지 토큰 (다음 페이지 조회 시 사용)", example = "") + @RequestParam(value = "page_token", defaultValue = "") String pageToken, + + @Parameter(description = "페이지 크기", example = "10") + @RequestParam(value = "page_size", defaultValue = "10") int pageSize, + + @Parameter(description = "정렬 기준 (sort_by)", example = "created_at desc") + @RequestParam(value = "sort_by", defaultValue = "created_at desc") String sortBy, + + @Parameter(description = "필터 JSON 문자열 (URL 인코딩 필요)", example = "{\"predicates\":[{\"key\":\"storage_state\",\"operation\":\"NOT_EQUALS\",\"string_value\":\"ARCHIVED\"}]}") + @RequestParam(value = "filter", required = false) String filter + ) { + try { + // filter가 없으면 기본값 적용 + if (filter == null || filter.isBlank()) { + filter = "{\"predicates\":[{\"key\":\"storage_state\",\"operation\":\"NOT_EQUALS\",\"string_value\":\"ARCHIVED\"}]}"; + } + + String encodedFilter = URLEncoder.encode(filter, StandardCharsets.UTF_8); + + String uri = String.format( + "%s/apis/v2beta1/runs?page_token=%s&page_size=%d&sort_by=%s&filter=%s", + kubeflowBaseUrl, pageToken, pageSize, URLEncoder.encode(sortBy, StandardCharsets.UTF_8), encodedFilter + ); + + return webClientBuilder.build() + .get() + .uri(uri) + .accept(MediaType.APPLICATION_JSON) + .retrieve() + .bodyToMono(Map.class) + .map(ResponseEntity::ok) + .doOnError(e -> log.error("Kubeflow Runs 조회 실패", e)); + + } catch (Exception e) { + log.error("URI 생성 실패", e); + return Mono.just(ResponseEntity.status(500).body(Map.of( + "status", 500, + "error", "Internal Server Error", + "message", e.getMessage() + ))); + } + } +}