Skip to content

Commit

Permalink
feat:template develop dryrun deploy (#3778)
Browse files Browse the repository at this point in the history
Signed-off-by: jingyang <3161362058@qq.com>
  • Loading branch information
zjy365 committed Aug 29, 2023
1 parent f3a24d6 commit 5f6c39b
Show file tree
Hide file tree
Showing 9 changed files with 112 additions and 71 deletions.
3 changes: 2 additions & 1 deletion frontend/providers/template/public/locales/en/common.json
Expand Up @@ -154,6 +154,7 @@
"Preview": "Preview",
"Configure Form": "Configure Form",
"YAML File": "YAML File",
"Template Development": "Template Development"
"Template Development": "Template Development",
"Dryrun Deploy": "Dryrun Deploy"
}
}
3 changes: 2 additions & 1 deletion frontend/providers/template/public/locales/zh/common.json
Expand Up @@ -160,6 +160,7 @@
"Preview": "预览",
"Configure Form": "配置表单",
"YAML File": "YAML 文件",
"Template Development": "模板开发"
"Template Development": "模板开发",
"Dryrun Deploy": "试运行部署"
}
}
4 changes: 3 additions & 1 deletion frontend/providers/template/src/api/app.ts
@@ -1,4 +1,6 @@
import { POST } from '@/services/request';

export const postDeployApp = (yamlList: string[]) => POST('/api/applyApp', { yamlList });
export const postDeployApp = (yamlList: string[], type: 'create' | 'replace' | 'dryrun') =>
POST('/api/applyApp', { yamlList, type });

export const getTemplate = (templateName: string) => POST('/api/getTemplate', { templateName });
8 changes: 6 additions & 2 deletions frontend/providers/template/src/pages/api/applyApp.ts
Expand Up @@ -5,7 +5,11 @@ import { ApiResp } from '@/services/kubernet';
import type { NextApiRequest, NextApiResponse } from 'next';

export default async function handler(req: NextApiRequest, res: NextApiResponse<ApiResp>) {
const { yamlList }: { yamlList: string[] } = req.body;
const { yamlList, type = 'create' } = req.body as {
yamlList: string[];
type: 'create' | 'replace' | 'dryrun';
};

if (!yamlList || yamlList.length < 2) {
jsonRes(res, {
code: 500,
Expand All @@ -18,7 +22,7 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
kubeconfig: await authSession(req.headers)
});

const applyRes = await applyYamlList(yamlList, 'create');
const applyRes = await applyYamlList(yamlList, type);

jsonRes(res, { data: applyRes.map((item) => item.kind) });
} catch (err: any) {
Expand Down
11 changes: 4 additions & 7 deletions frontend/providers/template/src/pages/deploy/index.tsx
Expand Up @@ -143,7 +143,7 @@ const EditApp = ({ appName, tabType }: { appName?: string; tabType: string }) =>
return JSYAML.dump(_item);
});

const result = await postDeployApp(yamls);
const result = await postDeployApp(yamls, 'create');

