diff --git a/scaleph-api/src/main/java/cn/sliew/scaleph/api/controller/ws/WsDorisInstanceController.java b/scaleph-api/src/main/java/cn/sliew/scaleph/api/controller/ws/WsDorisInstanceController.java index cb75387b6..b2b179a71 100644 --- a/scaleph-api/src/main/java/cn/sliew/scaleph/api/controller/ws/WsDorisInstanceController.java +++ b/scaleph-api/src/main/java/cn/sliew/scaleph/api/controller/ws/WsDorisInstanceController.java @@ -61,6 +61,14 @@ public ResponseEntity> selectOne(@PathVariable("i return new ResponseEntity(ResponseVO.success(dto), HttpStatus.OK); } + @Logging + @GetMapping("fromTemplate") + @Operation(summary = "根据模板创建实例", description = "根据模板创建实例") + public ResponseEntity> fromTemplate(@RequestParam("templateId") Long templateId) { + WsDorisInstanceDTO cluster = wsDorisInstanceService.fromTemplate(templateId); + return new ResponseEntity(ResponseVO.success(cluster), HttpStatus.OK); + } + @Logging @PostMapping("asYaml") @Operation(summary = "转换实例信息", description = "转换实例信息") diff --git a/scaleph-engine/scaleph-engine-doris/src/main/java/cn/sliew/scaleph/engine/doris/service/impl/WsDorisInstanceServiceImpl.java b/scaleph-engine/scaleph-engine-doris/src/main/java/cn/sliew/scaleph/engine/doris/service/impl/WsDorisInstanceServiceImpl.java index ef16dac45..155eef96e 100644 --- a/scaleph-engine/scaleph-engine-doris/src/main/java/cn/sliew/scaleph/engine/doris/service/impl/WsDorisInstanceServiceImpl.java +++ b/scaleph-engine/scaleph-engine-doris/src/main/java/cn/sliew/scaleph/engine/doris/service/impl/WsDorisInstanceServiceImpl.java @@ -19,6 +19,7 @@ package cn.sliew.scaleph.engine.doris.service.impl; import cn.sliew.milky.common.util.JacksonUtil; +import cn.sliew.scaleph.common.dict.common.YesOrNo; import cn.sliew.scaleph.common.util.UUIDUtil; import cn.sliew.scaleph.dao.entity.master.ws.WsDorisInstance; import cn.sliew.scaleph.dao.mapper.master.ws.WsDorisInstanceMapper; @@ -27,9 +28,11 @@ import cn.sliew.scaleph.engine.doris.service.WsDorisTemplateService; import cn.sliew.scaleph.engine.doris.service.convert.WsDorisInstanceConvert; import cn.sliew.scaleph.engine.doris.service.dto.WsDorisInstanceDTO; +import cn.sliew.scaleph.engine.doris.service.dto.WsDorisTemplateDTO; import cn.sliew.scaleph.engine.doris.service.param.WsDorisInstanceAddParam; import cn.sliew.scaleph.engine.doris.service.param.WsDorisInstanceListParam; import cn.sliew.scaleph.engine.doris.service.param.WsDorisInstanceUpdateParam; +import cn.sliew.scaleph.engine.doris.service.resource.cluster.DorisClusterConverter; import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; import com.baomidou.mybatisplus.core.toolkit.Wrappers; import com.baomidou.mybatisplus.extension.plugins.pagination.Page; @@ -73,12 +76,20 @@ public WsDorisInstanceDTO selectOne(Long id) { @Override public WsDorisInstanceDTO fromTemplate(Long templateId) { - return null; + WsDorisTemplateDTO wsDorisTemplateDTO = wsDorisTemplateService.selectOne(templateId); + WsDorisInstanceDTO wsDorisInstanceDTO = new WsDorisInstanceDTO(); + wsDorisInstanceDTO.setAdmin(wsDorisTemplateDTO.getAdmin()); + wsDorisInstanceDTO.setFeSpec(wsDorisTemplateDTO.getFeSpec()); + wsDorisInstanceDTO.setBeSpec(wsDorisTemplateDTO.getBeSpec()); + wsDorisInstanceDTO.setCnSpec(wsDorisTemplateDTO.getCnSpec()); + wsDorisInstanceDTO.setBrokerSpec(wsDorisTemplateDTO.getBrokerSpec()); + wsDorisInstanceDTO.setRemark("generated from template-" + wsDorisTemplateDTO.getName()); + return wsDorisInstanceDTO; } @Override public DorisCluster asYaml(WsDorisInstanceDTO dto) { - return null; + return DorisClusterConverter.INSTANCE.convertTo(dto); } @Override @@ -86,6 +97,7 @@ public int insert(WsDorisInstanceAddParam param) { WsDorisInstance record = new WsDorisInstance(); BeanUtils.copyProperties(param, record); record.setInstanceId(UUIDUtil.randomUUId()); + record.setDeployed(YesOrNo.NO); if (param.getAdmin() != null) { record.setAdmin(JacksonUtil.toJsonString(param.getAdmin())); } diff --git a/scaleph-engine/scaleph-engine-doris/src/main/java/cn/sliew/scaleph/engine/doris/service/resource/cluster/DorisClusterConverter.java b/scaleph-engine/scaleph-engine-doris/src/main/java/cn/sliew/scaleph/engine/doris/service/resource/cluster/DorisClusterConverter.java new file mode 100644 index 000000000..d7ced0e4b --- /dev/null +++ b/scaleph-engine/scaleph-engine-doris/src/main/java/cn/sliew/scaleph/engine/doris/service/resource/cluster/DorisClusterConverter.java @@ -0,0 +1,73 @@ +/* + * 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. + */ + +package cn.sliew.scaleph.engine.doris.service.resource.cluster; + +import cn.sliew.scaleph.config.resource.ResourceLabels; +import cn.sliew.scaleph.engine.doris.operator.DorisCluster; +import cn.sliew.scaleph.engine.doris.operator.spec.DorisClusterSpec; +import cn.sliew.scaleph.engine.doris.service.dto.WsDorisInstanceDTO; +import cn.sliew.scaleph.kubernetes.resource.ResourceConverter; +import io.fabric8.kubernetes.api.model.ObjectMetaBuilder; +import org.springframework.util.StringUtils; + +import java.util.Map; + +public enum DorisClusterConverter implements ResourceConverter { + INSTANCE; + + @Override + public DorisCluster convertTo(WsDorisInstanceDTO source) { + DorisCluster cluster = new DorisCluster(); + ObjectMetaBuilder builder = new ObjectMetaBuilder(true); + String name = StringUtils.hasText(source.getInstanceId()) ? source.getInstanceId() : source.getName(); + builder.withName(name); + builder.withNamespace(source.getNamespace()); + builder.withLabels(Map.of(ResourceLabels.SCALEPH_LABEL_NAME, source.getName())); + cluster.setMetadata(builder.build()); + DorisClusterSpec spec = new DorisClusterSpec(); + spec.setFeSpec(source.getFeSpec()); + spec.setBeSpec(source.getBeSpec()); + spec.setCnSpec(source.getCnSpec()); + spec.setBrokerSpec(source.getBrokerSpec()); + spec.setAdminUser(source.getAdmin()); + cluster.setSpec(spec); + return cluster; + } + + @Override + public WsDorisInstanceDTO convertFrom(DorisCluster target) { + WsDorisInstanceDTO dto = new WsDorisInstanceDTO(); + String name = target.getMetadata().getName(); + if (target.getMetadata().getLabels() != null) { + Map labels = target.getMetadata().getLabels(); + name = labels.computeIfAbsent(ResourceLabels.SCALEPH_LABEL_NAME, key -> target.getMetadata().getName()); + } + dto.setName(name); + dto.setInstanceId(target.getMetadata().getName()); + dto.setNamespace(target.getMetadata().getNamespace()); + + DorisClusterSpec spec = target.getSpec(); + dto.setFeSpec(spec.getFeSpec()); + dto.setBeSpec(spec.getBeSpec()); + dto.setCnSpec(spec.getCnSpec()); + dto.setBrokerSpec(spec.getBrokerSpec()); + dto.setAdmin(spec.getAdminUser()); + return dto; + } +} diff --git a/scaleph-ui-react/src/locales/zh-CN/pages/project.ts b/scaleph-ui-react/src/locales/zh-CN/pages/project.ts index 3d8ce78ec..b9c84fb17 100644 --- a/scaleph-ui-react/src/locales/zh-CN/pages/project.ts +++ b/scaleph-ui-react/src/locales/zh-CN/pages/project.ts @@ -946,10 +946,13 @@ export default { 'pages.project.doris.instance': 'Instance', 'pages.project.doris.instance.name': '名称', 'pages.project.doris.instance.namespace': '命名空间', + 'pages.project.doris.instance.deployed': '是否部署?', 'pages.project.doris.instance.steps': '创建实例', 'pages.project.doris.instance.steps.base': '基础信息', 'pages.project.doris.instance.steps.base.cluster': 'Cluster', 'pages.project.doris.instance.steps.base.template': 'Template', + 'pages.project.doris.instance.steps.component': '集群组件', + 'pages.project.doris.instance.steps.yaml': 'YAML', 'Run': '运行', diff --git a/scaleph-ui-react/src/models/project/workspace/doris/instance/dorisInstanceSteps.ts b/scaleph-ui-react/src/models/project/workspace/doris/instance/dorisInstanceSteps.ts index 9a90b100c..57b8dc536 100644 --- a/scaleph-ui-react/src/models/project/workspace/doris/instance/dorisInstanceSteps.ts +++ b/scaleph-ui-react/src/models/project/workspace/doris/instance/dorisInstanceSteps.ts @@ -2,9 +2,9 @@ import {WsDorisInstance, WsDorisTemplate} from "@/services/project/typings"; import {Effect, Reducer} from "umi"; import YAML from "yaml"; import {WsDorisTemplateService} from "@/services/project/WsDorisTemplateService"; +import {WsDorisInstanceService} from "@/services/project/WsDorisInstanceService"; export interface StateType { - template: WsDorisTemplate, instance: WsDorisInstance, instanceYaml: string instanceYamlWithDefault: string @@ -28,32 +28,32 @@ const model: ModelType = { namespace: "dorisInstanceSteps", state: { - template: null, - templateYaml: null, - templateYamlWithDefault: null + instance: null, + instanceYaml: null, + instanceYamlWithDefault: null }, effects: { - *editTemplate({payload}, {call, put}) { - const {data} = yield call(WsDorisTemplateService.asYaml, payload); - const response = yield call(WsDorisTemplateService.asYaml, payload); - yield put({type: 'updateTemplate', + *editInstance({payload}, {call, put}) { + const {data} = yield call(WsDorisInstanceService.asYaml, payload); + const response = yield call(WsDorisInstanceService.asYaml, payload); + yield put({type: 'updateInstance', payload: { - template: payload, - templateYaml: YAML.stringify(data), - templateYamlWithDefault: YAML.stringify(response.data) + instance: payload, + instanceYaml: YAML.stringify(data), + instanceYamlWithDefault: YAML.stringify(response.data) } }); }, }, reducers: { - updateTemplate(state, {payload}) { + updateInstance(state, {payload}) { return { ...state, - template: payload.template, - templateYaml: payload.templateYaml, - templateYamlWithDefault: payload.templateYamlWithDefault, + instance: payload.instance, + instanceYaml: payload.instanceYaml, + instanceYamlWithDefault: payload.instanceYamlWithDefault, }; }, }, diff --git a/scaleph-ui-react/src/pages/Project/Workspace/Doris/Instance/Steps/ComponentStepForm.tsx b/scaleph-ui-react/src/pages/Project/Workspace/Doris/Instance/Steps/ComponentStepForm.tsx new file mode 100644 index 000000000..7ff90d37d --- /dev/null +++ b/scaleph-ui-react/src/pages/Project/Workspace/Doris/Instance/Steps/ComponentStepForm.tsx @@ -0,0 +1,31 @@ +import React, {useEffect} from "react"; +import {ProCard} from "@ant-design/pro-components"; +import DorisFeComponent from "@/pages/Project/Workspace/Doris/Template/Steps/Component/DorisFeComponent"; +import DorisAdminUser from "@/pages/Project/Workspace/Doris/Template/Steps/Component/DorisAdminUser"; +import DorisBeComponent from "@/pages/Project/Workspace/Doris/Template/Steps/Component/DorisBeComponent"; +import DorisCnComponent from "@/pages/Project/Workspace/Doris/Template/Steps/Component/DorisCnComponent"; +import {connect} from "umi"; +import {Form} from "antd"; +import {WsDorisTemplateService} from "@/services/project/WsDorisTemplateService"; + +const DorisInstanceComponent: React.FC = (props: any) => { + const form = Form.useFormInstance() + + useEffect(() => { + if (props.dorisInstanceSteps.instance) { + form.setFieldsValue(WsDorisTemplateService.parseData({...props.dorisInstanceSteps.instance})) + } + }, [props.dorisInstanceSteps.instance]); + + return ( + + + + + + + ); +} + +const mapModelToProps = ({dorisInstanceSteps}: any) => ({dorisInstanceSteps}) +export default connect(mapModelToProps)(DorisInstanceComponent); diff --git a/scaleph-ui-react/src/pages/Project/Workspace/Doris/Instance/Steps/YAMLStepForm.tsx b/scaleph-ui-react/src/pages/Project/Workspace/Doris/Instance/Steps/YAMLStepForm.tsx new file mode 100644 index 000000000..807aee7b1 --- /dev/null +++ b/scaleph-ui-react/src/pages/Project/Workspace/Doris/Instance/Steps/YAMLStepForm.tsx @@ -0,0 +1,41 @@ +import React, {useEffect, useRef} from "react"; +import {ProCard} from "@ant-design/pro-components"; +import Editor, {Monaco, useMonaco} from "@monaco-editor/react"; +import {connect} from "umi"; + +const DorisInstanceYAML: React.FC = (props: any) => { + const editorRef = useRef(null); + const monaco = useMonaco(); + + useEffect(() => { + monaco?.languages.typescript.javascriptDefaults.setEagerModelSync(true); + }, [monaco]); + + const handleEditorDidMount = (editor, monaco: Monaco) => { + editorRef.current = editor; + } + + return ( + + + + + ); +} + +const mapModelToProps = ({dorisInstanceSteps}: any) => ({dorisInstanceSteps}) +export default connect(mapModelToProps)(DorisInstanceYAML); diff --git a/scaleph-ui-react/src/pages/Project/Workspace/Doris/Instance/Steps/index.tsx b/scaleph-ui-react/src/pages/Project/Workspace/Doris/Instance/Steps/index.tsx index 6fead5b83..29f8eb320 100644 --- a/scaleph-ui-react/src/pages/Project/Workspace/Doris/Instance/Steps/index.tsx +++ b/scaleph-ui-react/src/pages/Project/Workspace/Doris/Instance/Steps/index.tsx @@ -5,6 +5,9 @@ import {WORKSPACE_CONF} from "@/constant"; import {WsDorisInstance, WsDorisTemplate} from "@/services/project/typings"; import {WsDorisTemplateService} from "@/services/project/WsDorisTemplateService"; import DorisInstanceBase from "@/pages/Project/Workspace/Doris/Instance/Steps/BaseStepForm"; +import {WsDorisInstanceService} from "@/services/project/WsDorisInstanceService"; +import DorisInstanceComponent from "@/pages/Project/Workspace/Doris/Instance/Steps/ComponentStepForm"; +import DorisInstanceYAML from "@/pages/Project/Workspace/Doris/Instance/Steps/YAMLStepForm"; const DorisInstanceSteps: React.FC = (props: any) => { const intl = useIntl(); @@ -12,34 +15,44 @@ const DorisInstanceSteps: React.FC = (props: any) => { const localProjectId = localStorage.getItem(WORKSPACE_CONF.projectId); const onBaseStepFinish = (values: Record) => { - const template: WsDorisInstance = { - projectId: localProjectId, - name: values.name, - namespace: values.namespace, - remark: values.remark, - } - editDorisTemplate(template) - return Promise.resolve(true) + return WsDorisInstanceService.fromTemplate(values.templateId).then(response => { + const instance: WsDorisInstance = response.data + instance.projectId = localProjectId + instance.name = values.name + instance.clusterCredentialId = values.clusterCredentialId + instance.namespace = values.namespace + instance.remark = values.remark + editDorisInstance(instance) + return true + }) } const onComponentStepFinish = (values: Record) => { try { - const template: WsDorisTemplate = WsDorisTemplateService.formatData(props.dorisTemplateSteps.template, values) - editDorisTemplate(template) + const template: WsDorisTemplate = WsDorisTemplateService.formatData({}, values) + const instance: WsDorisInstance = { + ...props.dorisInstanceSteps.instance, + admin: template.admin, + feSpec: template.feSpec, + beSpec: template.beSpec, + cnSpec: template.cnSpec, + brokerSpec: template.brokerSpec, + } + editDorisInstance(instance) } catch (unused) { } return Promise.resolve(true) } - const editDorisTemplate = (template: WsDorisTemplate) => { + const editDorisInstance = (template: WsDorisInstance) => { props.dispatch({ - type: 'dorisInstanceSteps/editTemplate', + type: 'dorisInstanceSteps/editInstance', payload: template }) } const onAllFinish = (values: Record) => { - return WsDorisTemplateService.add(props.dorisTemplateSteps.template).then((response) => { + return WsDorisInstanceService.add(props.dorisInstanceSteps.instance).then((response) => { if (response.success) { history.back() } @@ -64,6 +77,19 @@ const DorisInstanceSteps: React.FC = (props: any) => { onFinish={onBaseStepFinish}> + + + + + + diff --git a/scaleph-ui-react/src/pages/Project/Workspace/Doris/Instance/index.tsx b/scaleph-ui-react/src/pages/Project/Workspace/Doris/Instance/index.tsx index 667c9b776..c3f39f8e7 100644 --- a/scaleph-ui-react/src/pages/Project/Workspace/Doris/Instance/index.tsx +++ b/scaleph-ui-react/src/pages/Project/Workspace/Doris/Instance/index.tsx @@ -1,11 +1,12 @@ import {history, useAccess, useIntl} from "umi"; import React, {useRef, useState} from "react"; -import {Button, message, Modal, Space, Tooltip} from "antd"; +import {Button, message, Modal, Space, Tag, Tooltip} from "antd"; import {DeleteOutlined, EditOutlined, NodeIndexOutlined} from "@ant-design/icons"; -import {ActionType, ProColumns, ProFormInstance, ProTable} from "@ant-design/pro-components"; -import {PRIVILEGE_CODE, WORKSPACE_CONF} from "@/constant"; +import {ActionType, ProColumns, ProFormInstance, ProFormSelect, ProTable} from "@ant-design/pro-components"; +import {DICT_TYPE, PRIVILEGE_CODE, WORKSPACE_CONF} from "@/constant"; import {WsDorisInstance, WsDorisTemplate} from "@/services/project/typings"; import {WsDorisInstanceService} from "@/services/project/WsDorisInstanceService"; +import {DictDataService} from "@/services/admin/dictData.service"; const DorisInstanceWeb: React.FC = () => { const intl = useIntl(); @@ -21,14 +22,22 @@ const DorisInstanceWeb: React.FC = () => { const tableColumns: ProColumns[] = [ { - title: intl.formatMessage({id: 'pages.project.doris.template.name'}), + title: intl.formatMessage({id: 'pages.project.doris.instance.name'}), dataIndex: 'name' }, { - title: intl.formatMessage({id: 'pages.project.doris.template.namespace'}), + title: intl.formatMessage({id: 'pages.project.doris.instance.namespace'}), dataIndex: 'namespace', hideInSearch: true, }, + + { + title: intl.formatMessage({id: 'pages.project.doris.instance.deployed'}), + dataIndex: 'deployed', + render: (dom, entity) => { + return ({entity.deployed?.label}) + } + }, { title: intl.formatMessage({id: 'app.common.data.remark'}), dataIndex: 'remark', @@ -74,7 +83,7 @@ const DorisInstanceWeb: React.FC = () => { type="link" icon={} onClick={() => { - history.push("/workspace/doris/template/detail", record) + // history.push("/workspace/doris/template/detail", record) }} /> diff --git a/scaleph-ui-react/src/services/project/WsDorisInstanceService.ts b/scaleph-ui-react/src/services/project/WsDorisInstanceService.ts index be991d2a5..f8a7c90c1 100644 --- a/scaleph-ui-react/src/services/project/WsDorisInstanceService.ts +++ b/scaleph-ui-react/src/services/project/WsDorisInstanceService.ts @@ -20,6 +20,13 @@ export const WsDorisInstanceService = { }); }, + fromTemplate: async (templateId: number) => { + return request>(`${WsDorisInstanceService.url}/fromTemplate`, { + method: 'GET', + params: {templateId: templateId}, + }); + }, + asYaml: async (param: WsDorisInstance) => { return request>(`${WsDorisInstanceService.url}/asYaml`, { method: 'POST',