{
defaultExpandedKeys: React.Key[];
defaultCheckedKeys: React.Key[];
defaultActiveTab: CommonTabKeys;
- onOk?: (params: ResourceSelectorResponse | null) => void;
+ onOk?: (params: ResourceSelectorResponse | string | null) => void;
}
type ResourceGroup = {
@@ -116,6 +152,13 @@ type ResourceGroup = {
name: string; // 数据集或者模型 id
};
+type MirrorVersion = {
+ id: number; // 镜像版本id
+ status: MirrorVersionStatus; // 镜像版本状态
+ tag_name: string; // 镜像版本
+ url: string; // 镜像版本路径
+};
+
type ResourceFile = {
id: number; // 文件 id
file_name: string; // 文件 name
@@ -133,6 +176,27 @@ const convertToTreeData = (list: ResourceGroup[]): TreeDataNode[] => {
}));
};
+// 版本转成 treeData
+const convertVersionToTreeData = (parentId: number) => {
+ return (item: string | MirrorVersion): TreeDataNode => {
+ if (typeof item === 'string') {
+ return {
+ title: item,
+ key: `${parentId}-${item}`,
+ isLeaf: true,
+ checkable: true,
+ };
+ } else {
+ return {
+ title: item.tag_name,
+ key: `${parentId}-${item.id}-${item.url}`,
+ isLeaf: true,
+ checkable: true,
+ };
+ }
+ };
+};
+
// 更新树形结构的 children
const updateChildren = (parentId: number, children: TreeDataNode[]) => {
return (node: TreeDataNode) => {
@@ -197,11 +261,11 @@ function ResourceSelectorModal({
// 获取数据集或模型列表
const getTreeData = async () => {
- const available_range = activeTab === CommonTabKeys.Private ? '0' : '1';
+ const available_range = activeTab === CommonTabKeys.Private ? 0 : 1;
const params = {
page: 0,
size: 200,
- available_range: available_range,
+ [selectorTypeData[type].litReqParamKey]: available_range,
};
const getListReq = selectorTypeData[type].getList;
const [res] = await to(getListReq(params));
@@ -222,13 +286,8 @@ function ResourceSelectorModal({
const getVersionsReq = selectorTypeData[type].getVersions;
const [res, error] = await to(getVersionsReq(parentId));
if (res) {
- const list = res.data || [];
- const children = list.map((v: string) => ({
- title: v,
- key: `${parentId}-${v}`,
- isLeaf: true,
- checkable: true,
- }));
+ const list = selectorTypeData[type].handleVersionResponse(res);
+ const children = list.map(convertVersionToTreeData(parentId));
// 更新 treeData children
setOriginTreeData((prev) => prev.map(updateChildren(parentId, children)));
// 缓存 loadedKeys
@@ -248,7 +307,7 @@ function ResourceSelectorModal({
const getFiles = async (id: number, version: string) => {
const getFilesReq = selectorTypeData[type].getFiles;
const paramsKey = selectorTypeData[type].fileReqParamKey;
- const params = { version: version, [paramsKey]: id } as GetFilesReqParam;
+ const params = { version: version, [paramsKey]: id };
const [res] = await to(getFilesReq(params));
if (res) {
setVersionPath(res.data?.path || '');
@@ -329,17 +388,21 @@ function ResourceSelectorModal({
// 提交
const handleOk = () => {
if (checkedKeys.length > 0) {
- const last = checkedKeys[0] as string;
- const { id, version } = getIdAndVersion(last);
- const name = (treeData.find((v) => Number(v.key) === id)?.title ?? '') as string;
- const res = {
- id,
- name,
- path: versionPath,
- version,
- activeTab: activeTab as CommonTabKeys,
- };
- onOk?.(res);
+ if (type === ResourceSelectorType.Mirror) {
+ onOk?.(files[0].file_name);
+ } else {
+ const last = checkedKeys[0] as string;
+ const { id, version } = getIdAndVersion(last);
+ const name = (treeData.find((v) => Number(v.key) === id)?.title ?? '') as string;
+ const res = {
+ id,
+ name,
+ path: versionPath,
+ version,
+ activeTab: activeTab as CommonTabKeys,
+ };
+ onOk?.(res);
+ }
} else {
onOk?.(null);
}
@@ -347,7 +410,10 @@ function ResourceSelectorModal({
const title = `选择${selectorTypeData[type].name}`;
const palceholder = `请输入${selectorTypeData[type].name}名称`;
- const fileTitle = `已选${selectorTypeData[type].name}文件(${files.length})`;
+ const fileTitle =
+ type === ResourceSelectorType.Mirror
+ ? '已选镜像'
+ : `已选${selectorTypeData[type].name}文件(${files.length})`;
const tabItems = selectorTypeData[type].tabItems;
const titleImg = selectorTypeData[type].modalIcon;
diff --git a/react-ui/src/pages/Pipeline/editPipeline/globalParamsDrawer.tsx b/react-ui/src/pages/Pipeline/editPipeline/globalParamsDrawer.tsx
index fad63da3..82532a06 100644
--- a/react-ui/src/pages/Pipeline/editPipeline/globalParamsDrawer.tsx
+++ b/react-ui/src/pages/Pipeline/editPipeline/globalParamsDrawer.tsx
@@ -4,8 +4,9 @@ import {
} from '@/pages/Experiment/experimentText/addExperimentModal';
import { type PipelineGlobalParam } from '@/types';
import { to } from '@/utils/promise';
+import { modalConfirm } from '@/utils/ui';
import { DeleteOutlined, PlusOutlined } from '@ant-design/icons';
-import { Button, Drawer, Form, Input, Radio } from 'antd';
+import { Button, Drawer, Form, Input, Radio, Tooltip } from 'antd';
import { NamePath } from 'antd/es/form/interface';
import { forwardRef, useImperativeHandle } from 'react';
import styles from './globalParamsDrawer.less';
@@ -22,9 +23,8 @@ const GlobalParamsDrawer = forwardRef(
useImperativeHandle(ref, () => ({
getFieldsValue: async () => {
- const [res, error] = await to(form.validateFields());
- if (res && !error) {
- const values = form.getFieldsValue();
+ const [values, error] = await to(form.validateFields());
+ if (!error && values) {
return values;
} else {
return Promise.reject(error);
@@ -32,10 +32,20 @@ const GlobalParamsDrawer = forwardRef(
},
}));
+ // 处理参数类型变化
const handleTypeChange = (name: NamePath) => {
form.setFieldValue(name, null);
};
+ const removeParameter = (name: number, remove: (param: number) => void) => {
+ modalConfirm({
+ title: '确认删除该参数吗?',
+ onOk: () => {
+ remove(name);
+ },
+ });
+ };
+
return (
否
-
+
+
+
))}
@@ -150,6 +162,7 @@ const GlobalParamsDrawer = forwardRef(
)}
+ {/* //{contextHolder} */}
);
},
diff --git a/react-ui/src/pages/Pipeline/editPipeline/index.jsx b/react-ui/src/pages/Pipeline/editPipeline/index.jsx
index 94989ae5..948cd364 100644
--- a/react-ui/src/pages/Pipeline/editPipeline/index.jsx
+++ b/react-ui/src/pages/Pipeline/editPipeline/index.jsx
@@ -1,9 +1,7 @@
-import { ReactComponent as ParameterIcon } from '@/assets/svg/parameter.svg';
-import { ReactComponent as SaveAndReturn } from '@/assets/svg/save--return.svg';
+import KFIcon from '@/components/KFIcon';
import { useVisible } from '@/hooks';
import { getWorkflowById, saveWorkflow } from '@/services/pipeline/index.js';
import { to } from '@/utils/promise';
-import { SaveOutlined } from '@ant-design/icons';
import { useEmotionCss } from '@ant-design/use-emotion-css';
import G6 from '@antv/g6';
import { Button, message } from 'antd';
@@ -716,7 +714,7 @@ const EditPipeline = () => {
}
+ icon={}
style={{ marginRight: '20px' }}
onClick={openParamsDrawer}
>
@@ -724,7 +722,7 @@ const EditPipeline = () => {
}
+ icon={}
style={{ marginRight: '20px' }}
onClick={() => {
savePipeline(false);
@@ -740,7 +738,7 @@ const EditPipeline = () => {
background: '#fff',
color: '#1664ff',
}}
- icon={}
+ icon={}
onClick={() => {
savePipeline(true);
}}
diff --git a/react-ui/src/pages/Pipeline/editPipeline/props.jsx b/react-ui/src/pages/Pipeline/editPipeline/props.jsx
index 4e9d833d..1756855d 100644
--- a/react-ui/src/pages/Pipeline/editPipeline/props.jsx
+++ b/react-ui/src/pages/Pipeline/editPipeline/props.jsx
@@ -1,9 +1,10 @@
+import KFIcon from '@/components/KFIcon';
+import { getComputingResourceReq } from '@/services/pipeline';
import { pick } from '@/utils/index';
import { openAntdModal } from '@/utils/modal';
import { to } from '@/utils/promise';
-import { Icon } from '@umijs/max';
-import { Button, Drawer, Form, Input } from 'antd';
-import { forwardRef, useImperativeHandle, useState } from 'react';
+import { Button, Drawer, Form, Input, Select } from 'antd';
+import { forwardRef, useEffect, useImperativeHandle, useState } from 'react';
import ResourceSelectorModal, { ResourceSelectorType } from '../components/ResourceSelectorModal';
import Styles from './editPipeline.less';
const { TextArea } = Input;
@@ -14,6 +15,23 @@ const Props = forwardRef(({ onParentChange }, ref) => {
const [open, setOpen] = useState(false);
const [selectedModel, setSelectedModel] = useState(undefined);
const [selectedDataset, setSelectedDataset] = useState(undefined);
+ const [resourceStandardList, setResourceStandardList] = useState([]);
+
+ useEffect(() => {
+ getComputingResource();
+ }, []);
+
+ const getComputingResource = async () => {
+ const params = {
+ page: 0,
+ size: 1000,
+ resource_type: '',
+ };
+ const [res] = await to(getComputingResourceReq(params));
+ if (res && res.data && res.data.content) {
+ setResourceStandardList(res.data.content);
+ }
+ };
const afterOpenChange = () => {
if (!open) {
@@ -109,9 +127,21 @@ const Props = forwardRef(({ onParentChange }, ref) => {
// 选择数据集、模型
const selectResource = (name, item) => {
- const type =
- item.item_type === 'dataset' ? ResourceSelectorType.Dataset : ResourceSelectorType.Model;
- const resource = type === ResourceSelectorType.Dataset ? selectedDataset : selectedModel;
+ let type;
+ let resource = undefined;
+ switch (item.item_type) {
+ case 'dataset':
+ type = ResourceSelectorType.Dataset;
+ resource = selectedDataset;
+ break;
+ case 'model':
+ type = ResourceSelectorType.Model;
+ resource = selectedModel;
+ break;
+ default:
+ type = ResourceSelectorType.Mirror;
+ break;
+ }
const { close } = openAntdModal(
ResourceSelectorModal,
{
@@ -121,18 +151,23 @@ const Props = forwardRef(({ onParentChange }, ref) => {
defaultActiveTab: resource?.activeTab,
onOk: (res) => {
if (res) {
- const jsonObj = pick(res, ['id', 'version', 'path']);
- const value = JSON.stringify(jsonObj);
- form.setFieldValue(name, value);
+ if (type === ResourceSelectorType.Mirror) {
+ form.setFieldValue(name, res);
+ } else {
+ const jsonObj = pick(res, ['id', 'version', 'path']);
+ const value = JSON.stringify(jsonObj);
+ form.setFieldValue(name, value);
+ }
+
if (type === ResourceSelectorType.Dataset) {
setSelectedDataset(res);
- } else {
+ } else if (type === ResourceSelectorType.Model) {
setSelectedModel(res);
}
} else {
if (type === ResourceSelectorType.Dataset) {
setSelectedDataset(null);
- } else {
+ } else if (type === ResourceSelectorType.Model) {
setSelectedModel(null);
}
form.setFieldValue(name, '');
@@ -148,14 +183,18 @@ const Props = forwardRef(({ onParentChange }, ref) => {
const getSelectBtnIcon = (item) => {
const type = item.item_type;
if (type === 'dataset') {
- return ;
+ return ;
} else if (type === 'model') {
- return ;
+ return ;
} else {
- return ;
+ return ;
}
};
+ const filterResourceStandard = (input, { computing_resource = '' }) => {
+ return computing_resource.toLocaleLowerCase().includes(input.toLocaleLowerCase());
+ };
+
// 控制策略
const controlStrategy = stagingItem.control_strategy;
// 输入参数
@@ -237,17 +276,22 @@ const Props = forwardRef(({ onParentChange }, ref) => {
/>
任务信息
-
-
+
+
+
+
+
+
+
+
+
@@ -262,11 +306,20 @@ const Props = forwardRef(({ onParentChange }, ref) => {
rules={[
{
required: true,
- message: '请输入资源规格',
+ message: '请选择资源规格',
},
]}
>
-
+
diff --git a/react-ui/src/pages/Pipeline/index.jsx b/react-ui/src/pages/Pipeline/index.jsx
index a08aed4e..1dd095b2 100644
--- a/react-ui/src/pages/Pipeline/index.jsx
+++ b/react-ui/src/pages/Pipeline/index.jsx
@@ -1,3 +1,4 @@
+import KFIcon from '@/components/KFIcon';
import {
addWorkflow,
cloneWorkflow,
@@ -6,8 +7,9 @@ import {
getWorkflowById,
removeWorkflow,
} from '@/services/pipeline/index.js';
-import { CopyOutlined, DeleteOutlined, EditOutlined, PlusCircleOutlined } from '@ant-design/icons';
-import { Button, Form, Input, Modal, Space, Table, message } from 'antd';
+import themes from '@/styles/theme.less';
+import { modalConfirm } from '@/utils/ui';
+import { Button, ConfigProvider, Form, Input, Modal, Space, Table, message } from 'antd';
import momnet from 'moment';
import { useEffect, useRef, useState } from 'react';
import { useNavigate } from 'react-router-dom';
@@ -26,7 +28,7 @@ const Pipeline = () => {
const editTable = (e, record) => {
e.stopPropagation();
getWorkflowById(record.id).then((ret) => {
- if (ret.code == 200) {
+ if (ret.code === 200) {
form.resetFields();
form.setFieldsValue({ ...ret.data });
setFormId(ret.data.id);
@@ -72,6 +74,7 @@ const Pipeline = () => {
};
const pageOption = useRef({ page: 1, size: 10 });
const paginationProps = {
+ showSizeChanger: true,
showQuickJumper: true,
showTotal: () => `共${total}条`,
total: total,
@@ -111,7 +114,7 @@ const Pipeline = () => {
title: '序号',
dataIndex: 'index',
key: 'index',
- width: 140,
+ width: 120,
align: 'center',
render(text, record, index) {
return {(pageOption.current.page - 1) * 10 + index + 1};
@@ -152,7 +155,7 @@ const Pipeline = () => {
type="link"
size="small"
key="edit"
- icon={}
+ icon={}
onClick={(e) => {
editTable(e, record);
}}
@@ -163,7 +166,7 @@ const Pipeline = () => {
type="link"
size="small"
key="clone"
- icon={}
+ icon={}
onClick={async () => {
Modal.confirm({
title: '复制',
@@ -173,7 +176,7 @@ const Pipeline = () => {
onOk: () => {
console.log(record);
cloneWorkflow(record.id).then((ret) => {
- if (ret.code == 200) {
+ if (ret.code === 200) {
message.success('复制成功');
getList();
} else {
@@ -192,54 +195,45 @@ const Pipeline = () => {
>
复制
- }
- onClick={async () => {
- Modal.confirm({
- title: (
-
-

-
- 删除后,该流水线将不可恢复
-
-
- ),
- content: 是否确认删除?
,
- closable: true,
-
- okText: '确认',
- cancelText: '取消',
- onOk: () => {
- console.log(record);
- removeWorkflow(record.id).then((ret) => {
- if (ret.code === 200) {
- message.success('删除成功');
- getList();
- } else {
- message.error(ret.msg);
- }
- });
-
- // if (success) {
- // if (actionRef.current) {
- // actionRef.current.reload();
- // }
- // }
- },
- });
+
- 删除
-
+ }
+ onClick={() => {
+ modalConfirm({
+ title: '删除后,该流水线将不可恢复',
+ content: '是否确认删除?',
+ onOk: () => {
+ console.log(record);
+ removeWorkflow(record.id).then((ret) => {
+ if (ret.code === 200) {
+ message.success('删除成功');
+ getList();
+ } else {
+ message.error(ret.msg);
+ }
+ });
+
+ // if (success) {
+ // if (actionRef.current) {
+ // actionRef.current.reload();
+ // }
+ // }
+ },
+ });
+ }}
+ >
+ 删除
+
+
),
},
@@ -251,7 +245,7 @@ const Pipeline = () => {
type="primary"
className={Styles.plusButton}
onClick={showModal}
- icon={}
+ icon={}
>
新建流水线
@@ -309,7 +303,12 @@ const Pipeline = () => {
},
]}
>
-
+
diff --git a/react-ui/src/services/pipeline/index.js b/react-ui/src/services/pipeline/index.js
index d722398b..7262d6b1 100644
--- a/react-ui/src/services/pipeline/index.js
+++ b/react-ui/src/services/pipeline/index.js
@@ -1,3 +1,8 @@
+/*
+ * @Author: 赵伟
+ * @Date: 2024-03-25 13:52:54
+ * @Description:
+ */
import { request } from '@umijs/max';
// 查询流水线列表
export function getWorkflow(params) {
@@ -63,3 +68,11 @@ export function getWorkflowById(id) {
method: 'GET',
});
}
+
+// 获取资源规格
+export function getComputingResourceReq(params) {
+ return request(`/api/mmp/computingResource`, {
+ method: 'GET',
+ params
+ });
+}
diff --git a/react-ui/src/styles/theme.less b/react-ui/src/styles/theme.less
index 9f6aaace..64b78619 100644
--- a/react-ui/src/styles/theme.less
+++ b/react-ui/src/styles/theme.less
@@ -1,17 +1,32 @@
// 全局颜色变量
-// FIXME: 不能设置 @primary-color 不起作用,感觉是哪里被重置了
-@kf-primary-color: #1664ff; // 主色调
-@primary-color-hover: #4086ff;
+@primary-color: #1664ff; // 主色调
+@primary-color-hover: #69b1ff;
@background-color: #f9fafb; // 页面背景颜色
@text-color: #1d1d20;
-@text-color-second: #575757;
-@font-size: 15px;
+@text-color-secondary: #575757;
+@success-color: #1ace62;
+@error-color: #c73131;
+@warning-color: #f98e1b;
+
@border-color: rgba(22, 100, 255, 0.3);
@border-color-second: rgba(22, 100, 255, 0.1);
@background-color-primay: rgba(22, 100, 255, 0.03);
@background-color-gray: rgba(4, 3, 3, 0.06);
+@heading-color: rgba(0, 0, 0, 0.85);
+@input-icon-hover-color: rgba(0, 0, 0, 0.85);
+@border-color-base: #d9d9d9;
+@link-hover-color: #69b1ff;
+
+// 字体大小
+@font-size: 15px;
+
// 导出变量
:export {
- primaryColor: @kf-primary-color;
+ primaryColor: @primary-color;
+ successColor: @success-color;
+ errorColor: @error-color;
+ warningColor: @warning-color;
+ textColor: @text-color;
+ fontSize: @font-size;
}
diff --git a/react-ui/src/utils/modal.tsx b/react-ui/src/utils/modal.tsx
index d55d8378..4a3b765f 100644
--- a/react-ui/src/utils/modal.tsx
+++ b/react-ui/src/utils/modal.tsx
@@ -3,7 +3,8 @@
* @Date: 2024-04-13 10:08:35
* @Description:
*/
-import { type ModalProps } from 'antd';
+import { ConfigProvider, type ModalProps } from 'antd';
+import { globalConfig } from 'antd/es/config-provider';
import React, { useState } from 'react';
import { createRoot } from 'react-dom/client';
@@ -19,19 +20,20 @@ export const openAntdModal = (
modalProps: T,
) => {
const CustomModel = modal;
- const element = document.createElement('div');
- element.id = 'modal-container';
- document.body.appendChild(element);
- const root = createRoot(element);
+ const container = document.createDocumentFragment();
+ const root = createRoot(container);
const { afterClose, onCancel } = modalProps;
+ const global = globalConfig();
+ let timeoutId: ReturnType;
function destroy() {
root.unmount();
- document.body.removeChild(element);
}
function handleAfterClose() {
afterClose?.();
+ // Warning: Attempted to synchronously unmount a root while React was already rendering.
+ // React cannot finish unmounting the root until the current render has completed, which may lead to a race condition.
setTimeout(() => {
destroy();
}, 0);
@@ -46,11 +48,26 @@ export const openAntdModal = (
}
function render(props: T) {
- root.render();
+ clearTimeout(timeoutId);
+
+ timeoutId = setTimeout(() => {
+ const rootPrefixCls = global.getPrefixCls();
+ const iconPrefixCls = global.getIconPrefixCls();
+ const theme = global.getTheme();
+ const dom = (
+
+ );
+
+ root.render(
+
+ {global.holderRender ? global.holderRender(dom) : dom}
+ ,
+ );
+ });
}
function close() {
- render({ ...modalProps, open: false, afterClose: handleAfterClose });
+ render({ ...modalProps, open: false });
}
render({ ...modalProps, open: true });
diff --git a/react-ui/src/utils/ui.tsx b/react-ui/src/utils/ui.tsx
new file mode 100644
index 00000000..afe15ecc
--- /dev/null
+++ b/react-ui/src/utils/ui.tsx
@@ -0,0 +1,28 @@
+/*
+ * @Author: 赵伟
+ * @Date: 2024-04-19 14:42:51
+ * @Description: UI 公共方法
+ */
+import themes from '@/styles/theme.less';
+import { Modal, type ModalFuncProps } from 'antd';
+
+// 自定义 Confirm 弹框
+export function modalConfirm({ title, content, onOk, ...rest }: ModalFuncProps) {
+ Modal.confirm({
+ ...rest,
+ title: (
+
+

+
{title}
+
+ ),
+ content: content && {content}
,
+ okText: '确认',
+ cancelText: '取消',
+ onOk: onOk,
+ });
+}
diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ImageServiceImpl.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ImageServiceImpl.java
index 831c242f..b9a3d990 100644
--- a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ImageServiceImpl.java
+++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/service/impl/ImageServiceImpl.java
@@ -181,7 +181,6 @@ public class ImageServiceImpl implements ImageService {
ImageVersion imageVersion = new ImageVersion();
imageVersion.setImageId(imageInsert.getId());
imageVersion.setVersion(imageVo.getVersion());
- imageVersion.setUrl(imageVo.getUrl());
imageVersion.setTagName(imageVo.getTagName());
imageVersion.setFileSize(imageVo.getFileSize());
imageVersion.setStatus("building");
@@ -241,18 +240,18 @@ public class ImageServiceImpl implements ImageService {
String logs2 = k8sClientUtil.executeCommand(pod,"docker pull "+ netPath);
// 在容器里执行 docker tag name:tag nexus3.kube-system.svc:8083/imageName:imageTag
if (StringUtils.isNoneBlank(logs2)){
- String substring = logs2.substring(logs2.indexOf(harborUrl), logs2.length());
+ String substring = logs2.substring(logs2.lastIndexOf(harborUrl), logs2.length());
String cleanedString = substring.replaceAll("(\\r|\\n)", "");
- String cmd1 = "docker tag " + cleanedString+ " " + harborUrl+"/"+repository+"/"+username+"/" + imageName + imageTag;
- String imageUrl = harborUrl+"/"+repository+"/"+username+"/" + imageName + imageTag;
- String cmd2 = "docker push " + imageUrl;
- String cmd3 = "docker inspect --format='{{.Size}}' " + imageUrl;
-
- String s = k8sClientUtil.executeCommand(pod, cmd1);
- if (StringUtils.isNotEmpty(k8sClientUtil.executeCommand(pod, cmd2))){
+ String tagCmd = "docker tag " + cleanedString + " " + harborUrl + "/" + repository + "/" + username + "/" + imageName + ":" + imageTag;
+ String imageUrl = harborUrl + "/" + repository + "/" + username + "/" + imageName + ":" + imageTag;
+ String pushCmd = "docker push " + imageUrl;
+ String sizeCmd = "docker inspect --format='{{.Size}}' " + imageUrl;
+ String s = k8sClientUtil.executeCommand(pod, tagCmd);
+ if (StringUtils.isNotEmpty(k8sClientUtil.executeCommand(pod, pushCmd))){
resultMap.put("url", imageUrl);
//得到镜像文件大小
- long sizeInBytes = Long.parseLong(k8sClientUtil.executeCommand(pod, cmd3));
+ String imageSizeStr = k8sClientUtil.executeCommand(pod, sizeCmd);
+ long sizeInBytes = Long.parseLong(imageSizeStr.trim());
String formattedImageSize = FileUtil.formatFileSize(sizeInBytes); // 格式化镜像文件大小
resultMap.put("fileSize", formattedImageSize);
return resultMap;
@@ -283,17 +282,18 @@ public class ImageServiceImpl implements ImageService {
String logs2 = k8sClientUtil.executeCommand(pod,"docker load -i "+filePath);
// 在容器里执行 docker tag name:tag nexus3.kube-system.svc:8083/imageName:imageTag
if (StringUtils.isNoneBlank(logs2)){
- String substring = logs2.substring(logs2.indexOf(harborUrl), logs2.length());
+ String substring = logs2.substring(logs2.lastIndexOf(harborUrl), logs2.length());
String cleanedString = substring.replaceAll("(\\r|\\n)", "");
- String cmd1 = "docker tag " + cleanedString+ " " + harborUrl+"/"+repository+"/"+username+"/" + imageName + imageTag;
- String imageUrl = harborUrl+"/"+repository+"/"+username+"/" + imageName + imageTag;
- String cmd2 = "docker push " + imageUrl;
- String cmd3 = "docker inspect --format='{{.Size}}' " + imageUrl;
- String s = k8sClientUtil.executeCommand(pod, cmd1);
- if (StringUtils.isNotEmpty(k8sClientUtil.executeCommand(pod, cmd2))){
+ String tagCmd = "docker tag " + cleanedString + " " + harborUrl + "/" + repository + "/" + username + "/" + imageName + ":" + imageTag;
+ String imageUrl = harborUrl + "/" + repository + "/" + username + "/" + imageName + ":" + imageTag;
+ String pushCmd = "docker push " + imageUrl;
+ String sizeCmd = "docker inspect --format='{{.Size}}' " + imageUrl;
+ String s = k8sClientUtil.executeCommand(pod, tagCmd);
+ if (StringUtils.isNotEmpty(k8sClientUtil.executeCommand(pod, pushCmd))){
resultMap.put("url", imageUrl);
//得到镜像文件大小
- long sizeInBytes = Long.parseLong(k8sClientUtil.executeCommand(pod, cmd3));
+ String imageSizeStr = k8sClientUtil.executeCommand(pod, sizeCmd);
+ long sizeInBytes = Long.parseLong(imageSizeStr.trim());
String formattedImageSize = FileUtil.formatFileSize(sizeInBytes); // 格式化镜像文件大小
resultMap.put("fileSize", formattedImageSize);
return resultMap;
diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/utils/K8sClientUtil.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/utils/K8sClientUtil.java
index 58bdabfe..9815d25c 100644
--- a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/utils/K8sClientUtil.java
+++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/utils/K8sClientUtil.java
@@ -406,6 +406,13 @@ public class K8sClientUtil {
builder.append(line);
builder.append(System.getProperty("line.separator"));
}
+ // 等待进程结束,并获取退出值
+ int exitValue = proc.waitFor();
+ if (exitValue != 0) {
+ // 如果进程的退出值不为0,表示命令执行失败
+ throw new RuntimeException("命令执行失败,退出值:" + exitValue);
+ }
+
return builder.toString();
} catch (Exception e) {
log.error("执行命令异常", e);
diff --git a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/vo/ImageVo.java b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/vo/ImageVo.java
index 1f6319cf..c4b313c2 100644
--- a/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/vo/ImageVo.java
+++ b/ruoyi-modules/management-platform/src/main/java/com/ruoyi/platform/vo/ImageVo.java
@@ -33,11 +33,13 @@ public class ImageVo implements Serializable {
*/
@ApiModelProperty(name = "version")
private String version;
+
/**
* 镜像推送地址
*/
- @ApiModelProperty(name = "url")
- private String url;
+// @ApiModelProperty(name = "url")
+// private String url;
+
/**
* 镜像tag名称
*/
@@ -102,13 +104,13 @@ public class ImageVo implements Serializable {
this.version = version;
}
- public String getUrl() {
- return url;
- }
-
- public void setUrl(String url) {
- this.url = url;
- }
+// public String getUrl() {
+// return url;
+// }
+//
+// public void setUrl(String url) {
+// this.url = url;
+// }
public String getTagName() {
return tagName;