fix: 解决容器镜像为空导致升级失败的问题 (#3287)

Refs #3278
This commit is contained in:
ssongliu 2023-12-12 15:22:09 +08:00 committed by GitHub
parent 168b6b8667
commit ed6735e610
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 60 additions and 45 deletions

View File

@ -407,6 +407,7 @@ func (u *ContainerService) ContainerInfo(req dto.OperationWithName) (*dto.Contai
}
}
data.AutoRemove = oldContainer.HostConfig.AutoRemove
data.Privileged = oldContainer.HostConfig.Privileged
data.PublishAllPorts = oldContainer.HostConfig.PublishAllPorts
data.RestartPolicy = oldContainer.HostConfig.RestartPolicy.Name
if oldContainer.HostConfig.NanoCPUs != 0 {
@ -440,7 +441,7 @@ func (u *ContainerService) ContainerUpdate(req dto.ContainerOperate) error {
if !req.ForcePull {
return err
}
global.LOG.Errorf("force pull image %s failed, err: %v", req.Image, err)
return fmt.Errorf("pull image %s failed, err: %v", req.Image, err)
}
}
@ -483,7 +484,7 @@ func (u *ContainerService) ContainerUpgrade(req dto.ContainerUpgrade) error {
if !req.ForcePull {
return err
}
global.LOG.Errorf("force pull image %s failed, err: %v", req.Image, err)
return fmt.Errorf("pull image %s failed, err: %v", req.Image, err)
}
}
config := oldContainer.Config

View File

