Skip to content

Commit 4f9a31b

Browse files
authored
Merge pull request #9242 from zexi/automated-cherry-pick-of-#9240-upstream-master
Automated cherry pick of #9240: feat(k8s/compute): add custom registry input and credential handling
2 parents e6e5364 + 561807f commit 4f9a31b

File tree

12 files changed

+267
-77
lines changed

12 files changed

+267
-77
lines changed

containers/Compute/sections/MirrorRegistry/index.vue

Lines changed: 93 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -10,42 +10,58 @@
1010
:value="registry"
1111
showSearch
1212
:filterOption="filterOption"
13+
optionLabelProp="label"
1314
@change="handleRegistryChange">
14-
<a-select-option value="">{{ $t('common.tips.select', [$t('compute.eci.repo.image.registry')]) }}</a-select-option>
15+
<a-select-option value="" label="">{{ $t('common.tips.select', [$t('compute.eci.repo.image.registry')]) }}</a-select-option>
1516
<a-select-option
1617
v-for="cr in registrys"
1718
:key="cr.value"
18-
:value="cr.value">{{ cr.label }}</a-select-option>
19-
</a-select>
20-
</a-col>
21-
<a-col :span="colSpan">
22-
<a-select
23-
:loading="imageLoading"
24-
:value="image"
25-
showSearch
26-
:filterOption="filterOption"
27-
@change="handleImageChange">
28-
<a-select-option value="">{{ $t('common.tips.select', [$t('compute.pod-image')]) }}</a-select-option>
29-
<a-select-option
30-
v-for="cr in images"
31-
:key="cr.value"
32-
:value="cr.value">{{ cr.label }}</a-select-option>
33-
</a-select>
34-
</a-col>
35-
<a-col :span="colSpan">
36-
<a-select
37-
:loading="tagLoading"
38-
:value="tag"
39-
showSearch
40-
:filterOption="filterOption"
41-
@change="handleTagChange">
42-
<a-select-option value="">{{ $t('common.tips.select', [$t('compute.repo.image.tag')]) }}</a-select-option>
43-
<a-select-option
44-
v-for="cr in tags"
45-
:key="cr.value"
46-
:value="cr.value">{{ cr.label }}</a-select-option>
19+
:value="cr.value"
20+
:label="cr.label">
21+
<div>{{ cr.label }}</div>
22+
<div style="font-size: 12px; color: #999;">{{ cr.url }}</div>
23+
</a-select-option>
4724
</a-select>
4825
</a-col>
26+
<template v-if="isCustomRegistry">
27+
<a-col :span="16">
28+
<a-input
29+
:value="customImageUrl"
30+
:placeholder="$t('k8s.repo.image.custom.placeholder')"
31+
@change="handleCustomImageUrlChange" />
32+
<div v-if="previewImage" style="font-size: 12px; color: #999; margin-top: 4px;">{{ previewImage }}</div>
33+
</a-col>
34+
</template>
35+
<template v-else>
36+
<a-col :span="colSpan">
37+
<a-select
38+
:loading="imageLoading"
39+
:value="image"
40+
showSearch
41+
:filterOption="filterOption"
42+
@change="handleImageChange">
43+
<a-select-option value="">{{ $t('common.tips.select', [$t('compute.pod-image')]) }}</a-select-option>
44+
<a-select-option
45+
v-for="cr in images"
46+
:key="cr.value"
47+
:value="cr.value">{{ cr.label }}</a-select-option>
48+
</a-select>
49+
</a-col>
50+
<a-col :span="colSpan">
51+
<a-select
52+
:loading="tagLoading"
53+
:value="tag"
54+
showSearch
55+
:filterOption="filterOption"
56+
@change="handleTagChange">
57+
<a-select-option value="">{{ $t('common.tips.select', [$t('compute.repo.image.tag')]) }}</a-select-option>
58+
<a-select-option
59+
v-for="cr in tags"
60+
:key="cr.value"
61+
:value="cr.value">{{ cr.label }}</a-select-option>
62+
</a-select>
63+
</a-col>
64+
</template>
4965
</a-row>
5066
</template>
5167

