Skip to content

Commit c5b7870

Browse files
authored
feat(console): add params for import cluster by kubeconfig (#1647)
* feat(console): add more params * feat(console): add new params for import cluster * feat(console): add as-groups params * fix(console): fix handleChange bug
1 parent 8da43f2 commit c5b7870

File tree

9 files changed

+305
-16
lines changed

9 files changed

+305
-16
lines changed

Diff for: web/console/package-lock.json

+6
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: web/console/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,7 @@
8484
"@types/react-dom": "^16.9.9",
8585
"@types/redux-logger": "^3.0.7",
8686
"@types/redux-thunk": "^2.1.0",
87+
"@types/uuid": "^8.3.1",
8788
"@types/validator": "^13.1.3",
8889
"@typescript-eslint/eslint-plugin": "^4.7.0",
8990
"@typescript-eslint/parser": "^4.7.0",

Diff for: web/console/src/modules/cluster/WebAPI/ClusterAPI.ts

+1-4
Original file line numberDiff line numberDiff line change
@@ -338,10 +338,7 @@ export async function createImportClsutter(resource: CreateResource[], regionId:
338338
metadata: {
339339
generateName: 'cc'
340340
},
341-
caCert: clusterData.status.credential.caCert,
342-
token: clusterData.status.credential.token ? clusterData.status.credential.token : undefined,
343-
clientKey: clusterData.status.credential.clientKey || undefined,
344-
clientCert: clusterData.status.credential.clientCert || undefined
341+
...clusterData.status.credential
345342
};
346343
// 构建参数
347344
const clustercredentialParams: RequestParams = {

Diff for: web/console/src/modules/cluster/actions/validateClusterCreationAction.ts

+35-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,8 @@ export const validateClusterCreationAction = {
5656
let status = 0,
5757
message = '',
5858
numberReg = /^\d+$/,
59-
ipReg = /^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/,
59+
ipReg =
60+
/^(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])\.(\d{1,2}|1\d\d|2[0-4]\d|25[0-5])$/,
6061
hostReg = /^([\w-]+\.)+[\w-]+(\/[\w- .\/?%&=]*)?$/;
6162
//验证集群名称
6263

@@ -147,6 +148,39 @@ export const validateClusterCreationAction = {
147148
dispatch(clusterCreationAction.updateClusterCreationState({ v_token: result }));
148149
};
149150
},
151+
152+
_validateAsUserExtra(asUserExtra: { id: string; key: string; value: string }[]) {
153+
if (!asUserExtra) {
154+
return {
155+
status: 1,
156+
message: ''
157+
};
158+
}
159+
160+
const hasEmpty = asUserExtra.some(({ key, value }) => key === '' || value === '');
161+
162+
if (hasEmpty) {
163+
return {
164+
status: 2,
165+
message: 'key和value不能为空'
166+
};
167+
}
168+
169+
return {
170+
status: 1,
171+
message: ''
172+
};
173+
},
174+
175+
validateAsUserExtra() {
176+
return async (dispatch: Redux.Dispatch, getState: GetState) => {
177+
const { asUserExtra } = getState().clusterCreationState;
178+
179+
const result = await validateClusterCreationAction._validateAsUserExtra(asUserExtra);
180+
181+
dispatch(clusterCreationAction.updateClusterCreationState({ v_token: result }));
182+
};
183+
},
150184
/** 校验clusterconnection的正确性 */
151185
_validateclusterCreationState(clusterCreationState: ClusterCreationState) {
152186
const { name, apiServer, certFile, token } = clusterCreationState;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,144 @@
1+
/*
2+
* Tencent is pleased to support the open source community by making TKEStack
3+
* available.
4+
*
5+
* Copyright (C) 2012-2021 Tencent. All Rights Reserved.
6+
*
7+
* Licensed under the Apache License, Version 2.0 (the "License"); you may not use
8+
* this file except in compliance with the License. You may obtain a copy of the
9+
* License at
10+
*
11+
* https://opensource.org/licenses/Apache-2.0
12+
*
13+
* Unless required by applicable law or agreed to in writing, software
14+
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15+
* WARRANTIES OF ANY KIND, either express or implied. See the License for the
16+
* specific language governing permissions and limitations under the License.
17+
*/
18+
19+
import React, { useMemo } from 'react';
20+
import { Table, Text, Bubble, Icon, Form, Input, TableColumn, Button } from '@tencent/tea-component';
21+
import { t } from '@tencent/tea-app/lib/i18n';
22+
import { v4 as uuidv4 } from 'uuid';
23+
import { Validation } from '@src/modules/common';
24+
25+
interface IItem {
26+
id: string;
27+
key: string;
28+
value: string;
29+
}
30+
31+
interface IAsUserExtraInputProps {
32+
data?: IItem[];
33+
34+
onChange(data: IItem[]): void;
35+
}
36+
37+
interface IValidator {
38+
id: string;
39+
40+
key: {
41+
status: 'error' | 'success';
42+
message?: string;
43+
};
44+
45+
value: {
46+
status: 'error' | 'success';
47+
message?: string;
48+
};
49+
}
50+
51+
export const AsUserExtraInput = ({ data = [], onChange }: IAsUserExtraInputProps) => {
52+
function handleChange(id: string, params: { key?: string; value?: string }) {
53+
const newData = data.map(item => (item.id === id ? { ...item, ...params } : { ...item }));
54+
55+
onChange(newData);
56+
}
57+
58+
function handleDel(id: string) {
59+
const newData = data.filter(item => item.id !== id);
60+
61+
onChange(newData);
62+
}
63+
64+
function handleAdd() {
65+
const newData = [
66+
...data,
67+
{
68+
id: uuidv4(),
69+
key: '',
70+
value: ''
71+
}
72+
];
73+
74+
onChange(newData);
75+
}
76+
77+
const validators = useMemo(() => {
78+
return data.map<IValidator>(item => ({
79+
id: item.id,
80+
key: item.key ? { status: 'success' } : { status: 'error', message: t('变量名不能为空') },
81+
value: item.value ? { status: 'success' } : { status: 'error', message: t('变量值不能为空') }
82+
}));
83+
}, [data]);
84+
85+
function getValidatorById(id: string, propName: 'key' | 'value') {
86+
return validators?.find(item => item.id === id)?.[propName] ?? {};
87+
}
88+
89+
const columns: TableColumn[] = [
90+
{
91+
key: 'key',
92+
width: '35%',
93+
header: (
94+
<>
95+
<Text>{t('变量名')}</Text>
96+
<Bubble placement="top-start" content={t('变量名可以重复')}>
97+
<Icon type="info" />
98+
</Bubble>
99+
</>
100+
),
101+
102+
render: ({ key, id }) => (
103+
<Form.Item label="" {...getValidatorById(id, 'key')}>
104+
<Input value={key} onChange={key => handleChange(id, { key })} />
105+
</Form.Item>
106+
)
107+
},
108+
109+
{
110+
key: 'equal',
111+
width: '7%',
112+
header: '',
113+
render: () => <Text>=</Text>
114+
},
115+
116+
{
117+
key: 'value',
118+
width: '48%',
119+
header: t('变量值'),
120+
render: ({ value, id }) => (
121+
<Form.Item label="" {...getValidatorById(id, 'value')}>
122+
<Input value={value} onChange={value => handleChange(id, { value })} />
123+
</Form.Item>
124+
)
125+
},
126+
127+
{
128+
key: 'action',
129+
width: '10%',
130+
header: '',
131+
render: ({ id }) => <Icon type="close" onClick={() => handleDel(id)} />
132+
}
133+
];
134+
135+
return (
136+
<>
137+
<Table columns={columns} records={data} />
138+
139+
<Button type="link" onClick={handleAdd} style={{ marginTop: 15 }}>
140+
{t('手动增加')}
141+
</Button>
142+
</>
143+
);
144+
};

Diff for: web/console/src/modules/cluster/components/clusterManage/CreateClusterPanel.tsx

+63-8
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ import { router } from '../../router';
3232
import { RootProps } from '../ClusterApp';
3333
import { ClusterSubpageHeaderPanel } from './ClusterSubpageHeaderPanel';
3434
import { KubeconfigFileParse } from './KubeconfigFileParse';
35+
import { AsUserExtraInput } from './AsUserExtraInput';
3536

3637
const mapDispatchToProps = dispatch =>
3738
Object.assign({}, bindActionCreators({ actions: allActions }, dispatch), { dispatch });
@@ -57,7 +58,11 @@ export class CreateClusterPanel extends React.Component<RootProps, {}> {
5758
name,
5859
token,
5960
clientCert,
60-
clientKey
61+
clientKey,
62+
username,
63+
as,
64+
asGroups,
65+
asUserExtra
6166
} = clusterCreationState;
6267
const workflow = createClusterFlow;
6368
const action = actions.workflow.createCluster;
@@ -74,6 +79,18 @@ export class CreateClusterPanel extends React.Component<RootProps, {}> {
7479
router.navigate({}, { rid: route.queries['rid'] });
7580
};
7681

82+
function transAsUserExtra(asUserExtra: { key: string; value: string }[]) {
83+
if (!asUserExtra || asUserExtra.length <= 0) return undefined;
84+
85+
return asUserExtra.reduce(
86+
(all, { key, value }) => ({
87+
...all,
88+
[key]: all?.[key] ? `${all?.[key]},${value}` : value
89+
}),
90+
{}
91+
);
92+
}
93+
7794
const perform = () => {
7895
actions.validate.clusterCreation.validateclusterCreationState();
7996
if (validateClusterCreationAction._validateclusterCreationState(clusterCreationState)) {
@@ -122,13 +139,15 @@ export class CreateClusterPanel extends React.Component<RootProps, {}> {
122139
credential: {
123140
caCert: certIsBase64 ? clusterCreationState.certFile : window.btoa(clusterCreationState.certFile),
124141
clientCert: clusterCreationState.clientCert || undefined,
125-
clientKey: clusterCreationState.clientKey || undefined
142+
clientKey: clusterCreationState.clientKey || undefined,
143+
token: clusterCreationState.token || undefined,
144+
username: clusterCreationState.username || undefined,
145+
as: clusterCreationState.as || undefined,
146+
'as-groups': clusterCreationState.asGroups ? clusterCreationState.asGroups.split(',') : undefined,
147+
'as-user-extra': transAsUserExtra(asUserExtra)
126148
}
127149
}
128150
};
129-
if (clusterCreationState.token) {
130-
data.status.credential['token'] = clusterCreationState.token;
131-
}
132151

133152
const createClusterData: CreateResource[] = [
134153
{
@@ -142,9 +161,8 @@ export class CreateClusterPanel extends React.Component<RootProps, {}> {
142161
action.perform();
143162
}
144163
};
145-
function parseKubeconfigSuccess({ apiServer, certFile, token, clientCert, clientKey }) {
146-
console.log(apiServer, certFile, token);
147-
actions.clusterCreation.updateClusterCreationState({ apiServer, certFile, token, clientCert, clientKey });
164+
function parseKubeconfigSuccess(params) {
165+
actions.clusterCreation.updateClusterCreationState({ ...params });
148166
}
149167

150168
const failed = workflow.operationState === OperationState.Done && !isSuccessWorkflow(workflow);
@@ -223,6 +241,43 @@ export class CreateClusterPanel extends React.Component<RootProps, {}> {
223241
/>
224242
</FormPanel.Item>
225243

244+
<FormPanel.Item label="username">
245+
<InputField
246+
type="textarea"
247+
value={username}
248+
placeholder={t('请输入username')}
249+
tipMode="popup"
250+
onChange={value => actions.clusterCreation.updateClusterCreationState({ username: value })}
251+
/>
252+
</FormPanel.Item>
253+
254+
<FormPanel.Item label="as">
255+
<InputField
256+
type="textarea"
257+
value={as}
258+
placeholder={t('请输入as')}
259+
tipMode="popup"
260+
onChange={value => actions.clusterCreation.updateClusterCreationState({ as: value })}
261+
/>
262+
</FormPanel.Item>
263+
264+
<FormPanel.Item label="as-groups">
265+
<InputField
266+
type="textarea"
267+
value={asGroups}
268+
placeholder={t('请输入as-groups,多个group以逗号分隔')}
269+
tipMode="popup"
270+
onChange={value => actions.clusterCreation.updateClusterCreationState({ asGroups: value })}
271+
/>
272+
</FormPanel.Item>
273+
274+
<FormPanel.Item label="as-user-extra">
275+
<AsUserExtraInput
276+
data={asUserExtra}
277+
onChange={value => actions.clusterCreation.updateClusterCreationState({ asUserExtra: value })}
278+
/>
279+
</FormPanel.Item>
280+
226281
<FormPanel.Footer>
227282
<React.Fragment>
228283
<Button

0 commit comments

Comments
 (0)