|
|
|
|
<script setup lang="ts">
|
|
|
|
|
import { computed, onMounted, onBeforeUnmount, ref, watch } from "vue";
|
|
|
|
|
import { KubeflowService } from "@/components/service/management/KubeflowService";
|
|
|
|
|
|
|
|
|
|
type RunPayload = {
|
|
|
|
|
display_name: string;
|
|
|
|
|
description?: string;
|
|
|
|
|
pipeline_version_reference: { pipeline_id: string };
|
|
|
|
|
runtime_config?: { parameters?: Record<string, any> };
|
|
|
|
|
service_account?: string;
|
|
|
|
|
experiment_id?: string;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
const props = defineProps<{ pipelineId?: string | number | null }>();
|
|
|
|
|
|
|
|
|
|
const emit = defineEmits<{
|
|
|
|
|
(e: "close-modal"): void;
|
|
|
|
|
(e: "submitted", value: any): void;
|
|
|
|
|
}>();
|
|
|
|
|
|
|
|
|
|
const form = ref({
|
|
|
|
|
display_name: "",
|
|
|
|
|
description: "",
|
|
|
|
|
pipeline_id: "",
|
|
|
|
|
experiment_id: "" as string,
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
const loading = ref(false);
|
|
|
|
|
const errorMsg = ref("");
|
|
|
|
|
|
|
|
|
|
// 드롭다운 옵션
|
|
|
|
|
type ExperimentOption = { label: string; value: string; created?: string };
|
|
|
|
|
const experimentOptions = ref<ExperimentOption[]>([]);
|
|
|
|
|
const expLoading = ref(false);
|
|
|
|
|
|
|
|
|
|
const isValid = computed(
|
|
|
|
|
() => !!form.value.display_name.trim() && !!form.value.pipeline_id.trim(),
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
function initForm() {
|
|
|
|
|
form.value.pipeline_id = props.pipelineId ? String(props.pipelineId) : "";
|
|
|
|
|
}
|
|
|
|
|
onMounted(initForm);
|
|
|
|
|
watch(() => props.pipelineId, initForm);
|
|
|
|
|
|
|
|
|
|
function onEsc(e: KeyboardEvent) {
|
|
|
|
|
if (e.key === "Escape" && !loading.value) emit("close-modal");
|
|
|
|
|
}
|
|
|
|
|
onMounted(() => window.addEventListener("keydown", onEsc));
|
|
|
|
|
onBeforeUnmount(() => window.removeEventListener("keydown", onEsc));
|
|
|
|
|
|
|
|
|
|
/** 모든 experiments 수집해서 드롭다운 채우기 (콘솔 출력 제거) */
|
|
|
|
|
async function loadExperimentsAll() {
|
|
|
|
|
expLoading.value = true;
|
|
|
|
|
try {
|
|
|
|
|
const first = await KubeflowService.experiments({ pageSize: 500 });
|
|
|
|
|
const all: any[] = [...(first.data?.experiments ?? [])];
|
|
|
|
|
let token: string | undefined = first.data?.next_page_token;
|
|
|
|
|
|
|
|
|
|
if (token) {
|
|
|
|
|
const { data } = await KubeflowService.experiments({
|
|
|
|
|
pageSize: 500,
|
|
|
|
|
pageToken: token,
|
|
|
|
|
});
|
|
|
|
|
all.push(...(data?.experiments ?? []));
|
|
|
|
|
token = data?.next_page_token;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
experimentOptions.value = all.map((x: any) => ({
|
|
|
|
|
label: x?.display_name ?? x?.name ?? "(no name)",
|
|
|
|
|
value: x?.experiment_id ?? x?.id ?? x?.name,
|
|
|
|
|
created: x?.created_at ?? x?.create_time,
|
|
|
|
|
}));
|
|
|
|
|
|
|
|
|
|
if (!form.value.experiment_id && experimentOptions.value.length > 0) {
|
|
|
|
|
form.value.experiment_id = experimentOptions.value[0].value;
|
|
|
|
|
}
|
|
|
|
|
} catch (e: any) {
|
|
|
|
|
errorMsg.value =
|
|
|
|
|
e?.response?.data?.message ||
|
|
|
|
|
e?.response?.data?.error ||
|
|
|
|
|
e?.message ||
|
|
|
|
|
"Experiments 로드에 실패했습니다.";
|
|
|
|
|
} finally {
|
|
|
|
|
expLoading.value = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
onMounted(loadExperimentsAll);
|
|
|
|
|
|
|
|
|
|
async function submitRun() {
|
|
|
|
|
errorMsg.value = "";
|
|
|
|
|
if (!isValid.value) {
|
|
|
|
|
errorMsg.value = "Run 제목(display_name)과 pipeline_id는 필수입니다.";
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const payload: RunPayload = {
|
|
|
|
|
display_name: form.value.display_name.trim(),
|
|
|
|
|
...(form.value.description.trim() && {
|
|
|
|
|
description: form.value.description.trim(),
|
|
|
|
|
}),
|
|
|
|
|
pipeline_version_reference: { pipeline_id: form.value.pipeline_id.trim() },
|
|
|
|
|
service_account: "pipeline-runner",
|
|
|
|
|
...(form.value.experiment_id && {
|
|
|
|
|
experiment_id: form.value.experiment_id,
|
|
|
|
|
}),
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
loading.value = true;
|
|
|
|
|
const { data } = await KubeflowService.run(payload);
|
|
|
|
|
emit("submitted", data);
|
|
|
|
|
emit("close-modal");
|
|
|
|
|
} catch (e: any) {
|
|
|
|
|
errorMsg.value =
|
|
|
|
|
e?.response?.data?.message ||
|
|
|
|
|
e?.response?.data?.error ||
|
|
|
|
|
e?.message ||
|
|
|
|
|
"Run 생성에 실패했습니다.";
|
|
|
|
|
} finally {
|
|
|
|
|
loading.value = false;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
<template>
|
|
|
|
|
<v-card>
|
|
|
|
|
<v-card-title
|
|
|
|
|
class="text-white font-weight-bold text-h6"
|
|
|
|
|
style="background-color: #1976d2"
|
|
|
|
|
>
|
|
|
|
|
Run Pipeline
|
|
|
|
|
</v-card-title>
|
|
|
|
|
|
|
|
|
|
<v-card-text class="pa-6">
|
|
|
|
|
<v-form @submit.prevent="submitRun">
|
|
|
|
|
<!-- 제목 -->
|
|
|
|
|
<div class="mb-4">
|
|
|
|
|
<label class="text-subtitle-2 font-weight-medium mb-1 d-block">
|
|
|
|
|
Run Title (display_name)
|
|
|
|
|
</label>
|
|
|
|
|
<v-text-field
|
|
|
|
|
v-model="form.display_name"
|
|
|
|
|
variant="outlined"
|
|
|
|
|
:disabled="loading"
|
|
|
|
|
density="comfortable"
|
|
|
|
|
hide-details="auto"
|
|
|
|
|
required
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- 내용 -->
|
|
|
|
|
<div class="mb-4">
|
|
|
|
|
<label class="text-subtitle-2 font-weight-medium mb-1 d-block">
|
|
|
|
|
Run Description
|
|
|
|
|
</label>
|
|
|
|
|
<v-textarea
|
|
|
|
|
v-model="form.description"
|
|
|
|
|
variant="outlined"
|
|
|
|
|
:disabled="loading"
|
|
|
|
|
rows="3"
|
|
|
|
|
density="comfortable"
|
|
|
|
|
hide-details="auto"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- pipeline_id (읽기 전용) -->
|
|
|
|
|
<div class="mb-2">
|
|
|
|
|
<label class="text-subtitle-2 font-weight-medium mb-1 d-block">
|
|
|
|
|
pipeline_id
|
|
|
|
|
</label>
|
|
|
|
|
<v-text-field
|
|
|
|
|
v-model="form.pipeline_id"
|
|
|
|
|
variant="outlined"
|
|
|
|
|
:disabled="true"
|
|
|
|
|
density="comfortable"
|
|
|
|
|
hide-details="auto"
|
|
|
|
|
required
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<!-- Experiment 선택 -->
|
|
|
|
|
<div class="mb-2">
|
|
|
|
|
<label class="text-subtitle-2 font-weight-medium mb-1 d-block">
|
|
|
|
|
Experiment
|
|
|
|
|
</label>
|
|
|
|
|
<v-select
|
|
|
|
|
v-model="form.experiment_id"
|
|
|
|
|
:items="experimentOptions"
|
|
|
|
|
item-title="label"
|
|
|
|
|
item-value="value"
|
|
|
|
|
variant="outlined"
|
|
|
|
|
:disabled="loading || expLoading"
|
|
|
|
|
hide-details="auto"
|
|
|
|
|
:loading="expLoading"
|
|
|
|
|
clearable
|
|
|
|
|
placeholder="Select experiment"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div v-if="errorMsg" class="mt-3 text-error">{{ errorMsg }}</div>
|
|
|
|
|
</v-form>
|
|
|
|
|
</v-card-text>
|
|
|
|
|
|
|
|
|
|
<v-card-actions class="justify-end" style="padding: 16px 24px">
|
|
|
|
|
<v-btn
|
|
|
|
|
color="success"
|
|
|
|
|
:loading="loading"
|
|
|
|
|
:disabled="!isValid"
|
|
|
|
|
@click="submitRun"
|
|
|
|
|
>
|
|
|
|
|
RUN
|
|
|
|
|
</v-btn>
|
|
|
|
|
<v-btn text :disabled="loading" @click="$emit('close-modal')"
|
|
|
|
|
>CLOSE</v-btn
|
|
|
|
|
>
|
|
|
|
|
</v-card-actions>
|
|
|
|
|
</v-card>
|
|
|
|
|
</template>
|