mirror of
https://gitee.com/dromara/Jpom.git
synced 2024-11-30 02:48:17 +08:00
fix 容器重建
This commit is contained in:
parent
da0f307d2c
commit
eb2818c3fc
@ -27,6 +27,7 @@ import cn.hutool.core.util.ObjectUtil;
|
||||
import cn.hutool.core.util.StrUtil;
|
||||
import cn.keepbx.jpom.plugins.IPlugin;
|
||||
import com.alibaba.fastjson2.JSONObject;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.jpom.common.JsonMessage;
|
||||
import org.dromara.jpom.common.validator.ValidatorItem;
|
||||
import org.dromara.jpom.permission.Feature;
|
||||
@ -47,6 +48,7 @@ import java.util.Map;
|
||||
* @author bwcx_jzy
|
||||
* @since 2022/2/7
|
||||
*/
|
||||
@Slf4j
|
||||
public abstract class BaseDockerContainerController extends BaseDockerController {
|
||||
|
||||
|
||||
@ -212,6 +214,7 @@ public abstract class BaseDockerContainerController extends BaseDockerController
|
||||
|
||||
/**
|
||||
* drop old container and create new container
|
||||
*
|
||||
* @return json
|
||||
*/
|
||||
@PostMapping(value = "rebuild-container", produces = MediaType.APPLICATION_JSON_VALUE)
|
||||
@ -229,7 +232,11 @@ public abstract class BaseDockerContainerController extends BaseDockerController
|
||||
// drop old container
|
||||
if (StrUtil.isNotEmpty(containerId)) {
|
||||
parameter.put("containerId", containerId);
|
||||
plugin.execute("removeContainer", parameter);
|
||||
try {
|
||||
plugin.execute("removeContainer", parameter);
|
||||
} catch (com.github.dockerjava.api.exception.NotFoundException notFoundException) {
|
||||
log.warn(notFoundException.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
// create new container
|
||||
|
@ -41,6 +41,7 @@ import com.github.dockerjava.core.InvocationBuilder;
|
||||
import com.github.dockerjava.core.NameParser;
|
||||
import lombok.Lombok;
|
||||
import lombok.SneakyThrows;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.dromara.jpom.util.StringUtil;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@ -61,6 +62,7 @@ import java.util.stream.Collectors;
|
||||
* @since 2022/1/26
|
||||
*/
|
||||
@PluginConfig(name = "docker-cli")
|
||||
@Slf4j
|
||||
public class DefaultDockerPluginImpl implements IDockerConfigPlugin {
|
||||
|
||||
|
||||
@ -571,7 +573,7 @@ public class DefaultDockerPluginImpl implements IDockerConfigPlugin {
|
||||
dockerClient.removeImageCmd(imageId).withForce(false).exec();
|
||||
successCount++;
|
||||
} catch (Exception e) {
|
||||
|
||||
log.warn("删除容器异常", e);
|
||||
}
|
||||
}
|
||||
failCount = imagesIds.length - successCount;
|
||||
|
@ -1,5 +1,12 @@
|
||||
<template>
|
||||
<div>
|
||||
<a-alert v-if="containerData && Object.keys(containerData).length" message="操作提示" type="warning" show-icon>
|
||||
<template #description>
|
||||
容器重建是指使用已经创建的容器参数重新创建一个相同的容器。
|
||||
<div><b style="color: red">重启创建之前会自动将之前的容器删除掉</b>,如果未挂载容器数据目录请提前备份数据后再使用此功能。</div>
|
||||
<div><b>此功能不能保证新建的容器和之前容器参数完全一致请慎重使用。</b></div>
|
||||
</template>
|
||||
</a-alert>
|
||||
<a-form-model ref="editForm" :rules="rules" :model="temp" :label-col="{ span: 3 }" :wrapper-col="{ span: 20 }">
|
||||
<a-form-model-item label="基础镜像" prop="name">
|
||||
<a-row>
|
||||
@ -310,12 +317,7 @@
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import {
|
||||
dockerImageCreateContainer,
|
||||
dockerImageInspect,
|
||||
dockerInspectContainer,
|
||||
dockerContainerRebuildContainer
|
||||
} from "@/api/docker-api";
|
||||
import { dockerImageCreateContainer, dockerImageInspect, dockerInspectContainer, dockerContainerRebuildContainer } from "@/api/docker-api";
|
||||
export default {
|
||||
props: {
|
||||
id: {
|
||||
@ -351,7 +353,7 @@ export default {
|
||||
{ pattern: /[a-zA-Z0-9][a-zA-Z0-9_.-]$/, message: "容器名称数字字母,且长度大于1", trigger: "blur" },
|
||||
],
|
||||
},
|
||||
}
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
reqDataId() {
|
||||
@ -359,12 +361,12 @@ export default {
|
||||
},
|
||||
getLabels() {
|
||||
if (!this.containerData.labels) {
|
||||
return ""
|
||||
return "";
|
||||
}
|
||||
let labels = ""
|
||||
let labels = "";
|
||||
Object.keys(this.containerData.labels).map((key) => {
|
||||
labels += `${key}=${this.containerData.labels[key]}&`
|
||||
})
|
||||
labels += `${key}=${this.containerData.labels[key]}&`;
|
||||
});
|
||||
return labels.slice(0, -1);
|
||||
},
|
||||
},
|
||||
@ -388,8 +390,8 @@ export default {
|
||||
item.disabled = item.privatePort !== null;
|
||||
item.port = item.privatePort;
|
||||
return item;
|
||||
})
|
||||
return _ports.length > 0 ? _ports : null
|
||||
});
|
||||
return _ports.length > 0 ? _ports : null;
|
||||
},
|
||||
getPortsFromExposedPorts(exposedPorts) {
|
||||
const _ports = exposedPorts.map((item) => {
|
||||
@ -397,44 +399,80 @@ export default {
|
||||
item.ip = "0.0.0.0";
|
||||
item.scheme = item.scheme || "tcp";
|
||||
return item;
|
||||
})
|
||||
return _ports.length > 0 ? _ports : null
|
||||
});
|
||||
return _ports.length > 0 ? _ports : null;
|
||||
},
|
||||
getVolumesFromMounts(mounts) {
|
||||
const _mounts = mounts.map((item) => {
|
||||
item.disabled = item.destination !== null;
|
||||
item.host = item.source;
|
||||
item.container = item.destination;
|
||||
item.container = item.destination?.path;
|
||||
return item;
|
||||
})
|
||||
return _mounts.length > 0 ? _mounts : null
|
||||
});
|
||||
return _mounts.length > 0 ? _mounts : null;
|
||||
},
|
||||
getRestartPolicy(restartPolicy) {
|
||||
if (!restartPolicy) {
|
||||
return "";
|
||||
}
|
||||
const name = restartPolicy.name;
|
||||
if (restartPolicy.maximumRetryCount) {
|
||||
return name + ":" + restartPolicy.maximumRetryCount;
|
||||
}
|
||||
return name;
|
||||
},
|
||||
// inspect container
|
||||
inspectContainer() {
|
||||
// 单独获取 image 信息
|
||||
this.temp = {};
|
||||
dockerImageInspect(this.urlPrefix, {
|
||||
id: this.reqDataId,
|
||||
imageId: this.imageId,
|
||||
}).then((res) => {
|
||||
this.temp.image = (res.data.repoTags || []).join(",")
|
||||
})
|
||||
this.temp = { ...this.temp, image: (res.data.repoTags || []).join(",") };
|
||||
});
|
||||
|
||||
dockerInspectContainer(this.urlPrefix, {
|
||||
id: this.reqDataId,
|
||||
containerId: this.containerId,
|
||||
}).then(res => {
|
||||
}).then((res) => {
|
||||
this.buildVisible = true;
|
||||
const storageOpt = res.data.hostConfig?.storageOpt || { "": "" };
|
||||
|
||||
this.temp = {
|
||||
name: res.data.name,
|
||||
name: res.data?.name,
|
||||
labels: this.getLabels,
|
||||
volumes: this.getVolumesFromMounts(this.containerData.mounts) || [{}],
|
||||
volumes: this.getVolumesFromMounts(res.data?.mounts) || [{}],
|
||||
exposedPorts: this.getPortsFromPorts(this.containerData.ports) || this.getPortsFromExposedPorts(res.data.config.exposedPorts) || [{}],
|
||||
autorun: true,
|
||||
imageId: this.imageId,
|
||||
env: [{}],
|
||||
storageOpt: [{}],
|
||||
commands: [{}],
|
||||
...this.temp
|
||||
env: (res.data?.config?.env || [""]).map((item) => {
|
||||
const i = item.indexOf("=");
|
||||
if (i == -1) {
|
||||
return {};
|
||||
}
|
||||
return {
|
||||
key: item.substring(0, i),
|
||||
value: item.substring(i + 1, item.length),
|
||||
};
|
||||
}) || [{}],
|
||||
storageOpt: Object.keys(storageOpt).map((item) => {
|
||||
return {
|
||||
key: item,
|
||||
value: storageOpt[item],
|
||||
};
|
||||
}),
|
||||
commands: (res.data?.config?.cmd || [""]).map((item) => {
|
||||
return {
|
||||
value: item || "",
|
||||
};
|
||||
}) || [{}],
|
||||
hostname: res.data?.config?.hostName,
|
||||
restartPolicy: this.getRestartPolicy(res.data?.hostConfig?.restartPolicy),
|
||||
networkMode: res.data?.hostConfig?.networkMode,
|
||||
runtime: res.data?.hostConfig?.runtime,
|
||||
privileged: res.data.hostConfig?.privileged || false,
|
||||
...this.temp,
|
||||
};
|
||||
this.$refs["editForm"]?.resetFields();
|
||||
});
|
||||
@ -447,6 +485,7 @@ export default {
|
||||
}).then((res) => {
|
||||
this.buildVisible = true;
|
||||
this.temp = {
|
||||
name: (res.data?.repoTags[0] || "").split(":")[0] || "",
|
||||
volumes: [{}],
|
||||
exposedPorts: (res.data?.config?.exposedPorts || [{}]).map((item) => {
|
||||
item.disabled = item.port !== null;
|
||||
@ -454,7 +493,7 @@ export default {
|
||||
item.scheme = item.scheme || "tcp";
|
||||
return item;
|
||||
}),
|
||||
image: (res.data.repoTags || []).join(","),
|
||||
image: (res.data?.repoTags || []).join(","),
|
||||
autorun: true,
|
||||
imageId: this.imageId,
|
||||
env: [{}],
|
||||
@ -527,7 +566,7 @@ export default {
|
||||
});
|
||||
|
||||
// 通知父组件关闭弹窗
|
||||
this.$emit('confirmBtnClick');
|
||||
this.$emit("confirmBtnClick");
|
||||
}
|
||||
});
|
||||
} else {
|
||||
@ -536,14 +575,14 @@ export default {
|
||||
this.$notification.success({
|
||||
message: res.msg,
|
||||
});
|
||||
|
||||
|
||||
// 通知父组件关闭弹窗
|
||||
this.$emit('confirmBtnClick');
|
||||
this.$emit("confirmBtnClick");
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -383,6 +383,11 @@
|
||||
<a-icon type="more" />
|
||||
</a>
|
||||
<a-menu slot="overlay">
|
||||
<a-menu-item>
|
||||
<a-tooltip title="修改容器配置,重新运行">
|
||||
<a-button size="small" type="link" @click="rebuild(record)"><a-icon type="redo" />重建</a-button>
|
||||
</a-tooltip>
|
||||
</a-menu-item>
|
||||
<a-menu-item>
|
||||
<a-tooltip title="编辑容器的一些基础参数">
|
||||
<a-button size="small" type="link" icon="edit" :disabled="record.state !== 'running'" @click="editContainer(record)">编辑</a-button>
|
||||
@ -455,22 +460,24 @@
|
||||
title="重建容器"
|
||||
:maskClosable="false"
|
||||
>
|
||||
<BuildContainer
|
||||
:id="this.id"
|
||||
<BuildContainer
|
||||
:id="this.id"
|
||||
:imageId="this.temp.imageId"
|
||||
:machineDockerId="this.machineDockerId"
|
||||
:machineDockerId="this.machineDockerId"
|
||||
:urlPrefix="this.urlPrefix"
|
||||
:containerId="this.temp.id"
|
||||
:containerData="this.temp"
|
||||
@cancelBtnClick="
|
||||
() => {
|
||||
this.buildVisible = false;
|
||||
}"
|
||||
}
|
||||
"
|
||||
@confirmBtnClick="
|
||||
() => {
|
||||
this.buildVisible = false;
|
||||
this.loadData();
|
||||
}"
|
||||
}
|
||||
"
|
||||
/>
|
||||
</a-drawer>
|
||||
</div>
|
||||
@ -489,7 +496,7 @@ export default {
|
||||
LogView,
|
||||
Terminal,
|
||||
editContainer,
|
||||
BuildContainer
|
||||
BuildContainer,
|
||||
},
|
||||
props: {
|
||||
id: {
|
||||
@ -571,7 +578,7 @@ export default {
|
||||
start: {
|
||||
msg: "您确定要启动当前容器吗?",
|
||||
api: dockerContainerStart,
|
||||
}
|
||||
},
|
||||
},
|
||||
editVisible: false,
|
||||
|
||||
@ -674,7 +681,7 @@ export default {
|
||||
rebuild(record) {
|
||||
this.temp = Object.assign({}, record);
|
||||
this.buildVisible = true;
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
@ -3,6 +3,7 @@
|
||||
<a-table size="middle" :data-source="list" :columns="columns" :pagination="false" bordered rowKey="id" :row-selection="rowSelection">
|
||||
<template slot="title">
|
||||
<a-space>
|
||||
<!-- <a-input v-model="listQuery['name']" @pressEnter="loadData" placeholder="名称" class="search-input-item" /> -->
|
||||
<div>
|
||||
显示所有
|
||||
<a-switch checked-children="是" un-checked-children="否" v-model="listQuery['showAll']" />
|
||||
@ -71,20 +72,22 @@
|
||||
title="构建容器"
|
||||
:maskClosable="false"
|
||||
>
|
||||
<BuildContainer
|
||||
:id="this.id"
|
||||
<BuildContainer
|
||||
:id="this.id"
|
||||
:imageId="this.temp.id"
|
||||
:machineDockerId="this.machineDockerId"
|
||||
:machineDockerId="this.machineDockerId"
|
||||
:urlPrefix="this.urlPrefix"
|
||||
@cancelBtnClick="
|
||||
() => {
|
||||
this.buildVisible = false;
|
||||
}"
|
||||
}
|
||||
"
|
||||
@confirmBtnClick="
|
||||
() => {
|
||||
this.buildVisible = false;
|
||||
this.loadData();
|
||||
}"
|
||||
}
|
||||
"
|
||||
/>
|
||||
</a-drawer>
|
||||
<!-- 日志 -->
|
||||
@ -95,7 +98,7 @@
|
||||
</template>
|
||||
<script>
|
||||
import { parseTime, renderSize } from "@/utils/const";
|
||||
import {dockerImageCreateContainer, dockerImagePullImage, dockerImageRemove, dockerImagesList, dockerImageBatchRemove} from "@/api/docker-api";
|
||||
import { dockerImageCreateContainer, dockerImagePullImage, dockerImageRemove, dockerImagesList, dockerImageBatchRemove } from "@/api/docker-api";
|
||||
import PullImageLog from "@/pages/docker/pull-image-log";
|
||||
import BuildContainer from "./buildContainer.vue";
|
||||
|
||||
@ -162,7 +165,7 @@ export default {
|
||||
},
|
||||
},
|
||||
buildVisible: false,
|
||||
tableSelections: []
|
||||
tableSelections: [],
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
@ -321,7 +324,7 @@ export default {
|
||||
});
|
||||
},
|
||||
batchDelete() {
|
||||
let ids = this.tableSelections
|
||||
let ids = this.tableSelections;
|
||||
this.$confirm({
|
||||
title: "系统提示",
|
||||
content: "真的要批量删除选择的镜像吗?已经被容器使用的镜像无法删除!",
|
||||
@ -331,7 +334,7 @@ export default {
|
||||
// 组装参数
|
||||
const params = {
|
||||
id: this.reqDataId,
|
||||
imagesIds: ids.join(','),
|
||||
imagesIds: ids.join(","),
|
||||
};
|
||||
dockerImageBatchRemove(this.urlPrefix, params).then((res) => {
|
||||
if (res.code === 200) {
|
||||
@ -343,7 +346,7 @@ export default {
|
||||
});
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
Loading…
Reference in New Issue
Block a user