diff --git a/dolphinscheduler-ui-next/src/layouts/content/use-dataList.ts b/dolphinscheduler-ui-next/src/layouts/content/use-dataList.ts index 21d5f5f2d3..0118e93a5f 100644 --- a/dolphinscheduler-ui-next/src/layouts/content/use-dataList.ts +++ b/dolphinscheduler-ui-next/src/layouts/content/use-dataList.ts @@ -45,7 +45,8 @@ import { GroupOutlined, ContainerOutlined, ApartmentOutlined, - BarsOutlined + BarsOutlined, + CloudServerOutlined } from '@vicons/antd' import { useMenuStore } from '@/store/menu/menu' import { useUserStore } from '@/store/user/user' @@ -281,6 +282,11 @@ export function useDataList() { key: `/security/environment-manage`, icon: renderIcon(EnvironmentOutlined) }, + { + label: t('menu.k8s_namespace_manage'), + key: `/security/k8s-namespace-manage`, + icon: renderIcon(CloudServerOutlined) + }, { label: t('menu.token_manage'), key: `/security/token-manage`, diff --git a/dolphinscheduler-ui-next/src/locales/modules/en_US.ts b/dolphinscheduler-ui-next/src/locales/modules/en_US.ts index 1034bc66b0..d161cd9080 100644 --- a/dolphinscheduler-ui-next/src/locales/modules/en_US.ts +++ b/dolphinscheduler-ui-next/src/locales/modules/en_US.ts @@ -73,6 +73,7 @@ const menu = { worker_group_manage: 'Worker Group Manage', yarn_queue_manage: 'Yarn Queue Manage', environment_manage: 'Environment Manage', + k8s_namespace_manage: 'K8S Namespace Manage', token_manage: 'Token Manage', task_group_manage: 'Task Group Manage', task_group_option: 'Task Group Option', @@ -803,6 +804,29 @@ const security = { botToken: 'Bot Token', chatId: 'Channel Chat Id', parseMode: 'Parse Mode' + }, + k8s_namespace: { + create_namespace: 'Create Namespace', + edit_namespace: 'Edit Namespace', + search_tips: 'Please enter keywords', + k8s_namespace: 'K8S Namespace', + k8s_namespace_tips: 'Please enter k8s namespace', + k8s_cluster: 'K8S Cluster', + k8s_cluster_tips: 'Please enter k8s cluster', + owner: 'Owner', + owner_tips: 'Please enter owner', + tag: 'Tag', + tag_tips: 'Please enter tag', + limit_cpu: 'Limit CPU', + limit_cpu_tips: 'Please enter limit CPU', + limit_memory: 'Limit Memory', + limit_memory_tips: 'Please enter limit memory', + create_time: 'Create Time', + update_time: 'Update Time', + operation: 'Operation', + edit: 'Edit', + delete: 'Delete', + delete_confirm: 'Delete?' } } diff --git a/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts b/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts index c17ff86924..22d096fe19 100644 --- a/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts +++ b/dolphinscheduler-ui-next/src/locales/modules/zh_CN.ts @@ -73,6 +73,7 @@ const menu = { worker_group_manage: 'Worker分组管理', yarn_queue_manage: 'Yarn队列管理', environment_manage: '环境管理', + k8s_namespace_manage: 'K8S命名空间管理', token_manage: '令牌管理', task_group_manage: '任务组管理', task_group_option: '任务组配置', @@ -794,6 +795,29 @@ const security = { botToken: '机器人Token', chatId: '频道ID', parseMode: '解析类型' + }, + k8s_namespace: { + create_namespace: '创建命名空间', + edit_namespace: '编辑命名空间', + search_tips: '请输入关键词', + k8s_namespace: 'K8S命名空间', + k8s_namespace_tips: '请输入k8s命名空间', + k8s_cluster: 'K8S集群', + k8s_cluster_tips: '请输入k8s集群', + owner: '负责人', + owner_tips: '请输入负责人', + tag: '标签', + tag_tips: '请输入标签', + limit_cpu: '最大CPU', + limit_cpu_tips: '请输入最大CPU', + limit_memory: '最大内存', + limit_memory_tips: '请输入最大内存', + create_time: '创建时间', + update_time: '更新时间', + operation: '操作', + edit: '编辑', + delete: '删除', + delete_confirm: '确定删除吗?' } } diff --git a/dolphinscheduler-ui-next/src/router/modules/security.ts b/dolphinscheduler-ui-next/src/router/modules/security.ts index bc694a1259..7190f3b188 100644 --- a/dolphinscheduler-ui-next/src/router/modules/security.ts +++ b/dolphinscheduler-ui-next/src/router/modules/security.ts @@ -108,6 +108,16 @@ export default { showSide: true, auth: ['ADMIN_USER'] } + }, + { + path: '/security/k8s-namespace-manage', + name: 'k8s-namespace-manage', + component: components['security-k8s-namespace-manage'], + meta: { + title: 'K8S命名空间管理', + showSide: true, + auth: ['ADMIN_USER'] + } } ] } diff --git a/dolphinscheduler-ui-next/src/service/modules/k8s-namespace/index.ts b/dolphinscheduler-ui-next/src/service/modules/k8s-namespace/index.ts new file mode 100644 index 0000000000..69aaae9684 --- /dev/null +++ b/dolphinscheduler-ui-next/src/service/modules/k8s-namespace/index.ts @@ -0,0 +1,62 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { axios } from '@/service/service' +import { ListReq, K8SReq } from './types' + +export function queryNamespaceListPaging(params: ListReq): any { + return axios({ + url: '/k8s-namespace', + method: 'get', + params + }) +} + +export function verifyNamespaceK8s(params: K8SReq): any { + return axios({ + url: '/k8s-namespace/verify', + method: 'post', + params + }) +} + +export function createK8sNamespace(params: K8SReq): any { + return axios({ + url: '/k8s-namespace', + method: 'post', + params + }) +} + +export function updateK8sNamespace(params: K8SReq, id: number): any { + return axios({ + url: `/k8s-namespace/${id}`, + method: 'put', + params: { + ...params, + id + } + }) +} + +export function delNamespaceById(id: number): any { + return axios({ + url: '/k8s-namespace/delete', + method: 'post', + params: { id } + }) +} diff --git a/dolphinscheduler-ui-next/src/service/modules/k8s-namespace/types.ts b/dolphinscheduler-ui-next/src/service/modules/k8s-namespace/types.ts new file mode 100644 index 0000000000..a29a1cf51a --- /dev/null +++ b/dolphinscheduler-ui-next/src/service/modules/k8s-namespace/types.ts @@ -0,0 +1,52 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +interface ListReq { + pageNo: number + pageSize: number + searchVal?: string +} + +interface K8SReq { + namespace: string + k8s: string + owner?: string + tag?: string + limitsCpu?: number | string + limitsMemory?: number | string +} + +interface NamespaceItem extends K8SReq { + id: number + createTime: string + updateTime: string + podRequestCpu?: any + podRequestMemory?: any + podReplicas?: any + onlineJobNum?: any +} + +interface NamespaceListRes { + totalList: NamespaceItem[] + total: number + totalPage: number + pageSize: number + currentPage: number + start: number +} + +export { ListReq, K8SReq, NamespaceItem, NamespaceListRes } diff --git a/dolphinscheduler-ui-next/src/views/security/environment-manage/index.tsx b/dolphinscheduler-ui-next/src/views/security/environment-manage/index.tsx index f60d2d62df..12a87baa4d 100644 --- a/dolphinscheduler-ui-next/src/views/security/environment-manage/index.tsx +++ b/dolphinscheduler-ui-next/src/views/security/environment-manage/index.tsx @@ -105,7 +105,12 @@ const environmentManage = defineComponent({
- + {t('security.environment.create_environment')}
@@ -129,7 +134,11 @@ const environmentManage = defineComponent({
- +
, + default: false + }, + statusRef: { + type: Number as PropType, + default: 0 + }, + row: { + type: Object as PropType, + default: {} + } + }, + emits: ['cancelModal', 'confirmModal'], + setup(props, ctx) { + const { variables, handleValidate } = useModal(props, ctx) + const { t } = useI18n() + + const cancelModal = () => { + if (props.statusRef === 0) { + variables.model.namespace = '' + variables.model.k8s = '' + variables.model.tag = '' + variables.model.limitsCpu = '' + variables.model.limitsMemory = '' + variables.model.owner = '' + } + ctx.emit('cancelModal', props.showModalRef) + } + + const confirmModal = () => { + handleValidate(props.statusRef) + } + + watch( + () => props.statusRef, + () => { + if (props.statusRef === 0) { + variables.model.namespace = '' + variables.model.k8s = '' + variables.model.tag = '' + variables.model.limitsCpu = '' + variables.model.limitsMemory = '' + variables.model.owner = '' + } else { + variables.model.id = props.row.id + variables.model.namespace = props.row.namespace + variables.model.k8s = props.row.k8s + variables.model.tag = props.row.tag + variables.model.limitsCpu = props.row.limitsCpu + '' + variables.model.limitsMemory = props.row.limitsMemory + '' + variables.model.owner = props.row.owner + } + } + ) + + watch( + () => props.row, + () => { + variables.model.id = props.row.id + variables.model.namespace = props.row.namespace + variables.model.k8s = props.row.k8s + variables.model.tag = props.row.tag + variables.model.limitsCpu = props.row.limitsCpu + '' + variables.model.limitsMemory = props.row.limitsMemory + '' + variables.model.owner = props.row.owner + } + ) + + return { t, ...toRefs(variables), cancelModal, confirmModal } + }, + render() { + const { t } = this + return ( +
+ + {{ + default: () => ( + + + + + + + + + + + + + + CORE + + + + + + GB + + + + + + + ) + }} + +
+ ) + } +}) + +export default K8sNamespaceModal diff --git a/dolphinscheduler-ui-next/src/views/security/k8s-namespace-manage/components/use-modal.ts b/dolphinscheduler-ui-next/src/views/security/k8s-namespace-manage/components/use-modal.ts new file mode 100644 index 0000000000..26206d82e3 --- /dev/null +++ b/dolphinscheduler-ui-next/src/views/security/k8s-namespace-manage/components/use-modal.ts @@ -0,0 +1,99 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { reactive, ref, SetupContext } from 'vue' +import { useI18n } from 'vue-i18n' +import { + verifyNamespaceK8s, + createK8sNamespace, + updateK8sNamespace +} from '@/service/modules/k8s-namespace' + +export function useModal( + props: any, + ctx: SetupContext<('cancelModal' | 'confirmModal')[]> +) { + const { t } = useI18n() + + const variables = reactive({ + k8sNamespaceFormRef: ref(), + model: { + id: ref(-1), + namespace: ref(''), + k8s: ref(''), + owner: ref(''), + tag: ref(''), + limitsCpu: ref(''), + limitsMemory: ref('') + }, + rules: { + namespace: { + required: true, + trigger: ['input', 'blur'], + validator() { + if (variables.model.namespace === '') { + return new Error(t('security.k8s_namespace.k8s_namespace_tips')) + } + } + }, + k8s: { + required: true, + trigger: ['input', 'blur'], + validator() { + if (variables.model.k8s === '') { + return new Error(t('security.k8s_namespace.k8s_cluster_tips')) + } + } + } + } + }) + + const handleValidate = (statusRef: number) => { + variables.k8sNamespaceFormRef.validate((errors: any) => { + if (!errors) { + statusRef === 0 ? submitK8SNamespaceModal() : updateK8SNamespaceModal() + } else { + return + } + }) + } + + const submitK8SNamespaceModal = () => { + verifyNamespaceK8s(variables.model).then(() => { + createK8sNamespace(variables.model).then(() => { + variables.model.namespace = '' + variables.model.k8s = '' + variables.model.tag = '' + variables.model.limitsCpu = '' + variables.model.limitsMemory = '' + variables.model.owner = '' + ctx.emit('confirmModal', props.showModalRef) + }) + }) + } + + const updateK8SNamespaceModal = () => { + updateK8sNamespace(variables.model, variables.model.id).then((res: any) => { + ctx.emit('confirmModal', props.showModalRef) + }) + } + + return { + variables, + handleValidate + } +} diff --git a/dolphinscheduler-ui-next/src/views/security/k8s-namespace-manage/index.module.scss b/dolphinscheduler-ui-next/src/views/security/k8s-namespace-manage/index.module.scss new file mode 100644 index 0000000000..12e457b748 --- /dev/null +++ b/dolphinscheduler-ui-next/src/views/security/k8s-namespace-manage/index.module.scss @@ -0,0 +1,32 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +.search-card { + display: flex; + justify-content: space-between; + align-items: center; +} + +.table-card { + margin-top: 8px; + + .pagination { + margin-top: 20px; + display: flex; + justify-content: center; + } +} diff --git a/dolphinscheduler-ui-next/src/views/security/k8s-namespace-manage/index.tsx b/dolphinscheduler-ui-next/src/views/security/k8s-namespace-manage/index.tsx new file mode 100644 index 0000000000..1ae290d463 --- /dev/null +++ b/dolphinscheduler-ui-next/src/views/security/k8s-namespace-manage/index.tsx @@ -0,0 +1,159 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { defineComponent, onMounted, toRefs, watch } from 'vue' +import { + NButton, + NCard, + NDataTable, + NIcon, + NInput, + NPagination, + NSpace +} from 'naive-ui' +import { SearchOutlined } from '@vicons/antd' +import { useI18n } from 'vue-i18n' +import { useTable } from './use-table' +import Card from '@/components/card' +import K8sNamespaceModal from './components/k8s-namespace-modal' +import styles from './index.module.scss' + +const k8sNamespaceManage = defineComponent({ + name: 'k8s-namespace-manage', + setup() { + const { t } = useI18n() + const { variables, getTableData, createColumns } = useTable() + + const requestData = () => { + getTableData({ + pageSize: variables.pageSize, + pageNo: variables.page, + searchVal: variables.searchVal + }) + } + + const onUpdatePageSize = () => { + variables.page = 1 + requestData() + } + + const onSearch = () => { + variables.page = 1 + requestData() + } + + const handleModalChange = () => { + variables.showModalRef = true + variables.statusRef = 0 + } + + const onCancelModal = () => { + variables.showModalRef = false + } + + const onConfirmModal = () => { + variables.showModalRef = false + requestData() + } + + onMounted(() => { + createColumns(variables) + requestData() + }) + + watch(useI18n().locale, () => { + createColumns(variables) + }) + + return { + t, + ...toRefs(variables), + requestData, + onCancelModal, + onConfirmModal, + onUpdatePageSize, + handleModalChange, + onSearch + } + }, + render() { + const { + t, + requestData, + onUpdatePageSize, + onCancelModal, + onConfirmModal, + handleModalChange, + onSearch + } = this + + return ( +
+ +
+
+ + {t('security.k8s_namespace.create_namespace')} + +
+ + + + {{ + icon: () => ( + + + + ) + }} + + +
+
+ + +
+ +
+
+ +
+ ) + } +}) + +export default k8sNamespaceManage diff --git a/dolphinscheduler-ui-next/src/views/security/k8s-namespace-manage/use-table.ts b/dolphinscheduler-ui-next/src/views/security/k8s-namespace-manage/use-table.ts new file mode 100644 index 0000000000..a14408cad8 --- /dev/null +++ b/dolphinscheduler-ui-next/src/views/security/k8s-namespace-manage/use-table.ts @@ -0,0 +1,201 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You under the Apache License, Version 2.0 + * (the "License"); you may not use this file except in compliance with + * the License. You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import { useAsyncState } from '@vueuse/core' +import { reactive, h, ref } from 'vue' +import { NButton, NPopconfirm, NSpace, NTooltip } from 'naive-ui' +import { useI18n } from 'vue-i18n' +import { format } from 'date-fns' +import { DeleteOutlined, EditOutlined } from '@vicons/antd' +import { + queryNamespaceListPaging, + delNamespaceById +} from '@/service/modules/k8s-namespace' +import type { + NamespaceListRes, + NamespaceItem +} from '@/service/modules/k8s-namespace/types' + +export function useTable() { + const { t } = useI18n() + + const handleEdit = (row: NamespaceItem) => { + variables.showModalRef = true + variables.statusRef = 1 + variables.row = row + } + + const handleDelete = (row: NamespaceItem) => { + delNamespaceById(row.id).then(() => { + getTableData({ + pageSize: variables.pageSize, + pageNo: + variables.tableData.length === 1 && variables.page > 1 + ? variables.page - 1 + : variables.page, + searchVal: variables.searchVal + }) + }) + } + + const createColumns = (variables: any) => { + variables.columns = [ + { + title: '#', + key: 'index' + }, + { + title: t('security.k8s_namespace.k8s_namespace'), + key: 'namespace' + }, + { + title: t('security.k8s_namespace.k8s_cluster'), + key: 'k8s' + }, + { + title: t('security.k8s_namespace.owner'), + key: 'owner' + }, + { + title: t('security.k8s_namespace.tag'), + key: 'tag' + }, + { + title: t('security.k8s_namespace.limit_cpu'), + key: 'limitsCpu' + }, + { + title: t('security.k8s_namespace.limit_memory'), + key: 'limitsMemory' + }, + { + title: t('security.k8s_namespace.create_time'), + key: 'createTime' + }, + { + title: t('security.k8s_namespace.update_time'), + key: 'updateTime' + }, + { + title: t('security.k8s_namespace.operation'), + key: 'operation', + render(row: NamespaceItem) { + return h(NSpace, null, { + default: () => [ + h( + NTooltip, + {}, + { + trigger: () => + h( + NButton, + { + circle: true, + type: 'info', + size: 'small', + onClick: () => { + handleEdit(row) + } + }, + { + icon: () => h(EditOutlined) + } + ), + default: () => t('security.k8s_namespace.edit') + } + ), + h( + NPopconfirm, + { + onPositiveClick: () => { + handleDelete(row) + } + }, + { + trigger: () => + h( + NTooltip, + {}, + { + trigger: () => + h( + NButton, + { + circle: true, + type: 'error', + size: 'small' + }, + { + icon: () => h(DeleteOutlined) + } + ), + default: () => t('security.k8s_namespace.delete') + } + ), + default: () => t('security.k8s_namespace.delete_confirm') + } + ) + ] + }) + } + } + ] + } + + const variables = reactive({ + columns: [], + tableData: [], + page: ref(1), + pageSize: ref(10), + searchVal: ref(null), + totalPage: ref(1), + showModalRef: ref(false), + statusRef: ref(0), + row: {} + }) + + const getTableData = (params: any) => { + const { state } = useAsyncState( + queryNamespaceListPaging({ ...params }).then((res: NamespaceListRes) => { + variables.tableData = res.totalList.map((item, index) => { + item.createTime = format( + new Date(item.createTime), + 'yyyy-MM-dd HH:mm:ss' + ) + item.updateTime = format( + new Date(item.updateTime), + 'yyyy-MM-dd HH:mm:ss' + ) + return { + index: index + 1, + ...item + } + }) as any + variables.totalPage = res.totalPage + }), + {} + ) + + return state + } + + return { + variables, + getTableData, + createColumns + } +} diff --git a/dolphinscheduler-ui-next/src/views/security/token-manage/components/use-modal.ts b/dolphinscheduler-ui-next/src/views/security/token-manage/components/use-modal.ts index 811ceea2f8..2249635e6c 100644 --- a/dolphinscheduler-ui-next/src/views/security/token-manage/components/use-modal.ts +++ b/dolphinscheduler-ui-next/src/views/security/token-manage/components/use-modal.ts @@ -64,7 +64,6 @@ export function useModal( required: true, trigger: ['input', 'blur'], validator() { - console.log(variables.model.expireTime) if (!variables.model.expireTime) { return new Error(t('security.token.expiration_time_tips')) }