@@ -75,8 +91,23 @@ export default {
7591
tag: '',
7692
tags: [],
7793
colSpan: 8,
94+
customImageUrl: '',
7895
}
7996
},
97+
computed: {
98+
isCustomRegistry () {
99+
const cur = this.registrys.find(o => o.value === this.registry)
100+
return cur?.type === 'custom'
101+
},
102+
previewImage () {
103+
const cur = this.registrys.find(o => o.value === this.registry)
104+
const url = cur?.url
105+
if (url && this.customImageUrl) {
106+
return `${this.stripProtocol(url)}/${this.customImageUrl}`
107+
}
108+
return ''
109+
},
110+
},
80111
watch: {
81112
registry (val) {
82113
if (!val) {
@@ -96,14 +127,27 @@ export default {
96127
handleRegistryChange (val) {
97128
if (val) {
98129
this.registry = val
99-
this.getImagesByRegistryId(val)
130+
this.customImageUrl = ''
131+
const cur = this.registrys.find(o => o.value === val)
132+
this.$emit('credential-change', cur?.credential_id || '')
133+
if (cur?.type === 'custom') {
134+
this.image = ''
135+
this.images = []
136+
this.tag = ''
137+
this.tags = []
138+
this.$emit('change', '')
139+
} else {
140+
this.getImagesByRegistryId(val)
141+
}
100142
} else {
101143
this.registry = ''
102144
this.image = ''
103145
this.images = []
104146
this.tag = ''
105147
this.tags = []
148+
this.customImageUrl = ''
106149
this.$emit('change', '')
150+
this.$emit('credential-change', '')
107151
}
108152
},
109153
async getRegistrys () {
@@ -112,14 +156,16 @@ export default {
112156
this.registryLoading = true
113157
this.registry = ''
114158
this.registrys = []
115-
const params = { details: true, limit: 20, offset: 0, $t: uuid() }
159+
const params = { details: true, limit: 20, offset: 0, scope: this.$store.getters.scope, $t: uuid() }
116160
const result = await manager.list({ params })
117161
const dataArr = result.data.data || []
118162
this.registrys = dataArr.map(item => {
119163
return {
120164
label: item.name,
121165
value: item.id,
122166
url: item.url,
167+
type: item.type,
168+
credential_id: item.credential_id,
123169
}
124170
})
125171
if (this.isDefaultSelect && this.registrys?.length > 0) {
@@ -203,11 +249,25 @@ export default {
203249
this.tagLoading = false
204250
}
205251
},
252+
stripProtocol (url) {
253+
return url.replace(/^https?:\/\//, '')
254+
},
255+
handleCustomImageUrlChange (e) {
256+
const val = e.target.value
257+
this.customImageUrl = val
258+
const curRegistry = this.registrys.find(o => o.value === this.registry)
259+
const url = curRegistry?.url
260+
if (url && val) {
261+
this.$emit('change', `${this.stripProtocol(url)}/${val}`)
262+
} else {
263+
this.$emit('change', '')
264+
}
265+
},
206266
triggerChange (registry, image, tag) {
207267
const curRegistry = this.registrys.find(o => o.value === registry)
208268
const url = curRegistry?.url
209269
if (url && image && tag) {
210-
this.$emit('change', `${url}/${image}:${tag}`)
270+
this.$emit('change', `${this.stripProtocol(url)}/${image}:${tag}`)
211271
} else {
212272
this.$emit('change', '')
213273
}

containers/Compute/sections/SpecContainer/Form.vue

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,8 @@
1717
:placeholder="$t('common.tips.input', [$t('compute.repo.container_image')])" />
1818
</a-form-item>
1919
<a-form-item v-else :label="$t('compute.repo.container_image')">
20-
<mirror-registry v-decorator="decorators.registryImage" />
20+
<mirror-registry v-decorator="decorators.registryImage" @credential-change="handleCredentialChange" />
21+
<a-input v-show="false" v-decorator="decorators.imageCredentialId" />
2122
</a-form-item>
2223
<a-form-item label="CPU" v-show="false">
2324
<a-input
@@ -140,6 +141,13 @@ export default {
140141
handleSourceChange (e) {
141142
this.source = e.target.value
142143
},
144+
handleCredentialChange (credentialId) {
145+
if (this.form && this.form.fc && this.decorators.imageCredentialId) {
146+
this.form.fc.setFieldsValue({
147+
[this.decorators.imageCredentialId[0]]: credentialId,
148+
})
149+
}
150+
},
143151
labelChangeHandle (val) {
144152
this.labelList = val
145153
},

containers/Compute/views/vminstance-container/utils/createServer.js

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -690,6 +690,9 @@ export const createVmDecorators = () => {
690690
],
691691
},
692692
],
693+
imageCredentialId: i => [
694+
`imageCredentialIds[${i}]`,
695+
],
693696
image: i => [
694697
`containerimages[${i}]`,
695698
{
@@ -1221,8 +1224,9 @@ export class GenCreateData {
12211224
const containers = tabKeys.map(k => {
12221225
const pciDevices = (this.fd.pciEnable && this.genPciDevices()) || []
12231226
const image = this.fd.registryImages?.[k] || ''
1227+
const credentialId = this.fd.imageCredentialIds?.[k] || ''
12241228

1225-
return {
1229+
const spec = {
12261230
name: this.fd.containerNames?.[k],
12271231
image: removeHttp(image) || this.fd.containerimages?.[k],
12281232
command: this.fd.containerCommands?.[k]?.split(' '),
@@ -1239,6 +1243,10 @@ export class GenCreateData {
12391243
envs: getEnvs(this.fd.containerEnvNames?.[k], this.fd.containerEnvValues?.[k]),
12401244
volume_mounts: getVolumeMounts(this.fd.containerVolumeMountNames?.[k], this.fd.containerVolumeMountPaths?.[k]),
12411245
}
1246+
if (credentialId) {
1247+
spec.image_credential_id = credentialId
1248+
}
1249+
return spec
12421250
})
12431251

12441252
return {

containers/K8S/locales/en.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,13 @@
431431
"k8s.repo": "warehouse",
432432
"k8s.repo.url": "Warehouse address",
433433
"k8s.repo.url.extra": "The address must have a suffix, indicating the namespace in the corresponding image warehouse, for example: http|https://ip:port/repo",
434+
"k8s.repo.type.common": "Docker Open Source Image Registry",
435+
"k8s.repo.type.custom": "Other Image Registry",
436+
"k8s.repo.url.extra.custom": "Third-party image registry address, e.g. Alibaba Cloud: https://registry.cn-beijing.aliyuncs.com/yunion",
437+
"k8s.repo.credential": "Credential",
438+
"k8s.repo.credential.bindled": "Bindled",
439+
"k8s.repo.credential.none": "None",
440+
"k8s.repo.image.custom.placeholder": "Enter image address, e.g. nginx:latest",
434441
"k8s.repo.image.name": "Image name",
435442
"k8s.repo.image.tag": "Image version",
436443
"k8s.repo.image.tag_empty": "No image version yet",

containers/K8S/locales/ja-JP.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,13 @@
431431
"k8s.repo": "倉庫",
432432
"k8s.repo.url": "倉庫住所",
433433
"k8s.repo.url.extra": "アドレスには、対応するイメージ リポジトリ内の名前空間を示すサフィックスが必要です (例: http|https://ip:port/repo",
434+
"k8s.repo.type.common": "Dockerオープンソースイメージレジストリ",
435+
"k8s.repo.type.custom": "その他のイメージレジストリ",
436+
"k8s.repo.url.extra.custom": "サードパーティのイメージレジストリアドレス、例えばAlibaba Cloud: https://registry.cn-beijing.aliyuncs.com/yunion",
437+
"k8s.repo.credential": "認証情報",
438+
"k8s.repo.credential.bindled": "バインド済み",
439+
"k8s.repo.credential.none": "なし",
440+
"k8s.repo.image.custom.placeholder": "イメージアドレスを入力してください、例: nginx:latest",
434441
"k8s.repo.image.name": "イメージ名",
435442
"k8s.repo.image.tag": "イメージ バージョン",
436443
"k8s.repo.image.tag_empty": "イメージ バージョンはありません",

containers/K8S/locales/zh-CN.json

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -431,6 +431,13 @@
431431
"k8s.repo": "仓库",
432432
"k8s.repo.url": "仓库地址",
433433
"k8s.repo.url.extra": "地址必须要有后缀,表示对应镜像仓库里面的namespace,例如: http|https://ip:port/repo",
434+
"k8s.repo.type.common": "Docker开源镜像仓库",
435+
"k8s.repo.type.custom": "其它镜像仓库",
436+
"k8s.repo.url.extra.custom": "第三方镜像仓库地址,例如阿里云镜像仓库: https://registry.cn-beijing.aliyuncs.com/yunion",
437+
"k8s.repo.credential": "凭证",
438+
"k8s.repo.credential.bindled": "已绑定",
439+
"k8s.repo.credential.none": "",
440+
"k8s.repo.image.custom.placeholder": "请输入镜像地址,例如: nginx:latest",
434441
"k8s.repo.image.name": "镜像名称",
435442
"k8s.repo.image.tag": "镜像版本",
436443
"k8s.repo.image.tag_empty": "暂无镜像版本",

0 commit comments

Comments
 (0)