diff --git a/router/config_router.go b/router/config_router.go index 8bd3ae4f..0d3d9ecc 100644 --- a/router/config_router.go +++ b/router/config_router.go @@ -52,6 +52,8 @@ func ConfigRouter(r *gin.Engine, config *bootstrap.Config) { configV1.POST("configfiles/releases/delete", handlers.ReverseProxyForServer(&config.PolarisServer, config)) // 查询配置发布的版本列表 configV1.GET("configfiles/release/versions", handlers.ReverseProxyForServer(&config.PolarisServer, config)) + // 取消配置灰度发布 + configV1.POST("configfiles/releases/stopbeta", handlers.ReverseProxyForServer(&config.PolarisServer, config)) // 配置文件发布历史 configV1.GET("configfiles/releasehistory", handlers.ReverseProxyForServer(&config.PolarisServer, config)) //配置文件模板 diff --git a/web/src/polaris/configuration/fileGroup/detail/file/Page.tsx b/web/src/polaris/configuration/fileGroup/detail/file/Page.tsx index bcaeb9de..9f463f01 100644 --- a/web/src/polaris/configuration/fileGroup/detail/file/Page.tsx +++ b/web/src/polaris/configuration/fileGroup/detail/file/Page.tsx @@ -34,6 +34,7 @@ import { Link } from 'react-router-dom' import { FileFormat } from './operation/Create' import router from '@src/polaris/common/util/router' import { TAB } from '../Page' +import { ConfigFile } from '../../types' export const NoSearchResultKey = '__NO_SEARCH_RESULT__' const getHandlers = memorize(({ creators }: Duck, dispatch) => ({ @@ -49,6 +50,8 @@ const getHandlers = memorize(({ creators }: Duck, dispatch) => ({ fetchData: path => dispatch(creators.fetchData(path)), setEditContent: v => dispatch(creators.setEditContent(v)), releaseCurrentFile: () => dispatch(creators.releaseCurrentFile()), + betaReleaseCurrentFile: () => dispatch(creators.betaReleaseCurrentFile()), + stopBetaReleaseCurrentFile: () => dispatch(creators.stopBetaReleaseCurrentFile()), showReleaseHistory: v => dispatch(creators.showReleaseHistory(v)), select: v => dispatch(creators.select(v)), cancel: () => dispatch(creators.cancel()), @@ -221,12 +224,12 @@ export default function Page(props: DuckCmpProps) { fullExpandable height={900} style={{ width: '450px', maxWidth: '450px' }} - // onSelect={v => { - // handlers.select(v) - // }} - // selectable - // selectedIds={selection} - // selectValueMode={'onlyLeaf'} + // onSelect={v => { + // handlers.select(v) + // }} + // selectable + // selectedIds={selection} + // selectValueMode={'onlyLeaf'} > {renderTree(props, fileTree, '', '')} @@ -277,8 +280,8 @@ export default function Page(props: DuckCmpProps) { content={ currentNode.tags.length > 3 ? currentNode.tags?.map(item => ( - {`${item.key}:${item.value}`} - )) + {`${item.key}:${item.value}`} + )) : null } > @@ -325,12 +328,30 @@ export default function Page(props: DuckCmpProps) { <> - + {isBetaingRelease(currentNode) ? + ( + + ) : ( + + ) + } {editing ? ( <> , + ...(isFirstStep ? [] : [submitCreator()]), + cancelCreator(), + ] + }} + defaultCancel={false} + defaultSubmit={false} + > + + {stepInfo[step]} + + ) +}) diff --git a/web/src/polaris/configuration/fileGroup/detail/file/operation/BetaReleaseConfigDuck.ts b/web/src/polaris/configuration/fileGroup/detail/file/operation/BetaReleaseConfigDuck.ts new file mode 100644 index 00000000..5b5d8609 --- /dev/null +++ b/web/src/polaris/configuration/fileGroup/detail/file/operation/BetaReleaseConfigDuck.ts @@ -0,0 +1,95 @@ +import { select, put } from 'redux-saga/effects' +import FormDuck from '@src/polaris/common/ducks/Form' +import Base from '@src/polaris/common/ducks/DialogPure' +import Create from './BetaReleaseConfig' +import { showDialog } from '@src/polaris/common//helpers/showDialog' +import { ClientLabel, ConfigFileRelease } from '../../../types' +import { releaseConfigFile } from '../../../model' + +interface Data { + lastRelease: ConfigFileRelease + namespace: string + group: string + content: string + format: string + name: string + clientLabels?: ClientLabel[] +} + +export default class BetaReleaseConfigDuck extends Base { + Data: Data + + get quickDucks() { + return { + ...super.quickDucks, + form: BetaCreateFormDuck, + } + } + *onShow() { + const duck = this + const { selector } = duck + const { data } = yield select(selector) + yield* this.initData(data) + } + + *initData(data) { + const { ducks } = this + yield put(ducks.form.creators.setAllTouched(false)) + yield put(ducks.form.creators.setValues(data, true)) + } + static show(data: any) { + return new Promise(resolve => { + showDialog(Create, BetaReleaseConfigDuck, function* (duck) { + yield duck.show(data, function* () { + resolve(yield duck.ducks.form.submit()) + }) + }) + }) + } +} + +export interface Fvalues { + regionId: number + instanceId: string + comment: string + releaseVersion: string + namespace: string + group: string + name: string + clientLabels?: ClientLabel[] +} + +export class BetaCreateFormDuck extends FormDuck { + Values: Fvalues + validate(v, metaData) { + return betaValidator(v, metaData) + } + *submit() { + const { creators, selectors, selector } = this + yield put(creators.setAllTouched(true)) + const firstInvalid = yield select(selectors.firstInvalid) + if (firstInvalid) { + throw firstInvalid + } + const { values } = yield select(selector) + const { name, namespace, group, releaseVersion, comment, clientLabels: clientLabels } = values + const result = yield releaseConfigFile({ + namespace, + group, + fileName: name, + name: releaseVersion, + releaseDescription: comment, + betaLabels: clientLabels, + releaseType: "gray" + }) + return result + } +} +// 一些校验 +const betaValidator = BetaCreateFormDuck.combineValidators({ + releaseVersion(v) { + if (!v) { + return '请输入版本号' + } + }, +}) diff --git a/web/src/polaris/configuration/fileGroup/detail/file/operation/ReleaseConfigDuck.ts b/web/src/polaris/configuration/fileGroup/detail/file/operation/ReleaseConfigDuck.ts index b7d1a800..a3e22953 100644 --- a/web/src/polaris/configuration/fileGroup/detail/file/operation/ReleaseConfigDuck.ts +++ b/web/src/polaris/configuration/fileGroup/detail/file/operation/ReleaseConfigDuck.ts @@ -45,6 +45,7 @@ export default class ReleaseConfigDuck extends Base { }) } } + export interface Fvalues { regionId: number instanceId: string diff --git a/web/src/polaris/configuration/fileGroup/detail/version/getColumns.tsx b/web/src/polaris/configuration/fileGroup/detail/version/getColumns.tsx index c828356e..32c7e2b2 100644 --- a/web/src/polaris/configuration/fileGroup/detail/version/getColumns.tsx +++ b/web/src/polaris/configuration/fileGroup/detail/version/getColumns.tsx @@ -18,7 +18,14 @@ export default ({ render: x => ( {x.fileName} - {x.active ? ( + {(x.active && x.releaseType === 'gray') ? ( + + {'灰度使用中'} + + ) : ( + <> + )} + {(x.active && x.releaseType !== 'gray') ? ( {'使用中'} diff --git a/web/src/polaris/configuration/fileGroup/model.ts b/web/src/polaris/configuration/fileGroup/model.ts index 66f0bcda..a9995df3 100644 --- a/web/src/polaris/configuration/fileGroup/model.ts +++ b/web/src/polaris/configuration/fileGroup/model.ts @@ -1,4 +1,4 @@ -import { ConfigFileGroup, KeyValuePair, ConfigFile, ConfigFileRelease } from './types' +import { ConfigFileGroup, KeyValuePair, ConfigFile, ConfigFileRelease, ClientLabel } from './types' import { getApiRequest, apiRequest, putApiRequest, deleteApiRequest } from '@src/polaris/common/util/apiRequest' import { object2FormData } from '@src/polaris/common/helpers/form' @@ -194,6 +194,8 @@ export interface ReleaseConfigFileParams { comment?: string createBy?: string releaseDescription?: string + betaLabels?: ClientLabel[] + releaseType?: string } export interface ReleaseConfigFileResult { configFileRelease: ConfigFileRelease @@ -220,6 +222,19 @@ export async function describeLastReleaseConfigFile(params: DescribeLastReleaseC }) return res } +export interface StopBetaReleaseConfigFileParams { + file_name: string + namespace: string + group: string +} +export type StopConfigFileBetaReleaseResult = {} +export async function stopBetaReleaseConfigFile(params: StopBetaReleaseConfigFileParams[]) { + const res = await apiRequest({ + action: 'config/v1/configfiles/releases/stopbeta', + data: params, + }) + return res +} export interface ConfigFileTemplate { id: string @@ -316,7 +331,6 @@ export async function importConfigFile(params: ImportConfigFilesParams) { return res } - export interface ConfigFileEncryptAlgorithm { algorithms: string[] } diff --git a/web/src/polaris/configuration/fileGroup/types.ts b/web/src/polaris/configuration/fileGroup/types.ts index f66a4726..c194eda4 100644 --- a/web/src/polaris/configuration/fileGroup/types.ts +++ b/web/src/polaris/configuration/fileGroup/types.ts @@ -52,6 +52,7 @@ export interface ConfigFileRelease { releaseDescription?: string releaseStatus?: string active: string + releaseType?: string format: string } export interface ConfigFileReleaseHistory { @@ -79,3 +80,73 @@ export interface ConfigFileGroupTag { key?: string value?: string } + +export interface ClientLabel { + key?: string + value: { + type: string + value: string + value_type: string + } +} + +// 匹配规则类型 +export enum ClientLabelType { + CLIENT_ID = 'CLIENT_ID', + // CLIENT_LANGUAGE = 'CLIENT_LANGUAGE', + CLIENT_IP = 'CLIENT_IP', +} + +export const ClientLabelTypeOptions = [ + { + value: ClientLabelType.CLIENT_ID, + text: '客户端ID', + }, + { + value: ClientLabelType.CLIENT_IP, + text: '客户端Host', + }, + // { + // value: ClientLabelType.CLIENT_LANGUAGE, + // text: '请求头(HEADER)', + // }, +] +export const ClientLabelTextMap = ClientLabelTypeOptions.reduce((map, curr) => { + map[curr.value] = curr.text + return map +}, {}) + +export enum ClientLabelMatchType { + EXACT = 'EXACT', + REGEX = 'REGEX', + NOT_EQUALS = 'NOT_EQUALS', + IN = 'IN', + NOT_IN = 'NOT_IN', + RANGE = 'RANGE', +} +export const ClientLabelMatchTypeOptions = [ + { + value: ClientLabelMatchType.EXACT, + text: '等于', + }, + { + value: ClientLabelMatchType.REGEX, + text: '正则表达式匹配', + }, + { + value: ClientLabelMatchType.NOT_EQUALS, + text: '不等于', + }, + { + value: ClientLabelMatchType.IN, + text: '包含', + }, + { + value: ClientLabelMatchType.NOT_IN, + text: '不包含', + }, +] +export const RouteLabelTextMap = ClientLabelMatchTypeOptions.reduce((map, curr) => { + map[curr.value] = curr.text + return map +}, {}) diff --git a/web/src/polaris/configuration/releaseHistory/types.ts b/web/src/polaris/configuration/releaseHistory/types.ts index 25354764..b36270fa 100644 --- a/web/src/polaris/configuration/releaseHistory/types.ts +++ b/web/src/polaris/configuration/releaseHistory/types.ts @@ -17,10 +17,14 @@ export enum ConfigReleaseType { ROLLBACK = 'rollback', DELETE = 'delete', CLEAN = 'clean', + BETAING = 'betaing', + CANCEL_BETA = 'cancel-gray', } export const ConfigReleaseTypeMap = { [ConfigReleaseType.PUBLISH]: '发布', [ConfigReleaseType.ROLLBACK]: '回滚', [ConfigReleaseType.DELETE]: '删除', [ConfigReleaseType.CLEAN]: '文件删除', + [ConfigReleaseType.BETAING]: '灰度发布', + [ConfigReleaseType.CANCEL_BETA]: '取消灰度发布', }