From 6edcea695c01b2d00ef8ed6aff9cb07e61302cd1 Mon Sep 17 00:00:00 2001 From: bjkim Date: Fri, 19 Sep 2025 10:05:18 +0900 Subject: [PATCH] =?UTF-8?q?[ADD]=20Pipeline=20=EC=97=85=EB=A1=9C=EB=93=9C?= =?UTF-8?q?=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80=20(Controller,=20Ser?= =?UTF-8?q?vice)=20=EB=B0=8F=20RestTemplate=20=EB=B9=88=20=EB=93=B1?= =?UTF-8?q?=EB=A1=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../re/etri/autoflow/AutoFlowApplication.java | 8 ++- .../controllers/PipelineUploadController.java | 34 +++++++++++ .../MultipartInputStreamFileResource.java | 24 ++++++++ .../service/PipelineUploadService.java | 60 +++++++++++++++++++ src/main/resources/application.properties | 5 ++ 5 files changed, 130 insertions(+), 1 deletion(-) create mode 100644 src/main/java/kr/re/etri/autoflow/controllers/PipelineUploadController.java create mode 100644 src/main/java/kr/re/etri/autoflow/service/MultipartInputStreamFileResource.java create mode 100644 src/main/java/kr/re/etri/autoflow/service/PipelineUploadService.java diff --git a/src/main/java/kr/re/etri/autoflow/AutoFlowApplication.java b/src/main/java/kr/re/etri/autoflow/AutoFlowApplication.java index 44d0a46..4a5a897 100644 --- a/src/main/java/kr/re/etri/autoflow/AutoFlowApplication.java +++ b/src/main/java/kr/re/etri/autoflow/AutoFlowApplication.java @@ -3,7 +3,9 @@ package kr.re.etri.autoflow; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.context.annotation.Bean; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; +import org.springframework.web.client.RestTemplate; @SpringBootApplication @EnableJpaAuditing @@ -14,4 +16,8 @@ public class AutoFlowApplication { SpringApplication.run(AutoFlowApplication.class, args); } -} + @Bean + public RestTemplate restTemplate() { + return new RestTemplate(); + } +} \ No newline at end of file diff --git a/src/main/java/kr/re/etri/autoflow/controllers/PipelineUploadController.java b/src/main/java/kr/re/etri/autoflow/controllers/PipelineUploadController.java new file mode 100644 index 0000000..5ee148b --- /dev/null +++ b/src/main/java/kr/re/etri/autoflow/controllers/PipelineUploadController.java @@ -0,0 +1,34 @@ +package kr.re.etri.autoflow.controllers; + +import kr.re.etri.autoflow.service.PipelineUploadService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.multipart.MultipartFile; + +import java.util.Map; + +@RestController +@RequestMapping("/pipelines") +@RequiredArgsConstructor +public class PipelineUploadController { + + private final PipelineUploadService pipelineUploadService; + + @PostMapping(value = "/upload", consumes = MediaType.MULTIPART_FORM_DATA_VALUE) + public ResponseEntity> uploadPipeline( + @RequestParam("uploadfile") MultipartFile file, + @RequestParam(value = "name", required = false) String name, + @RequestParam(value = "display_name", required = false) String displayName, + @RequestParam(value = "description", required = false) String description, + @RequestParam(value = "namespace", required = false) String namespace) { + + Map result = pipelineUploadService.uploadPipeline(file, name, displayName, description, namespace); + return ResponseEntity.ok(result); + } +} + diff --git a/src/main/java/kr/re/etri/autoflow/service/MultipartInputStreamFileResource.java b/src/main/java/kr/re/etri/autoflow/service/MultipartInputStreamFileResource.java new file mode 100644 index 0000000..1cffae0 --- /dev/null +++ b/src/main/java/kr/re/etri/autoflow/service/MultipartInputStreamFileResource.java @@ -0,0 +1,24 @@ +package kr.re.etri.autoflow.service; + +import org.springframework.core.io.InputStreamResource; +import java.io.InputStream; + +public class MultipartInputStreamFileResource extends InputStreamResource { + + private final String filename; + + public MultipartInputStreamFileResource(InputStream inputStream, String filename) { + super(inputStream); + this.filename = filename; + } + + @Override + public String getFilename() { + return this.filename; + } + + @Override + public long contentLength() { + return -1; // 파일 크기를 알 수 없으면 -1로 설정 + } +} diff --git a/src/main/java/kr/re/etri/autoflow/service/PipelineUploadService.java b/src/main/java/kr/re/etri/autoflow/service/PipelineUploadService.java new file mode 100644 index 0000000..bd26324 --- /dev/null +++ b/src/main/java/kr/re/etri/autoflow/service/PipelineUploadService.java @@ -0,0 +1,60 @@ +package kr.re.etri.autoflow.service; + +import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.util.LinkedMultiValueMap; +import org.springframework.util.MultiValueMap; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.multipart.MultipartFile; +import org.springframework.web.util.UriComponentsBuilder; + +import java.io.IOException; +import java.util.Map; + +@Service +@RequiredArgsConstructor +public class PipelineUploadService { + + private final RestTemplate restTemplate; + + @Value("${kubeflow.pipeline.upload-url}") + private String kubeflowUploadUrl; + + public Map uploadPipeline(MultipartFile file, + String name, + String displayName, + String description, + String namespace) { + try { + // 파일 form-data + MultiValueMap body = new LinkedMultiValueMap<>(); + body.add("uploadfile", new MultipartInputStreamFileResource(file.getInputStream(), file.getOriginalFilename())); + + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.MULTIPART_FORM_DATA); + + HttpEntity> requestEntity = new HttpEntity<>(body, headers); + + // URL 조립 (쿼리 파라미터 방식) + UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(kubeflowUploadUrl); + + if (name != null && !name.isBlank()) builder.queryParam("name", name); + if (displayName != null && !displayName.isBlank()) builder.queryParam("display_name", displayName); + if (description != null && !description.isBlank()) builder.queryParam("description", description); + if (namespace != null && !namespace.isBlank()) builder.queryParam("namespace", namespace); + + String url = builder.toUriString(); + + ResponseEntity response = restTemplate.postForEntity(url, requestEntity, Map.class); + return response.getBody(); + + } catch (IOException e) { + throw new RuntimeException("Pipeline upload failed", e); + } + } +} diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 9baa79d..4717f20 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -54,3 +54,8 @@ minio.endpoint=http://192.168.10.135:31795 minio.access-key=minio minio.secret-key=minio123 minio.bucket=mlpipeline + +# Kubeflow +kubeflow.url=http://192.168.10.135:32473 +kubeflow.api.url=http://192.168.10.135:32473/apis/v2beta1/pipelines +kubeflow.pipeline.upload-url=http://192.168.10.135:32473/apis/v2beta1/pipelines/upload