@ -485,31 +485,20 @@ func (u *FirewallService) updatePingStatus(enable string) error {
if err != nil {
return err
}
hasV4Line, hasV6Line := false, false
if _, err := os.Stat("/proc/sys/net/ipv6/icmp_echo_ignore_all"); err != nil {
hasV6Line = true
}
files := strings.Split(string(lineBytes), "\n")
var newFiles []string
hasLine := false
for _, line := range files {
if strings.HasPrefix(strings.ReplaceAll(line, " ", ""), "net/ipv4/icmp_echo_ignore_all") && !hasV4Line {
if strings.Contains(line, "net/ipv4/icmp_echo_ignore_all") || strings.HasPrefix(line, "net/ipv4/icmp_echo_ignore_all") {
newFiles = append(newFiles, "net/ipv4/icmp_echo_ignore_all="+enable)
hasV4Line = true
continue
hasLine = true
} else {
newFiles = append(newFiles, line)
}
if strings.HasPrefix(strings.ReplaceAll(line, " ", ""), "net/ipv6/icmp_echo_ignore_all") && !hasV6Line {
newFiles = append(newFiles, "net/ipv6/icmp_echo_ignore_all="+enable)
hasV6Line = true
continue
}
newFiles = append(newFiles, line)
}
if !hasV4Line {
if !hasLine {
newFiles = append(newFiles, "net/ipv4/icmp_echo_ignore_all="+enable)
}
if !hasV6Line {
newFiles = append(newFiles, "net/ipv6/icmp_echo_ignore_all="+enable)
}
file, err := os.OpenFile(confPath, os.O_WRONLY|os.O_TRUNC, 0666)
if err != nil {
return err
@ -526,6 +515,11 @@ func (u *FirewallService) updatePingStatus(enable string) error {
if err != nil {
return fmt.Errorf("update ping status failed, err: %v", stdout)
}
handle := "-A"
if enable == "1" {
handle = "-D"
}
_, _ = cmd.Execf("%s ip6tables %s INPUT -p icmpv6 --icmpv6-type echo-request -j DROP", cmd.SudoHandleCmd(), handle)
return nil
}

View File

@ -597,7 +597,7 @@ const message = {
'The upgrade operation requires rebuilding the container, and any non-persistent data will be lost. Do you want to continue?',
oldImage: 'Current image',
targetImage: 'Target image',
targetImageHelper: 'Please enter the target image version',
imageLoadErr: 'System did not detect the container image name, please manually enter the full image name:tag ',
appHelper:
'This container is sourced from the application store. Upgrading it may cause the service to be unavailable.',

View File

@ -578,7 +578,7 @@ const message = {
upgradeWarning2: '升級操作需要重建容器任何未持久化的數據將會丟失是否繼續',
oldImage: '當前鏡像',
targetImage: '目標鏡像',
targetImageHelper: '請輸入目標鏡像版本',
imageLoadErr: '系統未檢測到容器的鏡像名稱請手動輸入完整的鏡像名稱:標籤',
appHelper: '該容器來源於應用商店升級可能導致該服務不可用',
input: '手動輸入',

View File

@ -579,7 +579,7 @@ const message = {
upgradeWarning2: '升级操作需要重建容器任何未持久化的数据将会丢失是否继续',
oldImage: '当前镜像',
targetImage: '目标镜像',
targetImageHelper: '请输入目标镜像版本',
imageLoadErr: '系统未检测到容器的镜像名称请手动输入完整的镜像名称:标签',
appHelper: '该容器来源于应用商店升级可能导致该服务不可用',
input: '手动输入',

View File

@ -162,6 +162,7 @@ const acceptParams = (): void => {
form.path = '';
form.file = '';
form.template = null;
onCreating.value = false;
loadTemplates();
loadPath();
};

View File

@ -15,27 +15,33 @@
/>
<el-form @submit.prevent ref="formRef" :model="form" label-position="top">
<el-form-item :label="$t('container.oldImage')" prop="oldImage">
<el-tooltip placement="top-start" :content="form.imageName" v-if="form.imageName.length > 50">
<el-tag>{{ form.imageName.substring(0, 50) }}...:{{ form.oldTag }}</el-tag>
<el-tooltip
placement="top-start"
:content="form.oldImageName"
v-if="form.oldImageName.length > 50"
>
<el-tag>{{ form.oldImageName.substring(0, 50) }}...</el-tag>
</el-tooltip>
<el-tag v-else>{{ form.imageName }}:{{ form.oldTag }}</el-tag>
<el-tag v-else>{{ form.oldImageName }}</el-tag>
</el-form-item>
<el-form-item prop="newTag" :rules="Rules.imageName">
<el-form-item prop="newImageName" :rules="Rules.imageName">
<template #label>
<el-tooltip
placement="top-start"
:content="form.imageName"
v-if="form.imageName.length > 40"
:content="form.imageHelper"
v-if="form.imageHelper.length > 40"
>
<span>
{{ $t('container.targetImage') + ' (' + form.imageName.substring(0, 40) + '...)' }}
{{
$t('container.targetImage') + ' (' + form.imageHelper.substring(0, 40) + '...)'
}}
</span>
</el-tooltip>
<span v-else>
{{ $t('container.targetImage') + ' (' + form.imageName + ')' }}
{{ $t('container.targetImage') + ' (' + form.imageHelper + ')' }}
</span>
</template>
<el-input v-model="form.newTag" :placeholder="$t('container.targetImageHelper')" />
<el-input v-model="form.newImageName" :placeholder="$t('container.imageNameHelper')" />
<span class="input-help">{{ $t('container.upgradeHelper') }}</span>
</el-form-item>
<el-form-item prop="ignoreCompare">
@ -79,9 +85,9 @@ const loading = ref(false);
const form = reactive({
name: '',
imageName: '',
oldTag: '',
newTag: '',
oldImageName: '',
newImageName: '',
imageHelper: '',
fromApp: false,
forcePull: false,
@ -100,11 +106,16 @@ interface DialogProps {
}
const acceptParams = (props: DialogProps): void => {
form.name = props.container;
form.imageName = props.image.indexOf(':') !== -1 ? props.image.split(':')[0] : props.image;
form.oldTag = props.image.indexOf(':') !== -1 ? props.image.split(':')[1] : 'latest';
form.newTag = form.oldTag;
form.oldImageName = props.image;
form.fromApp = props.fromApp;
form.ignoreCompare = false;
if (props.image.indexOf('sha256:') !== -1) {
form.imageHelper = i18n.global.t('container.imageLoadErr');
drawerVisible.value = true;
return;
}
form.imageHelper = props.image.indexOf(':') !== -1 ? props.image.split(':')[0] : props.image;
drawerVisible.value = true;
};
const emit = defineEmits<{ (e: 'search'): void }>();
@ -113,7 +124,7 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
if (!formEl) return;
formEl.validate(async (valid) => {
if (!valid) return;
if (!form.ignoreCompare && !compareVersion(form.newTag, form.oldTag)) {
if (!form.ignoreCompare && !compareVersion(form.newImageName, form.oldImageName)) {
MsgWarning(i18n.global.t('container.upgradeWarning'));
return;
}
@ -122,7 +133,7 @@ const onSubmit = async (formEl: FormInstance | undefined) => {
cancelButtonText: i18n.global.t('commons.button.cancel'),
}).then(async () => {
loading.value = true;
await upgradeContainer(form.name, form.imageName + ':' + form.newTag, form.forcePull)
await upgradeContainer(form.name, form.newImageName, form.forcePull)
.then(() => {
loading.value = false;
emit('search');
@ -141,14 +152,22 @@ const handleClose = async () => {
};
function compareVersion(vNew, vOld) {
if (vNew === 'latest') {
let newImageName = vNew.indexOf(':') !== -1 ? vNew.split(':')[0] : vNew;
let oldImageName = vOld.indexOf(':') !== -1 ? vOld.split(':')[0] : vOld;
if (newImageName !== oldImageName) {
return true;
}
let v1 = vNew
let newTag = vNew.indexOf(':') !== -1 ? vNew.split(':')[1] : 'latest';
if (newTag === 'latest') {
return true;
}
let oldTag = vOld.indexOf(':') !== -1 ? vOld.split(':')[1] : 'latest';
let v1 = newTag
.replace('-', '.')
.replace(/[^\d.]/g, '')
.split('.');
let v2 = vOld
let v2 = oldTag
.replace('-', '.')
.replace(/[^\d.]/g, '')
.split('.');

View File

@ -202,10 +202,10 @@ const handleClose = () => {
const rules = reactive({
name: [Rules.requiredInput],
driver: [Rules.requiredSelect],
subnet: [{ validator: checkCidr, trigger: 'blur' }],
subnet: [{ validator: checkCidr, trigger: 'blur' }, Rules.requiredInput],
gateway: [{ validator: checkGateway, trigger: 'blur' }],
scope: [{ validator: checkCidr, trigger: 'blur' }],
subnetV6: [{ validator: checkFixedCidrV6, trigger: 'blur' }],
subnetV6: [{ validator: checkFixedCidrV6, trigger: 'blur' }, Rules.requiredInput],
gatewayV6: [{ validator: checkGatewayV6, trigger: 'blur' }],
scopeV6: [{ validator: checkFixedCidrV6, trigger: 'blur' }],
});

View File

@ -1,5 +1,5 @@
<template>
<el-drawer :close-on-click-modal="false" v-model="drawerVisible" size="30%">
<el-drawer :close-on-click-modal="false" :destroy-on-close="true" v-model="drawerVisible" size="30%">
<template #header>
<Header :header="$t('toolbox.fail2ban.' + form.operate + 'IP')" :back="handleClose"></Header>
</template>