[ADD] MlflowController에 Artifact 목록 조회 API 추가, MinioAttachmentService 파일 경로 처리 로직 개선 및 예외 메시지 수정

main
bjkim 8 months ago
parent c8757c2877
commit 9cca0b4108

@ -63,7 +63,7 @@ public class DynamicMinioAttachmentController {
*/
@GetMapping("/download")
public ResponseEntity<byte[]> downloadFile(
@RequestParam String objectName,
@RequestParam(defaultValue = "4/9d08fa7973cf4c39a0979bb4d70c640b/artifacts/sklearn-model/model.pkl") String objectName,
@RequestParam(defaultValue = "type1") String type
) {
try {

@ -108,4 +108,45 @@ public class MlflowController {
.map(ResponseEntity::ok)
.onErrorResume(e -> Mono.just(ResponseEntity.internalServerError().body(e.getMessage())));
}
@Operation(
summary = "Run의 Artifact 목록 조회",
description = """
Run ID Artifact .
MLflow API `/artifacts/list` , `path` Artifact .
""",
responses = {
@ApiResponse(responseCode = "200", description = "Artifact 목록 조회 성공"),
@ApiResponse(responseCode = "404", description = "Run을 찾을 수 없음"),
@ApiResponse(responseCode = "500", description = "서버 오류 발생")
}
)
@GetMapping(value = "/artifacts", produces = MediaType.APPLICATION_JSON_VALUE)
public Mono<ResponseEntity<String>> listArtifacts(
@Parameter(description = "조회할 Run ID", required = true, example = "9d08fa7973cf4c39a0979bb4d70c640b")
@RequestParam String runId,
@Parameter(description = "조회할 경로 (선택)", example = "models")
@RequestParam(required = false) String path,
@Parameter(description = "페이징 토큰 (선택)", example = "MjAyNS0xMC0yN1QxMjo0NjozMlo=")
@RequestParam(required = false, name = "page_token") String pageToken
) {
return webClient.get()
.uri(uriBuilder -> {
var builder = uriBuilder.path("/artifacts/list")
.queryParam("run_id", runId);
if (path != null && !path.isBlank()) {
builder.queryParam("path", path);
}
if (pageToken != null && !pageToken.isBlank()) {
builder.queryParam("page_token", pageToken);
}
return builder.build();
})
.retrieve()
.bodyToMono(String.class)
.map(ResponseEntity::ok)
.onErrorResume(e -> Mono.just(ResponseEntity.internalServerError().body(e.getMessage())));
}
}

@ -107,25 +107,37 @@ public class DynamicMinioAttachmentService {
MinioClient client = getClientByType(type);
String bucketName = getBucketByType(type);
try {
// 1. mlflow-artifacts:/ 접두어 제거
String cleanObjectName = objectName.replaceFirst("^mlflow-artifacts:/", "");
// 2. 잘못된 슬래시 제거
cleanObjectName = cleanObjectName.replaceAll("^/+", "").replaceAll("/+$", "");
// 3. 파일 확장자가 없는 경우 (디렉터리 요청으로 추정)
// → MLflow 구조상 실제 파일은 artifacts/ 하위에 있으므로 경로 자동 보정
if (!cleanObjectName.matches(".*\\.[a-zA-Z0-9]+$")) {
throw new RuntimeException("요청된 객체가 파일이 아닙니다. 실제 파일 경로를 포함해야 합니다: " + cleanObjectName);
}
try (InputStream is = client.getObject(
GetObjectArgs.builder().bucket(bucketName).object(objectName).build()
GetObjectArgs.builder()
.bucket(bucketName)
.object(cleanObjectName)
.build()
)) {
return is.readAllBytes();
}
} catch (io.minio.errors.ErrorResponseException e) {
throw new RuntimeException(
"MinIO 서버가 요청을 거부했습니다: " + objectName +
", 코드=" + e.errorResponse().code() +
", 버킷이름=" + bucketName +
", 메시지=" + e.errorResponse().message() +
", 요청ID=" + e.errorResponse().requestId() +
", 호스트ID=" + e.errorResponse().hostId(), e);
} catch (io.minio.errors.ServerException e) {
throw new RuntimeException("MinIO 서버 오류 발생: " + objectName, e);
} catch (io.minio.errors.InsufficientDataException e) {
throw new RuntimeException("MinIO 데이터 부족 오류: " + objectName, e);
} catch (io.minio.errors.InternalException e) {
throw new RuntimeException("MinIO 내부 오류: " + objectName, e);
} catch (io.minio.errors.InvalidResponseException e) {
throw new RuntimeException("MinIO 응답 파싱 실패: " + objectName, e);
} catch (Exception e) {
throw new RuntimeException("MinIO 파일 다운로드 실패: " + objectName, e);
}

Loading…
Cancel
Save