mirror of
https://github.com/labring/sealos.git
synced 2026-08-30 17:58:09 +08:00
fix(dbprovider): support Redis parameter configuration (#7181)
* fix(dbprovider): show redis parameter config in detail * fix(dbprovider): read redis params from configuration CR * fix(dbprovider): fall back to redis config map parsing * fix(dbprovider): show unsupported redis data import state
This commit is contained in:
@@ -157,6 +157,7 @@
|
||||
"current_connections": "Current Connections",
|
||||
"manage_data_redirect_failed": "Failed to open data management",
|
||||
"data_import": "Data Import",
|
||||
"data_import_unsupported_for_redis": "Redis does not support data import yet",
|
||||
"data_migration_config": "Data Migration Settings",
|
||||
"database": "database",
|
||||
"database_config": "parameters",
|
||||
|
||||
@@ -156,6 +156,7 @@
|
||||
"manage_data_redirect_failed": "打开数据管理失败",
|
||||
"current_connections": "当前连接数",
|
||||
"data_import": "数据导入",
|
||||
"data_import_unsupported_for_redis": "Redis 暂不支持数据导入",
|
||||
"data_migration_config": "数据迁移配置",
|
||||
"database": "数据库",
|
||||
"database_config": "数据库参数",
|
||||
|
||||
@@ -440,10 +440,10 @@ export const DBReconfigureMap: Partial<
|
||||
},
|
||||
redis: {
|
||||
type: 'ini',
|
||||
configMapName: '',
|
||||
configMapKey: '',
|
||||
reconfigureName: '',
|
||||
reconfigureKey: ''
|
||||
configMapName: '-redis-redis-replication-config',
|
||||
configMapKey: 'redis.conf',
|
||||
reconfigureName: 'redis-replication-config',
|
||||
reconfigureKey: 'redis.conf'
|
||||
},
|
||||
kafka: {
|
||||
type: 'ini',
|
||||
@@ -639,5 +639,11 @@ export const ParameterFieldMetadataMap: Partial<
|
||||
default: {
|
||||
// No params are allowed to be modified for MongoDB
|
||||
}
|
||||
},
|
||||
redis: {
|
||||
default: {
|
||||
maxclients: { editable: true },
|
||||
maxmemory: { editable: true }
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
ParameterConfigField,
|
||||
ParameterFieldMetadata
|
||||
} from '@/types/db';
|
||||
import { parseConfig, flattenObject } from '@/utils/tools';
|
||||
import { parseConfig, parseRedisConfig, flattenObject } from '@/utils/tools';
|
||||
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||
|
||||
/**
|
||||
@@ -91,13 +91,6 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
});
|
||||
}
|
||||
|
||||
const key = name + dbConfig.configMapName;
|
||||
if (!key || !dbConfig.configMapName) {
|
||||
return jsonRes(res, {
|
||||
data: null
|
||||
});
|
||||
}
|
||||
|
||||
let dbVersion: string | undefined;
|
||||
try {
|
||||
const { body: clusterData } = (await k8sCustomObjects.getNamespacedCustomObject(
|
||||
@@ -112,19 +105,71 @@ export default async function handler(req: NextApiRequest, res: NextApiResponse<
|
||||
console.warn('Failed to get cluster version, using default config:', error);
|
||||
}
|
||||
|
||||
const { body } = await k8sCore.readNamespacedConfigMap(key, namespace);
|
||||
let parsedConfig: Record<string, any> | null = null;
|
||||
|
||||
const configData = body?.data && body?.data[dbConfig.configMapKey];
|
||||
if (!configData) {
|
||||
if (dbType === 'redis') {
|
||||
const redisConfigMapKey = name + dbConfig.configMapName;
|
||||
let redisConfigMapData = '';
|
||||
|
||||
if (redisConfigMapKey && dbConfig.configMapName) {
|
||||
try {
|
||||
const { body } = await k8sCore.readNamespacedConfigMap(redisConfigMapKey, namespace);
|
||||
redisConfigMapData = body?.data?.[dbConfig.configMapKey] || '';
|
||||
} catch (error) {
|
||||
console.warn('Failed to get redis config map, falling back to configuration CR:', error);
|
||||
}
|
||||
}
|
||||
|
||||
try {
|
||||
const { body: configurationBody } = (await k8sCustomObjects.getNamespacedCustomObject(
|
||||
'apps.kubeblocks.io',
|
||||
'v1alpha1',
|
||||
namespace,
|
||||
'configurations',
|
||||
`${name}-redis`
|
||||
)) as { body: any };
|
||||
const redisConfigItem = configurationBody?.spec?.configItemDetails?.find(
|
||||
(item: any) => item.name === dbConfig.reconfigureName
|
||||
);
|
||||
const redisConfigParams = redisConfigItem?.configFileParams?.['redis.conf']?.parameters || {};
|
||||
const mergedRedisConfig = {
|
||||
...(redisConfigMapData ? parseRedisConfig(redisConfigMapData) : {}),
|
||||
...redisConfigParams
|
||||
};
|
||||
parsedConfig = Object.keys(mergedRedisConfig).length > 0 ? mergedRedisConfig : null;
|
||||
} catch (error) {
|
||||
console.warn('Failed to get redis configuration, using config map fallback if available:', error);
|
||||
parsedConfig = redisConfigMapData ? parseRedisConfig(redisConfigMapData) : null;
|
||||
}
|
||||
} else {
|
||||
const key = name + dbConfig.configMapName;
|
||||
if (!key || !dbConfig.configMapName) {
|
||||
return jsonRes(res, {
|
||||
data: null
|
||||
});
|
||||
}
|
||||
|
||||
const { body } = await k8sCore.readNamespacedConfigMap(key, namespace);
|
||||
|
||||
const configData = body?.data && body?.data[dbConfig.configMapKey];
|
||||
if (!configData) {
|
||||
return jsonRes(res, {
|
||||
data: null
|
||||
});
|
||||
}
|
||||
|
||||
parsedConfig = parseConfig({
|
||||
configString: configData,
|
||||
type: dbConfig.type
|
||||
});
|
||||
}
|
||||
|
||||
if (!parsedConfig) {
|
||||
return jsonRes(res, {
|
||||
data: null
|
||||
});
|
||||
}
|
||||
|
||||
const parsedConfig = parseConfig({
|
||||
configString: configData,
|
||||
type: dbConfig.type
|
||||
});
|
||||
const flattenedConfig = flattenObject(parsedConfig);
|
||||
|
||||
const versionedOverrides = ParameterFieldOverrides[dbType] || {};
|
||||
|
||||
@@ -1,11 +1,12 @@
|
||||
import { DBDetailType } from '@/types/db';
|
||||
import { Box, Button, Flex } from '@chakra-ui/react';
|
||||
import { Box, Button, Flex, Text } from '@chakra-ui/react';
|
||||
import { useTranslation } from 'next-i18next';
|
||||
import { useState } from 'react';
|
||||
import { MigrateTable } from './Migrate/Table';
|
||||
import DumpImport from './DumpImport';
|
||||
import useEnvStore from '@/store/env';
|
||||
import { useRouter } from 'next/router';
|
||||
import MyIcon from '@/components/Icon';
|
||||
|
||||
enum MenuType {
|
||||
DumpImport = 'DumpImport',
|
||||
@@ -20,6 +21,17 @@ export default function DataImport({ db }: { db?: DBDetailType }) {
|
||||
const { SystemEnv } = useEnvStore();
|
||||
const router = useRouter();
|
||||
|
||||
if (db.dbType === 'redis') {
|
||||
return (
|
||||
<Flex h={'full'} alignItems={'center'} justifyContent={'center'} flexDirection={'column'}>
|
||||
<MyIcon name={'noEvents'} color={'transparent'} width={'36px'} height={'36px'} />
|
||||
<Text pt={'8px'} color={'grayModern.600'} fontSize={'14px'}>
|
||||
{t('data_import_unsupported_for_redis')}
|
||||
</Text>
|
||||
</Flex>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<Flex flexDirection={'column'} h={'full'}>
|
||||
<Flex justifyContent={'space-between'} alignItems={'center'} mb={'16px'}>
|
||||
|
||||
@@ -56,7 +56,9 @@ const AppDetail = ({
|
||||
const [isSmallScreen] = useMediaQuery('(max-width: 1180px)');
|
||||
|
||||
const { isDataImportSupported, listNav } = useMemo(() => {
|
||||
const PublicNetMigration = ['postgresql', 'apecloud-mysql', 'mongodb'].includes(dbType);
|
||||
const PublicNetMigration = ['postgresql', 'apecloud-mysql', 'mongodb', 'redis'].includes(
|
||||
dbType
|
||||
);
|
||||
const MigrateSupported = ['postgresql', 'mongodb', 'apecloud-mysql'].includes(dbType);
|
||||
const BackupSupported = BackupSupportedDBTypeList.includes(dbType) && SystemEnv.BACKUP_ENABLED;
|
||||
const MonitorSupported = dbType !== 'polardbx';
|
||||
@@ -333,7 +335,7 @@ export async function getServerSideProps(context: any) {
|
||||
const queryListType = context.query?.listType || TabEnum.Overview;
|
||||
const dataImportEnabled = process.env.DATA_IMPORT_ENABLED !== 'false';
|
||||
const dataImportSupported =
|
||||
['postgresql', 'apecloud-mysql', 'mongodb'].includes(dbType) && dataImportEnabled;
|
||||
['postgresql', 'apecloud-mysql', 'mongodb', 'redis'].includes(dbType) && dataImportEnabled;
|
||||
const listType =
|
||||
queryListType === TabEnum.DataImport && !dataImportSupported ? TabEnum.Overview : queryListType;
|
||||
|
||||
|
||||
@@ -241,6 +241,15 @@ export async function getServerSideProps(context: any) {
|
||||
const dbType = context?.query?.dbType || '';
|
||||
const tabType = context?.query?.type || 'form';
|
||||
|
||||
if (dbType === 'redis') {
|
||||
return {
|
||||
redirect: {
|
||||
destination: `/db/detail?name=${dbName}&dbType=${dbType}&listType=dataImport`,
|
||||
permanent: false
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
props: { ...(await serviceSideProps(context)), dbName, tabType, dbType }
|
||||
};
|
||||
|
||||
@@ -583,7 +583,7 @@ export const json2MigrateCR = (data: MigrateForm) => {
|
||||
};
|
||||
|
||||
const templateName = templateByDB[data.dbType];
|
||||
if (templateName === undefined) {
|
||||
if (!templateName) {
|
||||
throw new Error(`Migration is not supported for database type: ${data.dbType}`);
|
||||
}
|
||||
|
||||
|
||||
@@ -463,6 +463,22 @@ export const parseConfig = ({
|
||||
}
|
||||
};
|
||||
|
||||
export const parseRedisConfig = (configString: string): Record<string, string> => {
|
||||
const result: Record<string, string> = {};
|
||||
|
||||
configString
|
||||
.split('\n')
|
||||
.map((line) => line.trim())
|
||||
.filter((line) => line && !line.startsWith('#'))
|
||||
.forEach((line) => {
|
||||
const [key, ...rest] = line.split(/\s+/);
|
||||
if (!key) return;
|
||||
result[key] = rest.join(' ');
|
||||
});
|
||||
|
||||
return result;
|
||||
};
|
||||
|
||||
export const flattenObject = (ob: any, prefix: string = ''): { key: string; value: string }[] => {
|
||||
const result: { key: string; value: string }[] = [];
|
||||
|
||||
@@ -483,7 +499,7 @@ export const adjustDifferencesForIni = (
|
||||
type: 'ini' | 'yaml',
|
||||
dbType: DBType
|
||||
): { path: string; newValue: string; oldValue: string }[] => {
|
||||
if (type !== 'ini' || dbType === 'postgresql') {
|
||||
if (type !== 'ini' || dbType !== 'apecloud-mysql') {
|
||||
return differences;
|
||||
}
|
||||
return differences.map((diff) => {
|
||||
|
||||
Reference in New Issue
Block a user