toast({
title: t(applySuccess),
Expand Down Expand Up @@ -246,8 +246,7 @@ const EditApp = ({ appName, tabType }: { appName?: string; tabType: string }) =>
justifyContent={'start'}
alignItems={'center'}
backgroundColor={'rgba(255, 255, 255)'}
backdropBlur={'100px'}
>
backdropBlur={'100px'}>
<Box cursor={'pointer'} onClick={() => router.push('/')}>
<MyIcon ml={'46px'} name="arrowLeft" color={'#24282C'} w={'16px'} h={'16px'}></MyIcon>
</Box>
Expand All @@ -256,8 +255,7 @@ const EditApp = ({ appName, tabType }: { appName?: string; tabType: string }) =>
fontWeight={500}
fontSize={16}
textDecoration={'none'}
color={'#7B838B'}
>
color={'#7B838B'}>
<BreadcrumbItem textDecoration={'none'}>
<BreadcrumbLink _hover={{ color: '#219BF4', textDecoration: 'none' }} href="/">
{t('Template List')}
Expand All @@ -276,8 +274,7 @@ const EditApp = ({ appName, tabType }: { appName?: string; tabType: string }) =>
flexDirection={'column'}
width={'100%'}
flexGrow={1}
backgroundColor={'rgba(255, 255, 255, 0.90)'}
>
backgroundColor={'rgba(255, 255, 255, 0.90)'}>
<Header
templateDetail={templateDetail}
appName={''}
Expand Down
@@ -1,18 +1,9 @@
import MyIcon from '@/components/Icon';
import {
Flex,
Button,
Text,
Box,
BreadcrumbItem,
BreadcrumbLink,
Breadcrumb
} from '@chakra-ui/react';
import { t } from 'i18next';
import { useRouter } from 'next/router';
import { Box, Breadcrumb, BreadcrumbItem, BreadcrumbLink, Button, Flex } from '@chakra-ui/react';
import { useTranslation } from 'next-i18next';
import { useRouter } from 'next/router';

const BreadCrumbHeader = () => {
const BreadCrumbHeader = ({ applyCb }: { applyCb: () => void }) => {
const router = useRouter();
const { t } = useTranslation();

Expand All @@ -23,8 +14,7 @@ const BreadCrumbHeader = () => {
justifyContent={'start'}
alignItems={'center'}
backgroundColor={'rgba(255, 255, 255)'}
backdropBlur={'100px'}
>
backdropBlur={'100px'}>
<Box cursor={'pointer'} onClick={() => router.push('/')}>
<MyIcon name="arrowLeft" color={'#24282C'} w={'16px'} h={'16px'}></MyIcon>
</Box>
Expand All @@ -33,8 +23,7 @@ const BreadCrumbHeader = () => {
fontWeight={500}
fontSize={16}
textDecoration={'none'}
color={'#7B838B'}
>
color={'#7B838B'}>
<BreadcrumbItem textDecoration={'none'}>
<BreadcrumbLink _hover={{ color: '#219BF4', textDecoration: 'none' }} href="/">
{t('Template List')}
Expand All @@ -46,6 +35,9 @@ const BreadCrumbHeader = () => {
</BreadcrumbLink>
</BreadcrumbItem>
</Breadcrumb>
<Button ml="auto" px={4} minW={'120px'} h={'34px'} variant={'primary'} onClick={applyCb}>
{t('develop.Dryrun Deploy')}
</Button>
</Flex>
);
};
Expand Down
Expand Up @@ -37,8 +37,7 @@ const Form = ({
w="200px"
className="template-dynamic-label"
color={'#333'}
userSelect={'none'}
>
userSelect={'none'}>
{item?.label}
{item?.required && (
<Text ml="2px" color={'#E53E3E'}>
Expand All @@ -51,7 +50,6 @@ const Form = ({
maxW={'500px'}
ml={'20px'}
defaultValue={item?.default}
autoFocus={true}
placeholder={item?.description}
{...register(item?.key, {
required: item?.required
Expand All @@ -68,16 +66,14 @@ const Form = ({
alignItems="center"
h={'100%'}
w={'100%'}
flexDirection="column"
>
flexDirection="column">
<Flex
border={'1px dashed #9CA2A8'}
borderRadius="50%"
w={'48px'}
h={'48px'}
justifyContent="center"
alignItems={'center'}
>
alignItems={'center'}>
<MyIcon color={'#7B838B'} name="empty"></MyIcon>
</Flex>
<Text mt={'12px'} fontSize={14} color={'#5A646E'}>
Expand Down
111 changes: 78 additions & 33 deletions frontend/providers/template/src/pages/develop/index.tsx
@@ -1,42 +1,42 @@
import { Flex, Box, Button, Text, Textarea } from '@chakra-ui/react';
import { useTranslation } from 'next-i18next';
import MyIcon from '@/components/Icon';
import { useRouter } from 'next/router';
import { ChangeEvent, useMemo, useState } from 'react';
import { serviceSideProps } from '@/utils/i18n';
import Form from './components/Form';
import { useQuery } from '@tanstack/react-query';
import { GET } from '@/services/request';
import JsYaml from 'js-yaml';
import { editModeMap } from '@/constants/editApp';
import { useLoading } from '@/hooks/useLoading';
import { useToast } from '@/hooks/useToast';
import { debounce, mapValues } from 'lodash';
import { YamlSourceType, TemplateType, YamlType } from '@/types/app';
import { GET } from '@/services/request';
import { YamlItemType } from '@/types';
import { TemplateType, YamlSourceType } from '@/types/app';
import { serviceSideProps } from '@/utils/i18n';
import {
developGenerateYamlList,
getTemplateDataSource,
parseTemplateString
} from '@/utils/json-yaml';
import YamlList from './components/YamlList';
import { useForm } from 'react-hook-form';
import { getTemplateDefaultValues } from '@/utils/template';
import { YamlItemType } from '@/types';
import Header from './components/Header';
import BreadCrumbHeader from './components/BreadCrumbHeader';
import CodeMirror from '@uiw/react-codemirror';
import { Box, Flex, Text } from '@chakra-ui/react';
import { StreamLanguage } from '@codemirror/language';
import { yaml } from '@codemirror/legacy-modes/mode/yaml';
import { useGlobalStore } from '@/store/global';
import { useQuery } from '@tanstack/react-query';
import CodeMirror from '@uiw/react-codemirror';
import JsYaml from 'js-yaml';
import { debounce, has, isObject, mapValues } from 'lodash';
import { useTranslation } from 'next-i18next';
import { useMemo, useState } from 'react';
import { useForm } from 'react-hook-form';
import ErrorModal from '../deploy/components/ErrorModal';
import BreadCrumbHeader from './components/BreadCrumbHeader';
import Form from './components/Form';
import YamlList from './components/YamlList';
import { postDeployApp } from '@/api/app';

const Develop = () => {
export default function Develop() {
const { t } = useTranslation();
const router = useRouter();
const [yamlValue, setYamlValue] = useState('');
const { toast } = useToast();
const [yamlSource, setYamlSource] = useState<YamlSourceType>();
const [yamlList, setYamlList] = useState<YamlItemType[]>([]);
const { screenWidth } = useGlobalStore();
const isLargeScreen = useMemo(() => screenWidth > 1024, [screenWidth]);
const [showSlider, setShowSlider] = useState(false);
const { Loading, setIsLoading } = useLoading();
const [errorMessage, setErrorMessage] = useState('');
const { title, applyBtnText, applyMessage, applySuccess, applyError } = editModeMap(false);

const detailName = useMemo(
() => yamlSource?.source?.defaults?.app_name?.value || '',
Expand Down Expand Up @@ -116,16 +116,61 @@ const Develop = () => {
}
}, 1000);

const submitSuccess = async () => {
setIsLoading(true);
try {
const result: string[] = await postDeployApp(
yamlList.map((item) => item.value),
'dryrun'
);
toast({
title: t(applySuccess),
status: 'success',
description: result?.toString()
});
} catch (error) {
console.log(error, 'sadasdas');

setErrorMessage(JSON.stringify(error));
}
setIsLoading(false);
};

const submitError = () => {
formHook.getValues();
function deepSearch(obj: any): string {
if (has(obj, 'message')) {
return obj.message;
}
for (let key in obj) {
if (isObject(obj[key])) {
let message = deepSearch(obj[key]);
if (message) {
return message;
}
}
}
return t('Submit Error');
}

toast({
title: deepSearch(formHook.formState.errors),
status: 'error',
position: 'top',
duration: 3000,
isClosable: true
});
};

return (
<Flex flexDirection={'column'} p={'0px 34px 20px 34px '} h="100vh" maxW={'1440px'} mx="auto">
<BreadCrumbHeader />
<BreadCrumbHeader applyCb={() => formHook.handleSubmit(submitSuccess, submitError)()} />
<Flex
border={'1px solid #DEE0E2'}
borderRadius={'8px'}
overflowY={'hidden'}
overflowX={'scroll'}
flex={1}
>
flex={1}>
{/* left */}
<Flex flexDirection={'column'} flex={'0 1 500px'} borderRight={'1px solid #EFF0F1'}>
<Flex
Expand All @@ -135,8 +180,7 @@ const Develop = () => {
alignItems={'center'}
backgroundColor={'#F8FAFB'}
px="36px"
borderRadius={'8px 8px 0px 0px '}
>
borderRadius={'8px 8px 0px 0px '}>
<MyIcon name="dev" color={'#24282C'} w={'24px'} h={'24px'}></MyIcon>
<Text fontWeight={'500'} fontSize={'16px'} color={'#24282C'} ml="8px">
{t('develop.Development')}
Expand Down Expand Up @@ -164,8 +208,7 @@ const Develop = () => {
alignItems={'center'}
backgroundColor={'#F8FAFB'}
pl="42px"
borderRadius={'8px 8px 0px 0px '}
>
borderRadius={'8px 8px 0px 0px '}>
<MyIcon name="eyeShow" color={'#24282C'} w={'24px'} h={'24px'}></MyIcon>
<Text fontWeight={'500'} fontSize={'16px'} color={'#24282C'} ml="8px">
{t('develop.Preview')}
Expand All @@ -187,9 +230,13 @@ const Develop = () => {
</Flex>
</Flex>
</Flex>
<Loading />
{!!errorMessage && (
<ErrorModal title={applyError} content={errorMessage} onClose={() => setErrorMessage('')} />
)}
</Flex>
);
};
}

export async function getServerSideProps(content: any) {
return {
Expand All @@ -198,5 +245,3 @@ export async function getServerSideProps(content: any) {
}
};
}

export default Develop;
Expand Up @@ -53,7 +53,8 @@ export type CRDMeta = {

export async function CreateYaml(
kc: k8s.KubeConfig,
specs: k8s.KubernetesObject[]
specs: k8s.KubernetesObject[],
dryRun?: 'All'
): Promise<k8s.KubernetesObject[]> {
const client = k8s.KubernetesObjectApi.makeApiClient(kc);
const validSpecs = specs.filter((s) => s && s.kind && s.metadata);
Expand All @@ -68,7 +69,7 @@ export async function CreateYaml(
JSON.stringify(spec);

console.log('create yaml: ', spec.kind);
const response = await client.create(spec);
const response = await client.create(spec, undefined, dryRun ? dryRun : undefined);
created.push(response.body);
}
} catch (error: any) {
Expand Down Expand Up @@ -151,7 +152,7 @@ export async function getK8s({ kubeconfig }: { kubeconfig: string }) {

const namespace = GetUserDefaultNameSpace(kube_user.name);

const applyYamlList = async (yamlList: string[], type: 'create' | 'replace') => {
const applyYamlList = async (yamlList: string[], type: 'create' | 'replace' | 'dryrun') => {
// insert namespace
const formatYaml: k8s.KubernetesObject[] = yamlList
.map((item) => yaml.loadAll(item))
Expand All @@ -167,6 +168,8 @@ export async function getK8s({ kubeconfig }: { kubeconfig: string }) {
return CreateYaml(kc, formatYaml);
} else if (type === 'replace') {
return replaceYaml(kc, formatYaml);
} else if (type === 'dryrun') {
return CreateYaml(kc, formatYaml, 'All');
}
return CreateYaml(kc, formatYaml);
};
Expand Down

0 comments on commit 5f6c39b

Please sign in to comment.