From 423d8a78f3d4438b982092c444c873bea1655a17 Mon Sep 17 00:00:00 2001 From: lixd Date: Wed, 22 Jul 2026 14:25:05 +0800 Subject: [PATCH 1/3] feat: use image repository in cluster forms Signed-off-by: lixd --- src/locales/en.json | 6 +- src/locales/zh.json | 6 +- .../Cluster/Detail/BaseDetail/index.jsx | 6 +- .../Cluster/actions/Create/Cluster.jsx | 126 ++--------------- .../Cluster/actions/Create/Confirm.jsx | 18 +-- .../Cluster/actions/Create/index.jsx | 12 +- .../containers/Cluster/actions/Registry.jsx | 26 ++-- .../containers/Cluster/actions/Upgrade.jsx | 46 +++---- .../cluster/containers/Registry/index.jsx | 4 +- .../cluster/actions/Create/Cluster.jsx | 127 +++--------------- .../cluster/actions/Create/Confirm.jsx | 18 +-- .../Template/cluster/actions/Create/index.jsx | 12 +- src/resources/cluster.jsx | 2 +- src/stores/templates.js | 7 - src/utils/object.mapper.js | 16 +-- 15 files changed, 81 insertions(+), 351 deletions(-) diff --git a/src/locales/en.json b/src/locales/en.json index 4dc8b6cf..3fd7ed13 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -337,8 +337,8 @@ "Please input search text": "Please input search text", "Unknown": "Unknown", "{name} {action} successfully.": "{name} {action} successfully.", - "LocalRegistry": "LocalRegistry", - "Please input localRegistry": "Please input localRegistry", + "Image Repository": "Image Repository", + "Please input image repository": "Please input image repository", "CertSANs": "CertSANs", "The registry for image of k8s's own components, if not input, the built-in image will be used.": "The registry for image of k8s's own components, if not input, the built-in image will be used.", "k8s cluster certSANs ip or dns.": "k8s cluster certSANs ip or dns.", @@ -451,7 +451,7 @@ "Target version error": "Target version error", "TIP_CLUSTER_IMAGE_TYPE_DESC": "Select online and gcr.com will be automatically downloaded from k8s official website k8s. Pull the required image from Io; Select offline to specify a different mirror source.", "TIP_CLUSTER_UPGRADE_DESC": "Cluster upgrade can span small versions, but it does not support upgrading that omits the next version. For example, you can upgrade from v1.20.2 to v1,20.13, or by v1.20.x to v1.21.x, but not from v1.20.x to v1.22.x", - "TIP_CLUSTER_LOCALREGISTRY": "The address of the warehouse where k8s its own component image is stored. If it is not filled in, the built-in image will be used", + "TIP_CLUSTER_IMAGE_REPOSITORY": "Repository prefix used to pull Kubernetes and cluster component images.", "The registry for image, such as the insecure registry in the daemon.json of docker.": "The registry for image, such as the insecure registry in the daemon.json of docker.", "Healthy Status": "Healthy Status", "Registry Address": "Registry Address", diff --git a/src/locales/zh.json b/src/locales/zh.json index a094ab1a..140de361 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -337,8 +337,8 @@ "Please input search text": "请输入搜索内容", "Unknown": "未知", "{name} {action} successfully.": "{name} {action} 成功。", - "LocalRegistry": "镜像仓库", - "Please input localRegistry": "请输入镜像仓库", + "Image Repository": "镜像仓库", + "Please input image repository": "请输入镜像仓库", "CertSANs": "", "The registry for image of k8s's own components, if not input, the built-in image will be used.": "存放 k8s 自身组件镜像的仓库地址 ,不填则使用内置镜像。", "k8s cluster certSANs ip or dns.": "k8s 集群 ca 证书签名的 ip 或者 dns。", @@ -451,7 +451,7 @@ "Target version error": "目标版本错误", "TIP_CLUSTER_IMAGE_TYPE_DESC": "选择“在线”,将自动从 k8s 官网 gcr.k8s.io 中拉取所需镜像;选择“离线”,可指定其他镜像来源。", "TIP_CLUSTER_UPGRADE_DESC": "集群升级可以跨小版本,但不支持略过次版本的升级,如您可以从 v1.20.2 升级到 v1.20.13,或由 v1.20.x 升级到 v1.21.x,但不支持从 v1.20.x 升级到 v1.22.x", - "TIP_CLUSTER_LOCALREGISTRY": "存放 k8s 自身组件镜像的仓库地址,不填则使用内置镜像", + "TIP_CLUSTER_IMAGE_REPOSITORY": "用于拉取 Kubernetes 和集群组件镜像的仓库前缀。", "The registry for image, such as the insecure registry in the daemon.json of docker.": "存放镜像仓库的地址,如:docker 的 daemon.json 文件中的 insecure registry。", "Healthy Status": "健康状态", "Registry Address": "仓库地址", diff --git a/src/pages/cluster/containers/Cluster/Detail/BaseDetail/index.jsx b/src/pages/cluster/containers/Cluster/Detail/BaseDetail/index.jsx index 99b3e2cd..1594f2cd 100644 --- a/src/pages/cluster/containers/Cluster/Detail/BaseDetail/index.jsx +++ b/src/pages/cluster/containers/Cluster/Detail/BaseDetail/index.jsx @@ -89,11 +89,11 @@ function BaseDetail() { dataIndex: 'kubernetesVersion', }, { - label: t('LocalRegistry'), + label: t('Image Repository'), dataIndex: 'offline', render: (_, data) => { - const { offline, localRegistry = '' } = data; - return `${offline ? t('Offline') : t('Online')} ${localRegistry}`; + const { offline, imageRepository = 'registry.k8s.io' } = data; + return `${offline ? t('Offline') : t('Online')} ${imageRepository}`; }, }, { diff --git a/src/pages/cluster/containers/Cluster/actions/Create/Cluster.jsx b/src/pages/cluster/containers/Cluster/actions/Create/Cluster.jsx index 019b97c2..e95aa1e0 100644 --- a/src/pages/cluster/containers/Cluster/actions/Create/Cluster.jsx +++ b/src/pages/cluster/containers/Cluster/actions/Create/Cluster.jsx @@ -15,7 +15,6 @@ */ import { observer } from 'mobx-react'; import BaseForm from 'components/Form'; -import { toJS } from 'mobx'; import { rootStore } from 'stores'; import { fqdn, path } from 'utils/regex'; import { @@ -47,7 +46,6 @@ const { nodeStore, regionStore, clusterStore, - registryStore, backupPointStore, } = rootStore; @@ -57,10 +55,8 @@ export default class Cluster extends BaseForm { this.store = clusterStore; this.nodeStore = nodeStore; this.regionStore = regionStore; - this.registryStore = registryStore; this.backupPointStore = backupPointStore; - await this.getCommonRegistry(); await this.getBackupPoint(); await this.initVersions(); } @@ -92,10 +88,6 @@ export default class Cluster extends BaseForm { }; } - async getCommonRegistry() { - await this.registryStore.fetchList(); - } - async getBackupPoint() { await this.backupPointStore.fetchList(); } @@ -133,9 +125,11 @@ export default class Cluster extends BaseForm { const versions = isOffLine ? this.offlineVersions : this.onlineVersions; const [firstVersion] = versions; const currentVersions = this.getCurrentVersionsByK8s(firstVersion); + const imageRepository = isOffLine ? '' : 'registry.k8s.io'; await this.updateContext({ offline: isOffLine, + imageRepository, kubernetesVersions: versions, kubernetesVersion: firstVersion?.value, ...currentVersions, @@ -144,6 +138,7 @@ export default class Cluster extends BaseForm { kubernetesVersion: firstVersion?.value, ...currentVersions, }); + this.updateFormValue('imageRepository', imageRepository); }; updateFormVersions(versions) { @@ -218,14 +213,6 @@ export default class Cluster extends BaseForm { })); } - get getRegistryOptions() { - const data = toJS(this.registryStore.list.data); - return (data || []).map(({ host }) => ({ - value: host, - label: host, - })); - } - // eslint-disable-next-line no-unused-vars checkVip = (rule, value) => // const existNodeList = nodeStore.list.data.some((v) => v.ip === value); @@ -362,31 +349,6 @@ export default class Cluster extends BaseForm { return Promise.resolve(true); }; - checkRegistry = (rule, value) => { - if (!value) return Promise.resolve(true); - - const checkFunc = (item) => { - if (!item) return true; - if ( - isDomain(item) || - isDomainPath(item) || - isIPv4(item) || - isIpPort(item) - ) { - return true; - } - return false; - }; - - const isInValide = value.some((it) => !checkFunc(it.value)); - - if (isInValide) { - return Promise.reject(t('Please enter a legal registry')); - } - - return Promise.resolve(true); - }; - checkLabels = (rule, value = []) => { const checkFunc = (item) => { if (!item) return true; @@ -405,56 +367,12 @@ export default class Cluster extends BaseForm { return Promise.resolve(true); }; - updateInsecureRegistry = (value, type) => { - const checkFunc = (item) => { - if (!item) return false; - if ( - isDomain(item) || - isIPv4(item) || - isIpPort(item) || - isDomainPath(item) - ) { - return true; - } - return false; - }; - - const getInsecureRegistry = (isDocker) => - isDocker ? 'dockerInsecureRegistry' : 'containerdInsecureRegistry'; - - let updateFormKey = getInsecureRegistry(this.isDocker); - if (type) { - updateFormKey = getInsecureRegistry(type === 'docker'); - } - - if (checkFunc(value)) { - this.updateFormValue(updateFormKey, [ - { index: 0, value, disabled: true }, - ]); - } - }; - - onK8SRegistryChange = (e) => { - this.updateInsecureRegistry(e); - }; - - get localRegistryValue() { - return this.getFormInstance().getFieldValue('localRegistry'); - } - - onRunTimeTypeChange = (type) => { - if (this.isOffLine) { - this.updateInsecureRegistry(this.localRegistryValue, type); - } - }; - onK8SVersionChange = async (value) => { const isAfter20 = versionCompare('v1.20.0', value) <= 0; const isAfter24 = versionCompare('v1.24.0', value) <= 0; const containerRuntimeType = isAfter20 ? 'containerd' : 'docker'; this.updateFormValue('containerRuntimeType', containerRuntimeType); - this.updateInsecureRegistry(this.localRegistryValue, containerRuntimeType); this.setState({ containerRuntimeType, }); @@ -526,14 +444,13 @@ export default class Cluster extends BaseForm { onChange: this.handleImgType, }, { - name: 'localRegistry', - label: t('LocalRegistry'), - type: 'select-input', - placeholder: t('Please input localRegistry'), - options: this.getRegistryOptions, + name: 'imageRepository', + label: t('Image Repository'), + type: 'input', + placeholder: t('Please input image repository'), maxLength: 256, validator: this.checkk8sRegistry, - onChange: this.onK8SRegistryChange, + required: this.isOffLine, extra: this.isOffLine ? t( 'The images are pulled from the local server by default. You can also fill in another private registry. The components will inherit the registry by default. Please ensure that the required images are stored in the registry. You can also set an independent registry for a specific component, and the component image will be pulled from this address.' @@ -618,7 +535,6 @@ export default class Cluster extends BaseForm { type: 'select', required: true, options: runtimeOption, - onChange: this.onRunTimeTypeChange, tip: t( 'K8S version before the v1.20.0, container runtime default docker, and then default containerd, after v1.24.0 not support docker.' ), @@ -649,19 +565,6 @@ export default class Cluster extends BaseForm { hidden: !this.isDocker, tip: t('The root dir in daemon.json.'), }, - { - name: 'dockerInsecureRegistry', - label: t('Docker Registry'), - type: 'array-input', - isInput: true, - width: '100%', - hidden: !this.isDocker, - placeholder: t('Please input registry'), - tip: t( - 'The registry for image, such as the insecure registry in the daemon.json of docker.' - ), - validator: this.checkRegistry, - }, // containerd { name: 'containerdVersion', @@ -680,19 +583,6 @@ export default class Cluster extends BaseForm { hidden: this.isDocker, tip: t('Root dir in config.toml'), }, - { - name: 'containerdInsecureRegistry', - label: t('Containerd Registry'), - type: 'array-input', - isInput: true, - width: '100%', - hidden: this.isDocker, - validator: this.checkRegistry, - placeholder: t('Please input registry'), - tip: t( - 'The registry containerd image, registry.mirrors in the config.toml.' - ), - }, ], // 网络 [ diff --git a/src/pages/cluster/containers/Cluster/actions/Create/Confirm.jsx b/src/pages/cluster/containers/Cluster/actions/Create/Confirm.jsx index f2b8fdea..2164c65a 100644 --- a/src/pages/cluster/containers/Cluster/actions/Create/Confirm.jsx +++ b/src/pages/cluster/containers/Cluster/actions/Create/Confirm.jsx @@ -17,7 +17,7 @@ import { observer } from 'mobx-react'; import BaseForm from 'components/Form'; import { arrayInputValue } from 'utils'; -import { get, flatten, isEmpty } from 'lodash'; +import { get, isEmpty } from 'lodash'; import { rootStore } from 'stores'; export class ConfirmStep extends BaseForm { @@ -96,10 +96,6 @@ export class ConfirmStep extends BaseForm { return []; } - formatRegistry(values) { - return flatten(arrayInputValue(values)); - } - containerRuntimeItem() { const { context: { containerRuntimeType, dockerVersion, containerdVersion }, @@ -113,11 +109,6 @@ export class ConfirmStep extends BaseForm { value: dockerVersion, }, ...this.notFilled(t('Docker Data Path'), 'dockerRootDir'), - ...this.notFilled( - t('Docker Registry'), - 'dockerInsecureRegistry', - this.formatRegistry - ), ]; } return [ @@ -126,11 +117,6 @@ export class ConfirmStep extends BaseForm { value: containerdVersion, }, ...this.notFilled(t('Containerd Data Path'), 'containerdRootDir'), - ...this.notFilled( - t('Containerd Registry'), - 'containerdInsecureRegistry', - this.formatRegistry - ), ]; } @@ -240,7 +226,7 @@ export class ConfirmStep extends BaseForm { label: t('Image Type'), value: offline ? t('Offline') : t('Online'), }, - ...this.notFilled(t('LocalRegistry'), 'localRegistry'), + ...this.notFilled(t('Image Repository'), 'imageRepository'), { label: t('K8S Version'), value: kubernetesVersion, diff --git a/src/pages/cluster/containers/Cluster/actions/Create/index.jsx b/src/pages/cluster/containers/Cluster/actions/Create/index.jsx index a1fc9769..1ee68b89 100644 --- a/src/pages/cluster/containers/Cluster/actions/Create/index.jsx +++ b/src/pages/cluster/containers/Cluster/actions/Create/index.jsx @@ -15,7 +15,7 @@ */ import { StepAction } from 'containers/Action'; -import { flatten, get, omit, assign, filter } from 'lodash'; +import { get, omit, assign, filter } from 'lodash'; import { observer } from 'mobx-react'; import { computeAutoDetection } from 'resources/cluster'; import { formatNodesWithLabel } from 'resources/node'; @@ -124,8 +124,6 @@ export default class Create extends StepAction { return t('Cluster {name} is installing.', { name: this.instanceName }); } - getRegistry = (registry) => flatten(arrayInputValue(registry)); - getComponents = (values) => { const { plugins = {}, defaultStorage = '' } = values; const enabledComponents = []; @@ -176,7 +174,7 @@ export default class Create extends StepAction { /* step2: Cluster config */ // image offline, - localRegistry, + imageRepository, kubernetesVersion, certSANs, etcdDataDir, @@ -187,10 +185,8 @@ export default class Create extends StepAction { externalCaKey, // container runtime containerRuntimeType, - dockerInsecureRegistry, dockerRootDir, dockerVersion, - containerdInsecureRegistry, containerdRootDir, containerdVersion, backupPoint, @@ -278,19 +274,17 @@ export default class Create extends StepAction { externalCaKey: externalCA ? safeBtoa(externalCaKey) : '', masters: formatNodesWithLabel(values).master, workers: formatNodesWithLabel(values).worker || [], - localRegistry, + imageRepository, kubernetesVersion, containerRuntime: { type: containerRuntimeType, ...(containerRuntimeType === 'docker' ? { version: dockerVersion, - insecureRegistry: this.getRegistry(dockerInsecureRegistry), // dockerInsecureRegistry, rootDir: dockerRootDir, } : { version: containerdVersion, - insecureRegistry: this.getRegistry(containerdInsecureRegistry), rootDir: containerdRootDir, }), }, diff --git a/src/pages/cluster/containers/Cluster/actions/Registry.jsx b/src/pages/cluster/containers/Cluster/actions/Registry.jsx index 72e6be0b..6fe85360 100644 --- a/src/pages/cluster/containers/Cluster/actions/Registry.jsx +++ b/src/pages/cluster/containers/Cluster/actions/Registry.jsx @@ -71,13 +71,6 @@ class Registry extends ModalAction { const defaultRegistry = get(this.item, 'containerRuntime.registries') ?? []; const registries = defaultRegistry.map((it, index) => { - if (it.insecureRegistry) { - return { - value: it.insecureRegistry, - index, - }; - } - const existItem = this.registryData.find( (r) => it.registryRef === r.name ); @@ -118,6 +111,15 @@ class Registry extends ModalAction { type: 'array-input', itemComponent: SelectWithInput, options: this.registryOptions, + validator: (_, value = []) => { + const invalid = value.some( + ({ value: host }) => + !this.registryData.some((registry) => registry.host === host) + ); + return invalid + ? Promise.reject(t('Please select a Registry resource')) + : Promise.resolve(); + }, }, ]; } @@ -126,13 +128,9 @@ class Registry extends ModalAction { const { registries } = values; const formTemplate = this.item._originData; - const newRegistries = registries.map(({ value }) => { - const existItem = this.registryData.find(({ host }) => host === value); - return { - insecureRegistry: existItem ? '' : value, // 新增 insecureRegistry 传 - registryRef: existItem ? existItem.name : '', // 已存在 registryRef 传 name - }; - }); + const newRegistries = registries.map(({ value }) => ({ + registryRef: this.registryData.find(({ host }) => host === value).name, + })); set(formTemplate, 'containerRuntime.registries', newRegistries); const { name } = this.item; diff --git a/src/pages/cluster/containers/Cluster/actions/Upgrade.jsx b/src/pages/cluster/containers/Cluster/actions/Upgrade.jsx index ff8e8c62..50dc4439 100644 --- a/src/pages/cluster/containers/Cluster/actions/Upgrade.jsx +++ b/src/pages/cluster/containers/Cluster/actions/Upgrade.jsx @@ -15,7 +15,6 @@ */ import React from 'react'; import { observer } from 'mobx-react'; -import { toJS } from 'mobx'; import { ModalAction } from 'containers/Action'; import { rootStore } from 'stores'; import styles from './index.less'; @@ -25,7 +24,7 @@ import { checkExpired, isDisableByProviderType, } from 'utils'; -import { isIPv4, isDomain, isIpPort } from 'utils/validate'; +import { isIPv4, isDomain, isDomainPath, isIpPort } from 'utils/validate'; @observer export default class Upgrade extends ModalAction { @@ -39,29 +38,27 @@ export default class Upgrade extends ModalAction { async init() { this.store = rootStore.clusterStore; - this.registryStore = rootStore.registryStore; await this.getVersion(); - await this.getCommonRegistry(); await this.initDefaultValue(); } initDefaultValue = async () => { - const { offline, localRegistry } = this.item; + const { offline, imageRepository } = this.item; this.setState({ offline, targetVersions: this.getMetaVersion(offline), - localRegistry, + imageRepository, }); this.updateDefaultValue(); }; get defaultValue() { - const { offline, localRegistry = '' } = this.state; + const { offline, imageRepository = 'registry.k8s.io' } = this.state; const versions = this.getMetaVersion(offline); return { offline, version: versions[0]?.value, - localRegistry, + imageRepository, }; } @@ -85,9 +82,11 @@ export default class Upgrade extends ModalAction { handleImgType = async (offline) => { const versions = this.getMetaVersion(offline); + const imageRepository = offline ? '' : 'registry.k8s.io'; await this.setState({ offline, + imageRepository, targetVersions: versions, version: versions?.[0]?.value, }); @@ -98,10 +97,6 @@ export default class Upgrade extends ModalAction { await this.store.fetchVersion({ limit: -1 }); } - async getCommonRegistry() { - await this.registryStore.fetchList(); - } - static policy = 'clusters:edit'; static isStatusRunning(item) { @@ -139,7 +134,12 @@ export default class Upgrade extends ModalAction { const checkFunc = (item) => { if (!item) return true; - if (isDomain(item) || isIPv4(item) || isIpPort(item)) { + if ( + isDomain(item) || + isDomainPath(item) || + isIPv4(item) || + isIpPort(item) + ) { return true; } return false; @@ -157,14 +157,6 @@ export default class Upgrade extends ModalAction { return offline === true; } - getRegistryOptions() { - const data = toJS(this.registryStore.list.data); - return (data || []).map(({ host }) => ({ - value: host, - label: host, - })); - } - get formItems() { return [ { @@ -212,14 +204,14 @@ export default class Upgrade extends ModalAction { ], }, { - name: 'localRegistry', - label: t('LocalRegistry'), - type: 'select-input', - placeholder: t('Please input localRegistry'), - options: this.getRegistryOptions(), + name: 'imageRepository', + label: t('Image Repository'), + type: 'input', + placeholder: t('Please input image repository'), hidden: !this.isOffLine, + required: this.isOffLine, maxLength: 256, - tip: t('TIP_CLUSTER_LOCALREGISTRY'), + tip: t('TIP_CLUSTER_IMAGE_REPOSITORY'), validator: this.checkk8sRegistry, }, ]; diff --git a/src/pages/cluster/containers/Registry/index.jsx b/src/pages/cluster/containers/Registry/index.jsx index d24d38b5..1c91d284 100644 --- a/src/pages/cluster/containers/Registry/index.jsx +++ b/src/pages/cluster/containers/Registry/index.jsx @@ -46,9 +46,9 @@ export default function Registry() { const currentProps = { columns, - authKey: 'localRegistry', + authKey: 'registries', module: 'registries', - name: t('LocalRegistry'), + name: t('Registry'), searchFilters: [], actionConfigs, store, diff --git a/src/pages/cluster/containers/Template/cluster/actions/Create/Cluster.jsx b/src/pages/cluster/containers/Template/cluster/actions/Create/Cluster.jsx index 95aa88fc..d43a0f1a 100644 --- a/src/pages/cluster/containers/Template/cluster/actions/Create/Cluster.jsx +++ b/src/pages/cluster/containers/Template/cluster/actions/Create/Cluster.jsx @@ -15,7 +15,6 @@ */ import { observer } from 'mobx-react'; import BaseForm from 'components/Form'; -import { toJS } from 'mobx'; import { rootStore } from 'stores'; import { fqdn } from 'utils/regex'; import { @@ -45,7 +44,6 @@ const { nodeStore, regionStore, clusterStore, - registryStore, backupPointStore, templatesStore, } = rootStore; @@ -56,11 +54,9 @@ export default class Cluster extends BaseForm { this.store = clusterStore; this.nodeStore = nodeStore; this.regionStore = regionStore; - this.registryStore = registryStore; this.backupPointStore = backupPointStore; this.templatesStore = templatesStore; - this.getCommonRegistry(); this.getBackupPoint(); this.getVersion(); } @@ -119,9 +115,11 @@ export default class Cluster extends BaseForm { const versions = isOffLine ? this.offlineVersions : this.onlineVersions; const [firstVersion] = versions; const currentVersions = this.getCurrentVersionsByK8s(firstVersion); + const imageRepository = isOffLine ? '' : 'registry.k8s.io'; await this.updateContext({ offline: isOffLine, + imageRepository, kubernetesVersions: versions, kubernetesVersion: firstVersion?.value, ...currentVersions, @@ -131,6 +129,7 @@ export default class Cluster extends BaseForm { kubernetesVersion: firstVersion?.value, ...currentVersions, }); + this.updateFormValue('imageRepository', imageRepository); }; updateFormVersions(versions) { @@ -192,10 +191,6 @@ export default class Cluster extends BaseForm { })); } - async getCommonRegistry() { - await this.registryStore.fetchList(); - } - async getBackupPoint() { await this.backupPointStore.fetchList(); } @@ -237,14 +232,6 @@ export default class Cluster extends BaseForm { })); } - getRegistryOptions() { - const data = toJS(this.registryStore.list.data); - return (data || []).map(({ host }) => ({ - value: host, - label: host, - })); - } - // eslint-disable-next-line no-unused-vars checkVip = (rule, value) => // const existNodeList = nodeStore.list.data.some((v) => v.ip === value); @@ -352,7 +339,12 @@ export default class Cluster extends BaseForm { const checkFunc = (item) => { if (!item) return true; - if (isDomain(item) || isIPv4(item) || isIpPort(item)) { + if ( + isDomain(item) || + isDomainPath(item) || + isIPv4(item) || + isIpPort(item) + ) { return true; } return false; @@ -365,26 +357,6 @@ export default class Cluster extends BaseForm { return Promise.resolve(true); }; - checkRegistry = (rule, value) => { - if (!value) return Promise.resolve(true); - - const checkFunc = (item) => { - if (!item) return true; - if (isDomain(item) || isIPv4(item) || isIpPort(item)) { - return true; - } - return false; - }; - - const isInValide = value.some((it) => !checkFunc(it.value)); - - if (isInValide) { - return Promise.reject(t('Please enter a legal registry')); - } - - return Promise.resolve(true); - }; - checkLabels = (rule, value = []) => { const checkFunc = (item) => { if (!item) return true; @@ -403,56 +375,12 @@ export default class Cluster extends BaseForm { return Promise.resolve(true); }; - updateInsecureRegistry = (value, type) => { - const checkFunc = (item) => { - if (!item) return false; - if ( - isDomain(item) || - isDomainPath(item) || - isIPv4(item) || - isIpPort(item) - ) { - return true; - } - return false; - }; - - const getInsecureRegistry = (isDocker) => - isDocker ? 'dockerInsecureRegistry' : 'containerdInsecureRegistry'; - - let updateFormKey = getInsecureRegistry(this.isDocker); - if (type) { - updateFormKey = getInsecureRegistry(type === 'docker'); - } - - if (checkFunc(value)) { - this.updateFormValue(updateFormKey, [ - { index: 0, value, disabled: true }, - ]); - } - }; - - onK8SRegistryChange = (e) => { - this.updateInsecureRegistry(e); - }; - - get localRegistryValue() { - return this.getFormInstance().getFieldValue('localRegistry'); - } - - onRunTimeTypeChange = (type) => { - if (this.isOffLine) { - this.updateInsecureRegistry(this.localRegistryValue, type); - } - }; - onK8SVersionChange = async (value) => { const isAfter20 = versionCompare('v1.20.0', value) <= 0; const isAfter24 = versionCompare('v1.24.0', value) <= 0; const containerRuntimeType = isAfter20 ? 'containerd' : 'docker'; this.updateFormValue('containerRuntimeType', containerRuntimeType); - this.updateInsecureRegistry(this.localRegistryValue, containerRuntimeType); this.setState({ containerRuntimeType, }); @@ -535,16 +463,16 @@ export default class Cluster extends BaseForm { onChange: this.handleImgType, }, { - name: 'localRegistry', - label: t('LocalRegistry'), - type: 'select-input', - placeholder: t('Please input localRegistry'), - options: this.getRegistryOptions(), + name: 'imageRepository', + label: t('Image Repository'), + type: 'input', + placeholder: t('Please input image repository'), maxLength: 256, + validator: this.checkk8sRegistry, + required: this.isOffLine, tip: t( "The registry for image of k8s's own components, if not input, the built-in image will be used." ), - onChange: this.onK8SRegistryChange, }, { name: 'kubernetesVersion', @@ -610,7 +538,6 @@ export default class Cluster extends BaseForm { type: 'select', required: true, options: runtimeOption, - onChange: this.onRunTimeTypeChange, tip: t( 'K8S version before the v1.20.0, container runtime default docker, and then default containerd, after v1.24.0 not support docker.' ), @@ -640,18 +567,6 @@ export default class Cluster extends BaseForm { hidden: !this.isDocker, tip: t('The root dir in daemon.json.'), }, - { - name: 'dockerInsecureRegistry', - label: t('Docker Registry'), - type: 'array-input', - isInput: true, - width: '100%', - hidden: !this.isDocker, - placeholder: t('Please input registry'), - tip: t( - 'The registry for image, such as the insecure registry in the daemon.json of docker.' - ), - }, // containerd { name: 'containerdVersion', @@ -669,18 +584,6 @@ export default class Cluster extends BaseForm { hidden: this.isDocker, tip: t('Root dir in config.toml'), }, - { - name: 'containerdInsecureRegistry', - label: t('Containerd Registry'), - type: 'array-input', - isInput: true, - width: '100%', - hidden: this.isDocker, - placeholder: t('Please input registry'), - tip: t( - 'The registry containerd image, registry.mirrors in the config.toml.' - ), - }, ], // 网络 [ diff --git a/src/pages/cluster/containers/Template/cluster/actions/Create/Confirm.jsx b/src/pages/cluster/containers/Template/cluster/actions/Create/Confirm.jsx index a9df7ca1..c94c89f3 100644 --- a/src/pages/cluster/containers/Template/cluster/actions/Create/Confirm.jsx +++ b/src/pages/cluster/containers/Template/cluster/actions/Create/Confirm.jsx @@ -17,7 +17,7 @@ import { observer } from 'mobx-react'; import BaseForm from 'components/Form'; import { arrayInputValue } from 'utils'; -import { get, flatten, isEmpty } from 'lodash'; +import { get, isEmpty } from 'lodash'; import { rootStore } from 'stores'; export class ConfirmStep extends BaseForm { @@ -82,10 +82,6 @@ export class ConfirmStep extends BaseForm { return []; } - formatRegistry(values) { - return flatten(arrayInputValue(values)); - } - containerRuntimeItem() { const { context: { containerRuntimeType, dockerVersion, containerdVersion }, @@ -99,11 +95,6 @@ export class ConfirmStep extends BaseForm { value: dockerVersion, }, ...this.notFilled(t('Docker Data Path'), 'dockerRootDir'), - ...this.notFilled( - t('Docker Registry'), - 'dockerInsecureRegistry', - this.formatRegistry - ), ]; } return [ @@ -112,11 +103,6 @@ export class ConfirmStep extends BaseForm { value: containerdVersion, }, ...this.notFilled(t('Containerd Data Path'), 'containerdRootDir'), - ...this.notFilled( - t('Containerd Registry'), - 'containerdInsecureRegistry', - this.formatRegistry - ), ]; } @@ -225,7 +211,7 @@ export class ConfirmStep extends BaseForm { label: t('Image Type'), value: offline ? t('Offline') : t('Online'), }, - ...this.notFilled(t('LocalRegistry'), 'localRegistry'), + ...this.notFilled(t('Image Repository'), 'imageRepository'), { label: t('K8S Version'), value: kubernetesVersion, diff --git a/src/pages/cluster/containers/Template/cluster/actions/Create/index.jsx b/src/pages/cluster/containers/Template/cluster/actions/Create/index.jsx index 2e30f266..408f1cc2 100644 --- a/src/pages/cluster/containers/Template/cluster/actions/Create/index.jsx +++ b/src/pages/cluster/containers/Template/cluster/actions/Create/index.jsx @@ -19,7 +19,7 @@ import { StepAction } from 'containers/Action'; import { rootStore } from 'stores'; import { arrayInputValue } from 'utils'; import { computeAutoDetection } from 'resources/cluster'; -import { flatten, get, set, omit, assign, filter } from 'lodash'; +import { get, set, omit, assign, filter } from 'lodash'; import Cluster from './Cluster'; import Plugin from './Plugin'; @@ -93,8 +93,6 @@ export default class Create extends StepAction { this.templatesStore = rootStore.templatesStore; } - getRegistry = (registry) => flatten(arrayInputValue(registry)); - /* base64 加密 mask: true 的属性 */ encodeProperty(components, enabledComponents) { enabledComponents.forEach(({ config, name }) => { @@ -165,7 +163,7 @@ export default class Create extends StepAction { /* step2: Cluster config */ // image offline, - localRegistry, + imageRepository, certSANs, etcdDataDir, kubeletDataDir, @@ -176,10 +174,8 @@ export default class Create extends StepAction { externalCaKey, // container runtime containerRuntimeType, - dockerInsecureRegistry, dockerRootDir, dockerVersion, - containerdInsecureRegistry, containerdRootDir, containerdVersion, backupPoint, @@ -231,7 +227,7 @@ export default class Create extends StepAction { certSANs: arrayInputValue(certSANs), externalCaCert: externalCA ? externalCaCert : '', externalCaKey: externalCA ? externalCaKey : '', - localRegistry, + imageRepository, workerNodeVip, kubernetesVersion, containerRuntime: { @@ -239,12 +235,10 @@ export default class Create extends StepAction { ...(containerRuntimeType === 'docker' ? { version: dockerVersion, - insecureRegistry: this.getRegistry(dockerInsecureRegistry), // dockerInsecureRegistry, rootDir: dockerRootDir, } : { version: containerdVersion, - insecureRegistry: this.getRegistry(containerdInsecureRegistry), rootDir: containerdRootDir, }), }, diff --git a/src/resources/cluster.jsx b/src/resources/cluster.jsx index a609ee04..1af31a07 100644 --- a/src/resources/cluster.jsx +++ b/src/resources/cluster.jsx @@ -23,7 +23,7 @@ import moment from 'moment'; export const clusterParams = { offline: true, - localRegistry: '', + imageRepository: '', etcdDataDir: '/var/lib/etcd', kubeletDataDir: '/var/lib/kubelet', containerRuntimeType: 'containerd', diff --git a/src/stores/templates.js b/src/stores/templates.js index 271c6b84..60106915 100644 --- a/src/stores/templates.js +++ b/src/stores/templates.js @@ -25,13 +25,6 @@ export default class TemplatesStore extends BaseStore { module = 'templates'; - async update(datas) { - const data = { - insecureRegistry: datas, - }; - await request.put(`${this.getListUrl()}`, data); - } - create(params, config) { const { templateName, diff --git a/src/utils/object.mapper.js b/src/utils/object.mapper.js index ab8dcae0..73026a11 100644 --- a/src/utils/object.mapper.js +++ b/src/utils/object.mapper.js @@ -191,22 +191,16 @@ const ClusterTemplateMapper = (item) => { externalIP, backupPoint, certSANs: get(config, 'certSANs'), - localRegistry: get(config, 'localRegistry'), + imageRepository: get( + config, + 'imageRepository', + get(config, 'offline') ? '' : 'registry.k8s.io' + ), workerNodeVip: get(config, 'workerNodeVip'), kubernetesVersion, containerRuntimeType, [`${containerRuntimeType}Version`]: containerRuntimeVersion, [`${containerRuntimeType}RootDir`]: get(config, 'containerRuntime.rootDir'), - [`${containerRuntimeType}InsecureRegistry`]: get( - config, - 'containerRuntime.insecureRegistry', - [] - ) - .filter((val) => !!val) - .map((val, index) => ({ - value: val, - index, - })), // externalCa externalCA: !!(externalCaKey || externalCaCert), externalCaKey, From 5b131ae9661212cd26f6c4d17c1fa3c27dd88e51 Mon Sep 17 00:00:00 2001 From: lixd Date: Wed, 22 Jul 2026 14:25:05 +0800 Subject: [PATCH 2/3] feat: select image registry resources in cluster forms Signed-off-by: lixd --- src/locales/en.json | 5 ++ src/locales/zh.json | 5 ++ .../Cluster/Detail/BaseDetail/index.jsx | 6 +- .../Cluster/actions/Create/Cluster.jsx | 64 ++++++++---------- .../Cluster/actions/Create/Confirm.jsx | 2 +- .../Cluster/actions/Create/index.jsx | 5 +- .../containers/Cluster/actions/Upgrade.jsx | 64 ++++++++---------- .../cluster/actions/Create/Cluster.jsx | 66 ++++++++----------- .../cluster/actions/Create/Confirm.jsx | 2 +- .../Template/cluster/actions/Create/index.jsx | 4 +- src/resources/cluster.jsx | 2 +- src/utils/object.mapper.js | 4 +- 12 files changed, 106 insertions(+), 123 deletions(-) diff --git a/src/locales/en.json b/src/locales/en.json index 3fd7ed13..ce5974ae 100644 --- a/src/locales/en.json +++ b/src/locales/en.json @@ -338,6 +338,11 @@ "Unknown": "Unknown", "{name} {action} successfully.": "{name} {action} successfully.", "Image Repository": "Image Repository", + "Image Registry": "Image Registry", + "Select a configured Registry resource. Its address and access settings will be used to pull offline installation images, and components inherit this Registry by default.": "Select a configured Registry resource. Its address and access settings will be used to pull offline installation images, and components inherit this Registry by default.", + "Optionally select a configured Registry resource for image pulls. If none is selected, images are pulled from the official registries.": "Optionally select a configured Registry resource for image pulls. If none is selected, images are pulled from the official registries.", + "Select a configured Registry resource for image pulls. If none is selected, online installation uses the official registries.": "Select a configured Registry resource for image pulls. If none is selected, online installation uses the official registries.", + "Please select a Registry resource": "Please select a configured Registry resource", "Please input image repository": "Please input image repository", "CertSANs": "CertSANs", "The registry for image of k8s's own components, if not input, the built-in image will be used.": "The registry for image of k8s's own components, if not input, the built-in image will be used.", diff --git a/src/locales/zh.json b/src/locales/zh.json index 140de361..e5cc8a0d 100644 --- a/src/locales/zh.json +++ b/src/locales/zh.json @@ -338,6 +338,11 @@ "Unknown": "未知", "{name} {action} successfully.": "{name} {action} 成功。", "Image Repository": "镜像仓库", + "Image Registry": "镜像仓库(Registry)", + "Select a configured Registry resource. Its address and access settings will be used to pull offline installation images, and components inherit this Registry by default.": "请选择已配置的 Registry 资源。系统将使用该资源的地址及访问配置拉取离线安装镜像,组件默认继承此 Registry。", + "Optionally select a configured Registry resource for image pulls. If none is selected, images are pulled from the official registries.": "可选:选择已配置的 Registry 资源用于拉取镜像;未选择时使用官方镜像仓库。", + "Select a configured Registry resource for image pulls. If none is selected, online installation uses the official registries.": "请选择已配置的 Registry 资源用于拉取镜像;未选择时在线安装使用官方镜像仓库。", + "Please select a Registry resource": "请选择已配置的 Registry 资源", "Please input image repository": "请输入镜像仓库", "CertSANs": "", "The registry for image of k8s's own components, if not input, the built-in image will be used.": "存放 k8s 自身组件镜像的仓库地址 ,不填则使用内置镜像。", diff --git a/src/pages/cluster/containers/Cluster/Detail/BaseDetail/index.jsx b/src/pages/cluster/containers/Cluster/Detail/BaseDetail/index.jsx index 1594f2cd..03458721 100644 --- a/src/pages/cluster/containers/Cluster/Detail/BaseDetail/index.jsx +++ b/src/pages/cluster/containers/Cluster/Detail/BaseDetail/index.jsx @@ -89,11 +89,11 @@ function BaseDetail() { dataIndex: 'kubernetesVersion', }, { - label: t('Image Repository'), + label: t('Image Registry'), dataIndex: 'offline', render: (_, data) => { - const { offline, imageRepository = 'registry.k8s.io' } = data; - return `${offline ? t('Offline') : t('Online')} ${imageRepository}`; + const { offline, imageRegistry = 'registry.k8s.io' } = data; + return `${offline ? t('Offline') : t('Online')} ${imageRegistry}`; }, }, { diff --git a/src/pages/cluster/containers/Cluster/actions/Create/Cluster.jsx b/src/pages/cluster/containers/Cluster/actions/Create/Cluster.jsx index e95aa1e0..08fc526c 100644 --- a/src/pages/cluster/containers/Cluster/actions/Create/Cluster.jsx +++ b/src/pages/cluster/containers/Cluster/actions/Create/Cluster.jsx @@ -14,6 +14,7 @@ * limitations under the License. */ import { observer } from 'mobx-react'; +import { toJS } from 'mobx'; import BaseForm from 'components/Form'; import { rootStore } from 'stores'; import { fqdn, path } from 'utils/regex'; @@ -21,8 +22,6 @@ import { isIPv4, isIpv6, isDomain, - isDomainPath, - isIpPort, isDomainPort, isIp, } from 'utils/validate'; @@ -46,6 +45,7 @@ const { nodeStore, regionStore, clusterStore, + registryStore, backupPointStore, } = rootStore; @@ -55,9 +55,13 @@ export default class Cluster extends BaseForm { this.store = clusterStore; this.nodeStore = nodeStore; this.regionStore = regionStore; + this.registryStore = registryStore; this.backupPointStore = backupPointStore; - await this.getBackupPoint(); + await Promise.all([ + this.getBackupPoint(), + this.registryStore.fetchList({ limit: -1 }), + ]); await this.initVersions(); } @@ -125,11 +129,11 @@ export default class Cluster extends BaseForm { const versions = isOffLine ? this.offlineVersions : this.onlineVersions; const [firstVersion] = versions; const currentVersions = this.getCurrentVersionsByK8s(firstVersion); - const imageRepository = isOffLine ? '' : 'registry.k8s.io'; + const imageRegistry = ''; await this.updateContext({ offline: isOffLine, - imageRepository, + imageRegistry, kubernetesVersions: versions, kubernetesVersion: firstVersion?.value, ...currentVersions, @@ -138,7 +142,7 @@ export default class Cluster extends BaseForm { kubernetesVersion: firstVersion?.value, ...currentVersions, }); - this.updateFormValue('imageRepository', imageRepository); + this.updateFormValue('imageRegistry', imageRegistry); }; updateFormVersions(versions) { @@ -185,6 +189,13 @@ export default class Cluster extends BaseForm { return options; } + get registryOptions() { + return toJS(this.registryStore.list.data || []).map(({ name, host, scheme }) => ({ + value: name, + label: `${name} (${scheme}://${host})`, + })); + } + get onlineVersions() { const data = this.store.onlineVersion; return data.map(({ version, version_control, archs }) => ({ @@ -326,28 +337,10 @@ export default class Cluster extends BaseForm { return Promise.reject(t('The selected node and schema are inconsistent')); }; - checkk8sRegistry = (rule, value) => { - if (!value) return Promise.resolve(true); - - const checkFunc = (item) => { - if (!item) return true; - if ( - isDomain(item) || - isDomainPath(item) || - isIPv4(item) || - isIpPort(item) - ) { - return true; - } - return false; - }; - - if (!checkFunc(value)) { - return Promise.reject(t('Please enter a legal registry')); - } - - return Promise.resolve(true); - }; + checkImageRegistry = (rule, value) => + !value && this.isOffLine + ? Promise.reject(t('Please select a Registry resource')) + : Promise.resolve(true); checkLabels = (rule, value = []) => { const checkFunc = (item) => { @@ -444,19 +437,18 @@ export default class Cluster extends BaseForm { onChange: this.handleImgType, }, { - name: 'imageRepository', - label: t('Image Repository'), - type: 'input', - placeholder: t('Please input image repository'), - maxLength: 256, - validator: this.checkk8sRegistry, + name: 'imageRegistry', + label: t('Image Registry'), + type: 'select', + options: this.registryOptions, + validator: this.checkImageRegistry, required: this.isOffLine, extra: this.isOffLine ? t( - 'The images are pulled from the local server by default. You can also fill in another private registry. The components will inherit the registry by default. Please ensure that the required images are stored in the registry. You can also set an independent registry for a specific component, and the component image will be pulled from this address.' + 'Select a configured Registry resource. Its address and access settings will be used to pull offline installation images, and components inherit this Registry by default.' ) : t( - 'The images are pulled from the official registry by default, for example, k8s images are pulled from k8s.gcr.io and calico images are pulled from docker.io. You can also fill in another private registry. The components will inherit the registry by default. Please ensure that the required images are stored in the registry. You can also set an independent registry for a specific component, and the component image will be pulled from this address.' + 'Optionally select a configured Registry resource for image pulls. If none is selected, images are pulled from the official registries.' ), }, { diff --git a/src/pages/cluster/containers/Cluster/actions/Create/Confirm.jsx b/src/pages/cluster/containers/Cluster/actions/Create/Confirm.jsx index 2164c65a..fcc641c4 100644 --- a/src/pages/cluster/containers/Cluster/actions/Create/Confirm.jsx +++ b/src/pages/cluster/containers/Cluster/actions/Create/Confirm.jsx @@ -226,7 +226,7 @@ export class ConfirmStep extends BaseForm { label: t('Image Type'), value: offline ? t('Offline') : t('Online'), }, - ...this.notFilled(t('Image Repository'), 'imageRepository'), + ...this.notFilled(t('Image Registry'), 'imageRegistry'), { label: t('K8S Version'), value: kubernetesVersion, diff --git a/src/pages/cluster/containers/Cluster/actions/Create/index.jsx b/src/pages/cluster/containers/Cluster/actions/Create/index.jsx index 1ee68b89..5312803d 100644 --- a/src/pages/cluster/containers/Cluster/actions/Create/index.jsx +++ b/src/pages/cluster/containers/Cluster/actions/Create/index.jsx @@ -174,7 +174,7 @@ export default class Create extends StepAction { /* step2: Cluster config */ // image offline, - imageRepository, + imageRegistry, kubernetesVersion, certSANs, etcdDataDir, @@ -246,7 +246,6 @@ export default class Create extends StepAction { : {}; const offlineAnnotations = offline ? { 'kubeclipper.io/offline': '' } : {}; - const params = { kind: 'Cluster', apiVersion: 'core.kubeclipper.io/v1', @@ -274,7 +273,7 @@ export default class Create extends StepAction { externalCaKey: externalCA ? safeBtoa(externalCaKey) : '', masters: formatNodesWithLabel(values).master, workers: formatNodesWithLabel(values).worker || [], - imageRepository, + imageRegistry, kubernetesVersion, containerRuntime: { type: containerRuntimeType, diff --git a/src/pages/cluster/containers/Cluster/actions/Upgrade.jsx b/src/pages/cluster/containers/Cluster/actions/Upgrade.jsx index 50dc4439..bf69bb9c 100644 --- a/src/pages/cluster/containers/Cluster/actions/Upgrade.jsx +++ b/src/pages/cluster/containers/Cluster/actions/Upgrade.jsx @@ -15,6 +15,7 @@ */ import React from 'react'; import { observer } from 'mobx-react'; +import { toJS } from 'mobx'; import { ModalAction } from 'containers/Action'; import { rootStore } from 'stores'; import styles from './index.less'; @@ -24,7 +25,6 @@ import { checkExpired, isDisableByProviderType, } from 'utils'; -import { isIPv4, isDomain, isDomainPath, isIpPort } from 'utils/validate'; @observer export default class Upgrade extends ModalAction { @@ -38,27 +38,31 @@ export default class Upgrade extends ModalAction { async init() { this.store = rootStore.clusterStore; - await this.getVersion(); + this.registryStore = rootStore.registryStore; + await Promise.all([ + this.getVersion(), + this.registryStore.fetchList({ limit: -1 }), + ]); await this.initDefaultValue(); } initDefaultValue = async () => { - const { offline, imageRepository } = this.item; + const { offline, imageRegistry } = this.item; this.setState({ offline, targetVersions: this.getMetaVersion(offline), - imageRepository, + imageRegistry, }); this.updateDefaultValue(); }; get defaultValue() { - const { offline, imageRepository = 'registry.k8s.io' } = this.state; + const { offline, imageRegistry = '' } = this.state; const versions = this.getMetaVersion(offline); return { offline, version: versions[0]?.value, - imageRepository, + imageRegistry, }; } @@ -82,11 +86,11 @@ export default class Upgrade extends ModalAction { handleImgType = async (offline) => { const versions = this.getMetaVersion(offline); - const imageRepository = offline ? '' : 'registry.k8s.io'; + const imageRegistry = ''; await this.setState({ offline, - imageRepository, + imageRegistry, targetVersions: versions, version: versions?.[0]?.value, }); @@ -129,28 +133,14 @@ export default class Upgrade extends ModalAction { })); } - checkk8sRegistry = (rule, value) => { - if (!value) return Promise.resolve(true); - - const checkFunc = (item) => { - if (!item) return true; - if ( - isDomain(item) || - isDomainPath(item) || - isIPv4(item) || - isIpPort(item) - ) { - return true; - } - return false; - }; - - if (!checkFunc(value)) { - return Promise.reject(t('Please enter a legal registry')); - } - - return Promise.resolve(true); - }; + get registryOptions() { + return toJS(this.registryStore.list.data || []).map( + ({ name, host, scheme }) => ({ + value: name, + label: `${name} (${scheme}://${host})`, + }) + ); + } get isOffLine() { const { offline } = this.state; @@ -204,15 +194,15 @@ export default class Upgrade extends ModalAction { ], }, { - name: 'imageRepository', - label: t('Image Repository'), - type: 'input', - placeholder: t('Please input image repository'), + name: 'imageRegistry', + label: t('Image Registry'), + type: 'select', + options: this.registryOptions, hidden: !this.isOffLine, required: this.isOffLine, - maxLength: 256, - tip: t('TIP_CLUSTER_IMAGE_REPOSITORY'), - validator: this.checkk8sRegistry, + tip: t( + 'Select a configured Registry resource. Its address and access settings will be used to pull offline installation images, and components inherit this Registry by default.' + ), }, ]; } diff --git a/src/pages/cluster/containers/Template/cluster/actions/Create/Cluster.jsx b/src/pages/cluster/containers/Template/cluster/actions/Create/Cluster.jsx index d43a0f1a..862c5451 100644 --- a/src/pages/cluster/containers/Template/cluster/actions/Create/Cluster.jsx +++ b/src/pages/cluster/containers/Template/cluster/actions/Create/Cluster.jsx @@ -14,6 +14,7 @@ * limitations under the License. */ import { observer } from 'mobx-react'; +import { toJS } from 'mobx'; import BaseForm from 'components/Form'; import { rootStore } from 'stores'; import { fqdn } from 'utils/regex'; @@ -21,8 +22,6 @@ import { isIPv4, isIpv6, isDomain, - isDomainPath, - isIpPort, } from 'utils/validate'; import { clusterParams, @@ -44,21 +43,26 @@ const { nodeStore, regionStore, clusterStore, + registryStore, backupPointStore, templatesStore, } = rootStore; @observer export default class Cluster extends BaseForm { - init() { + async init() { this.store = clusterStore; this.nodeStore = nodeStore; this.regionStore = regionStore; + this.registryStore = registryStore; this.backupPointStore = backupPointStore; this.templatesStore = templatesStore; - this.getBackupPoint(); - this.getVersion(); + await Promise.all([ + this.getBackupPoint(), + this.registryStore.fetchList({ limit: -1 }), + this.getVersion(), + ]); } async getVersion() { @@ -115,11 +119,11 @@ export default class Cluster extends BaseForm { const versions = isOffLine ? this.offlineVersions : this.onlineVersions; const [firstVersion] = versions; const currentVersions = this.getCurrentVersionsByK8s(firstVersion); - const imageRepository = isOffLine ? '' : 'registry.k8s.io'; + const imageRegistry = ''; await this.updateContext({ offline: isOffLine, - imageRepository, + imageRegistry, kubernetesVersions: versions, kubernetesVersion: firstVersion?.value, ...currentVersions, @@ -129,7 +133,7 @@ export default class Cluster extends BaseForm { kubernetesVersion: firstVersion?.value, ...currentVersions, }); - this.updateFormValue('imageRepository', imageRepository); + this.updateFormValue('imageRegistry', imageRegistry); }; updateFormVersions(versions) { @@ -203,6 +207,13 @@ export default class Cluster extends BaseForm { return options; } + get registryOptions() { + return toJS(this.registryStore.list.data || []).map(({ name, host, scheme }) => ({ + value: name, + label: `${name} (${scheme}://${host})`, + })); + } + getCurrentVersionsByK8s = (kubernetesVersion) => { const cri = get(kubernetesVersion, 'version_control.cri', []); const cni = get(kubernetesVersion, 'version_control.cni', []); @@ -334,28 +345,10 @@ export default class Cluster extends BaseForm { return Promise.resolve(true); }; - checkk8sRegistry = (rule, value) => { - if (!value) return Promise.resolve(true); - - const checkFunc = (item) => { - if (!item) return true; - if ( - isDomain(item) || - isDomainPath(item) || - isIPv4(item) || - isIpPort(item) - ) { - return true; - } - return false; - }; - - if (!checkFunc(value)) { - return Promise.reject(t('Please enter a legal registry')); - } - - return Promise.resolve(true); - }; + checkImageRegistry = (rule, value) => + !value && this.isOffLine + ? Promise.reject(t('Please select a Registry resource')) + : Promise.resolve(true); checkLabels = (rule, value = []) => { const checkFunc = (item) => { @@ -463,15 +456,14 @@ export default class Cluster extends BaseForm { onChange: this.handleImgType, }, { - name: 'imageRepository', - label: t('Image Repository'), - type: 'input', - placeholder: t('Please input image repository'), - maxLength: 256, - validator: this.checkk8sRegistry, + name: 'imageRegistry', + label: t('Image Registry'), + type: 'select', + options: this.registryOptions, + validator: this.checkImageRegistry, required: this.isOffLine, tip: t( - "The registry for image of k8s's own components, if not input, the built-in image will be used." + 'Select a configured Registry resource for image pulls. If none is selected, online installation uses the official registries.' ), }, { diff --git a/src/pages/cluster/containers/Template/cluster/actions/Create/Confirm.jsx b/src/pages/cluster/containers/Template/cluster/actions/Create/Confirm.jsx index c94c89f3..490a8c9c 100644 --- a/src/pages/cluster/containers/Template/cluster/actions/Create/Confirm.jsx +++ b/src/pages/cluster/containers/Template/cluster/actions/Create/Confirm.jsx @@ -211,7 +211,7 @@ export class ConfirmStep extends BaseForm { label: t('Image Type'), value: offline ? t('Offline') : t('Online'), }, - ...this.notFilled(t('Image Repository'), 'imageRepository'), + ...this.notFilled(t('Image Registry'), 'imageRegistry'), { label: t('K8S Version'), value: kubernetesVersion, diff --git a/src/pages/cluster/containers/Template/cluster/actions/Create/index.jsx b/src/pages/cluster/containers/Template/cluster/actions/Create/index.jsx index 408f1cc2..d52ad8a2 100644 --- a/src/pages/cluster/containers/Template/cluster/actions/Create/index.jsx +++ b/src/pages/cluster/containers/Template/cluster/actions/Create/index.jsx @@ -163,7 +163,7 @@ export default class Create extends StepAction { /* step2: Cluster config */ // image offline, - imageRepository, + imageRegistry, certSANs, etcdDataDir, kubeletDataDir, @@ -227,7 +227,7 @@ export default class Create extends StepAction { certSANs: arrayInputValue(certSANs), externalCaCert: externalCA ? externalCaCert : '', externalCaKey: externalCA ? externalCaKey : '', - imageRepository, + imageRegistry, workerNodeVip, kubernetesVersion, containerRuntime: { diff --git a/src/resources/cluster.jsx b/src/resources/cluster.jsx index 1af31a07..a85e53ba 100644 --- a/src/resources/cluster.jsx +++ b/src/resources/cluster.jsx @@ -23,7 +23,7 @@ import moment from 'moment'; export const clusterParams = { offline: true, - imageRepository: '', + imageRegistry: '', etcdDataDir: '/var/lib/etcd', kubeletDataDir: '/var/lib/kubelet', containerRuntimeType: 'containerd', diff --git a/src/utils/object.mapper.js b/src/utils/object.mapper.js index 73026a11..97a09693 100644 --- a/src/utils/object.mapper.js +++ b/src/utils/object.mapper.js @@ -191,9 +191,9 @@ const ClusterTemplateMapper = (item) => { externalIP, backupPoint, certSANs: get(config, 'certSANs'), - imageRepository: get( + imageRegistry: get( config, - 'imageRepository', + 'imageRegistry', get(config, 'offline') ? '' : 'registry.k8s.io' ), workerNodeVip: get(config, 'workerNodeVip'), From ab9484f67853152a26f1410fed0189773e7b5ce4 Mon Sep 17 00:00:00 2001 From: lixd Date: Wed, 22 Jul 2026 14:48:33 +0800 Subject: [PATCH 3/3] fix: preserve empty template image registry Signed-off-by: lixd --- src/utils/object.mapper.js | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/src/utils/object.mapper.js b/src/utils/object.mapper.js index 97a09693..5f4ea744 100644 --- a/src/utils/object.mapper.js +++ b/src/utils/object.mapper.js @@ -191,11 +191,7 @@ const ClusterTemplateMapper = (item) => { externalIP, backupPoint, certSANs: get(config, 'certSANs'), - imageRegistry: get( - config, - 'imageRegistry', - get(config, 'offline') ? '' : 'registry.k8s.io' - ), + imageRegistry: get(config, 'imageRegistry', ''), workerNodeVip: get(config, 'workerNodeVip'), kubernetesVersion, containerRuntimeType,