From 2e1e4aaaf49cb2835ca03835e33a73dbe34282dd Mon Sep 17 00:00:00 2001 From: bwcx_jzy Date: Mon, 14 Feb 2022 12:35:40 +0800 Subject: [PATCH] =?UTF-8?q?feat(add)=20=E6=96=B0=E5=A2=9E=20docker=20swarm?= =?UTF-8?q?=20=E9=9B=86=E7=BE=A4=E7=AE=A1=E7=90=86?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- CHANGELOG.md | 2 + .../docker/DockerInfoController.java | 6 +- .../docker/DockerSwarmInfoController.java | 113 +++++++++------ .../service/docker/DockerInfoService.java | 43 ++++++ .../io/jpom/DefaultDockerSwarmPluginImpl.java | 63 ++++++++- .../docker-cli/src/test/java/TestSwarm.java | 27 +++- web-vue/src/api/docker-swarm.js | 24 ++++ web-vue/src/pages/docker/list.vue | 22 +-- web-vue/src/pages/docker/swarm/list.vue | 7 +- web-vue/src/pages/docker/swarm/node.vue | 132 +++++++++++++----- 10 files changed, 331 insertions(+), 108 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 62407d8a8..a39f00aba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ 1. nginx 删除操作新增还原配置 2. 【server】新增用户登录日志记录 3. 【server】log view 新增正则参考 +4. 【server】docker 控制台新增 docker 信息查看 +5. 【server】新增 docker swarm 集群管理 ### 🐞 解决BUG、优化功能 diff --git a/modules/server/src/main/java/io/jpom/controller/docker/DockerInfoController.java b/modules/server/src/main/java/io/jpom/controller/docker/DockerInfoController.java index f4f3521a7..985073ef7 100644 --- a/modules/server/src/main/java/io/jpom/controller/docker/DockerInfoController.java +++ b/modules/server/src/main/java/io/jpom/controller/docker/DockerInfoController.java @@ -275,11 +275,7 @@ public class DockerInfoController extends BaseServerController { parameter.put("force", true); plugin.execute("leaveSwarm", parameter, JSONObject.class); // - DockerInfoModel update = new DockerInfoModel(); - update.setId(id); - update.setSwarmId(StrUtil.EMPTY); - update.setSwarmNodeId(StrUtil.EMPTY); - dockerInfoService.update(update); + dockerInfoService.unbind(id); return new JsonMessage<>(200, "强制解绑成功"); } } diff --git a/modules/server/src/main/java/io/jpom/controller/docker/DockerSwarmInfoController.java b/modules/server/src/main/java/io/jpom/controller/docker/DockerSwarmInfoController.java index 63c695802..16e0aa9a1 100644 --- a/modules/server/src/main/java/io/jpom/controller/docker/DockerSwarmInfoController.java +++ b/modules/server/src/main/java/io/jpom/controller/docker/DockerSwarmInfoController.java @@ -2,7 +2,6 @@ package io.jpom.controller.docker; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.util.ArrayUtil; -import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.db.Entity; import cn.jiangzeyin.common.JsonMessage; @@ -25,7 +24,10 @@ import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import javax.servlet.http.HttpServletRequest; -import java.util.*; +import java.util.Date; +import java.util.List; +import java.util.Map; +import java.util.Optional; import java.util.function.Consumer; /** @@ -104,7 +106,7 @@ public class DockerSwarmInfoController extends BaseServerController { DockerSwarmInfoMode dockerSwarmInfoMode = infoModeBuilder.build(); consumer.accept(dockerSwarmInfoMode); // 更新 docker id - this.bindDockerSwarm(dockerInfoModel1, tag, null, id); + dockerInfoService.bindDockerSwarm(dockerInfoModel1, tag, null, id); } private void check(String id, String swarmId) { @@ -144,26 +146,6 @@ public class DockerSwarmInfoController extends BaseServerController { return JsonMessage.getString(200, "修改成功"); } - private void bindDockerSwarm(DockerInfoModel joinSwarmDocker, String tag, JSONObject swarm, String swarmId) { - DockerInfoModel dockerInfoModel = new DockerInfoModel(); - dockerInfoModel.setSwarmId(swarmId); - // - if (swarm != null) { - String swarmNodeId = swarm.getString("nodeID"); - dockerInfoModel.setSwarmNodeId(swarmNodeId); - } - dockerInfoModel.setId(joinSwarmDocker.getId()); - String tags = joinSwarmDocker.getTags(); - // 处理标签 - List allTag = StrUtil.splitTrim(tags, StrUtil.COMMA); - allTag = ObjectUtil.defaultIfNull(allTag, new ArrayList<>()); - if (!allTag.contains(tag)) { - allTag.add(tag); - } - dockerInfoModel.setTags(CollUtil.join(allTag, StrUtil.COMMA)); - dockerInfoService.update(dockerInfoModel); - } - /** * 加入集群 * @@ -210,7 +192,7 @@ public class DockerSwarmInfoController extends BaseServerController { Assert.state(optional.isPresent(), "当前 docker 已经加入到其他集群啦"); // 绑定数据 String managerId = managerSwarmInfo.getString("id"); - this.bindDockerSwarm(joinSwarmDocker, swarmInfoMode1.getTag(), swarm, managerId); + dockerInfoService.bindDockerSwarm(joinSwarmDocker, swarmInfoMode1.getTag(), swarm, managerId); return JsonMessage.getString(200, "集群绑定成功"); } } @@ -231,7 +213,7 @@ public class DockerSwarmInfoController extends BaseServerController { JSONObject swarm = info.getJSONObject("swarm"); Assert.notNull(swarm, "获取 docker 集群信息失败:-1"); String managerId = managerSwarmInfo.getString("id"); - this.bindDockerSwarm(joinSwarmDocker, swarmInfoMode1.getTag(), swarm, managerId); + dockerInfoService.bindDockerSwarm(joinSwarmDocker, swarmInfoMode1.getTag(), swarm, managerId); return JsonMessage.getString(200, "集群创建成功"); } @@ -265,14 +247,9 @@ public class DockerSwarmInfoController extends BaseServerController { public JsonMessage> nodeList( @ValidatorItem String id, String nodeId, String nodeName, String nodeRole) throws Exception { - HttpServletRequest request = getRequest(); - DockerSwarmInfoMode swarmInfoMode1 = dockerSwarmInfoService.getByKey(id, request); - Assert.notNull(swarmInfoMode1, "没有对应的集群"); - DockerInfoModel managerSwarmDocker = dockerInfoService.getByKey(swarmInfoMode1.getDockerId(), request); - Assert.notNull(managerSwarmDocker, "对应的 docker 不存在"); // IPlugin plugin = PluginFactory.getPlugin(DockerSwarmInfoService.DOCKER_PLUGIN_NAME); - Map map = managerSwarmDocker.toParameter(); + Map map = this.getPluginMap(id); map.put("id", nodeId); map.put("name", nodeName); map.put("role", nodeRole); @@ -280,19 +257,65 @@ public class DockerSwarmInfoController extends BaseServerController { return new JsonMessage<>(200, "", listSwarmNodes); } + private Map getPluginMap(String id) { + HttpServletRequest request = getRequest(); + DockerSwarmInfoMode swarmInfoMode1 = dockerSwarmInfoService.getByKey(id, request); + Assert.notNull(swarmInfoMode1, "没有对应的集群"); + // + DockerInfoModel managerSwarmDocker = dockerInfoService.getByKey(swarmInfoMode1.getDockerId(), request); + Assert.notNull(managerSwarmDocker, "对应的 docker 不存在"); + return managerSwarmDocker.toParameter(); + } -// /** -// * 退出集群 -// * -// * @param id 集群ID -// * @return json -// */ -// @GetMapping(value = "leave", produces = MediaType.APPLICATION_JSON_VALUE) -// @Feature(method = MethodFeature.DEL) -// public JsonMessage leave(@ValidatorItem String id) { -// HttpServletRequest request = getRequest(); -// DockerSwarmInfoMode swarmInfoMode1 = dockerSwarmInfoService.getByKey(id, request); -// Assert.notNull(swarmInfoMode1, "没有对应的集群"); -// return new JsonMessage<>(200, "解绑成功"); -// } + /** + * 修改节点信息 + * + * @param id 集群ID + * @return json + */ + @PostMapping(value = "update", produces = MediaType.APPLICATION_JSON_VALUE) + @Feature(method = MethodFeature.EXECUTE) + public JsonMessage update(@ValidatorItem String id, @ValidatorItem String nodeId, @ValidatorItem String availability, @ValidatorItem String role) throws Exception { + // + IPlugin plugin = PluginFactory.getPlugin(DockerSwarmInfoService.DOCKER_PLUGIN_NAME); + Map map = this.getPluginMap(id); + map.put("nodeId", nodeId); + map.put("availability", availability); + map.put("role", role); + plugin.execute("updateSwarmNode", map); + return new JsonMessage<>(200, "解绑成功"); + } + + /** + * 退出集群 + * + * @param id 集群ID + * @return json + */ + @GetMapping(value = "leave", produces = MediaType.APPLICATION_JSON_VALUE) + @Feature(method = MethodFeature.DEL) + public JsonMessage leave(@ValidatorItem String id, @ValidatorItem String nodeId) throws Exception { + // + IPlugin plugin = PluginFactory.getPlugin(DockerSwarmInfoService.DOCKER_PLUGIN_NAME); + // 查询节点信息 + DockerInfoModel dockerInfoModel = new DockerInfoModel(); + dockerInfoModel.setSwarmNodeId(nodeId); + List dockerInfoModels = dockerInfoService.queryList(dockerInfoModel, 2); + Assert.state(CollUtil.size(dockerInfoModels) == 1, "当前节点未在系统中绑定或者绑定信息异常,不能操作"); + // + { + DockerInfoModel dockerInfoModel1 = CollUtil.getFirst(dockerInfoModels); + Map parameter = dockerInfoModel1.toParameter(); + parameter.put("force", true); + plugin.execute("leaveSwarm", parameter, JSONObject.class); + } + { + Map map = this.getPluginMap(id); + map.put("nodeId", nodeId); + plugin.execute("removeSwarmNode", map); + } + // + dockerInfoService.unbind(id); + return new JsonMessage<>(200, "剔除成功"); + } } diff --git a/modules/server/src/main/java/io/jpom/service/docker/DockerInfoService.java b/modules/server/src/main/java/io/jpom/service/docker/DockerInfoService.java index e7b6e6159..513bd5cb6 100644 --- a/modules/server/src/main/java/io/jpom/service/docker/DockerInfoService.java +++ b/modules/server/src/main/java/io/jpom/service/docker/DockerInfoService.java @@ -25,6 +25,7 @@ package io.jpom.service.docker; import cn.hutool.core.collection.CollUtil; import cn.hutool.core.date.SystemClock; import cn.hutool.core.thread.ThreadUtil; +import cn.hutool.core.util.ObjectUtil; import cn.hutool.core.util.StrUtil; import cn.hutool.cron.task.Task; import cn.hutool.db.Entity; @@ -40,6 +41,7 @@ import io.jpom.service.h2db.BaseWorkspaceService; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -181,4 +183,45 @@ public class DockerInfoService extends BaseWorkspaceService imp String sql = StrUtil.format("SELECT * FROM {} where workspaceId=? and instr(tags,?)", super.getTableName()); return (int) super.count(sql, workspaceId, tag); } + + /** + * docker 绑定集群 + * + * @param joinSwarmDocker docker + * @param tag 标签 + * @param swarm 集群信息 + * @param swarmId 集群ID + */ + public void bindDockerSwarm(DockerInfoModel joinSwarmDocker, String tag, JSONObject swarm, String swarmId) { + DockerInfoModel dockerInfoModel = new DockerInfoModel(); + dockerInfoModel.setSwarmId(swarmId); + // + if (swarm != null) { + String swarmNodeId = swarm.getString("nodeID"); + dockerInfoModel.setSwarmNodeId(swarmNodeId); + } + dockerInfoModel.setId(joinSwarmDocker.getId()); + String tags = joinSwarmDocker.getTags(); + // 处理标签 + List allTag = StrUtil.splitTrim(tags, StrUtil.COMMA); + allTag = ObjectUtil.defaultIfNull(allTag, new ArrayList<>()); + if (!allTag.contains(tag)) { + allTag.add(tag); + } + dockerInfoModel.setTags(CollUtil.join(allTag, StrUtil.COMMA)); + this.update(dockerInfoModel); + } + + /** + * 解绑集群信息 + * + * @param id docker id + */ + public void unbind(String id) { + DockerInfoModel update = new DockerInfoModel(); + update.setId(id); + update.setSwarmId(StrUtil.EMPTY); + update.setSwarmNodeId(StrUtil.EMPTY); + this.update(update); + } } diff --git a/modules/sub-plugin/docker-cli/src/main/java/io/jpom/DefaultDockerSwarmPluginImpl.java b/modules/sub-plugin/docker-cli/src/main/java/io/jpom/DefaultDockerSwarmPluginImpl.java index 6689d3a86..3a4ab93c6 100644 --- a/modules/sub-plugin/docker-cli/src/main/java/io/jpom/DefaultDockerSwarmPluginImpl.java +++ b/modules/sub-plugin/docker-cli/src/main/java/io/jpom/DefaultDockerSwarmPluginImpl.java @@ -22,20 +22,21 @@ */ package io.jpom; +import cn.hutool.core.collection.CollUtil; import cn.hutool.core.convert.Convert; import cn.hutool.core.io.IoUtil; +import cn.hutool.core.util.EnumUtil; +import cn.hutool.core.util.ReflectUtil; import cn.hutool.core.util.StrUtil; import com.alibaba.fastjson.JSONObject; import com.github.dockerjava.api.DockerClient; -import com.github.dockerjava.api.command.JoinSwarmCmd; -import com.github.dockerjava.api.command.LeaveSwarmCmd; -import com.github.dockerjava.api.command.ListSwarmNodesCmd; -import com.github.dockerjava.api.model.Swarm; -import com.github.dockerjava.api.model.SwarmNode; -import com.github.dockerjava.api.model.SwarmSpec; +import com.github.dockerjava.api.command.*; +import com.github.dockerjava.api.model.*; +import com.github.dockerjava.core.command.RemoveSwarmNodeCmdImpl; import io.jpom.plugin.IDefaultPlugin; import io.jpom.plugin.PluginConfig; import lombok.extern.slf4j.Slf4j; +import org.springframework.util.Assert; import java.util.List; import java.util.Map; @@ -67,12 +68,62 @@ public class DefaultDockerSwarmPluginImpl implements IDefaultPlugin { case "leaveSwarm": this.leaveSwarmCmd(parameter); return null; + case "updateSwarmNode": + this.updateSwarmNodeCmd(parameter); + return null; + case "removeSwarmNode": + this.removeSwarmNodeCmd(parameter); + return null; default: break; } return null; } + private void removeSwarmNodeCmd(Map parameter) { + DockerClient dockerClient = DockerUtil.build(parameter); + try { + DockerCmdExecFactory dockerCmdExecFactory = (DockerCmdExecFactory) ReflectUtil.getFieldValue(dockerClient, "dockerCmdExecFactory"); + Assert.notNull(dockerCmdExecFactory, "当前方法不被支持,暂时不能使用"); + String nodeId = (String) parameter.get("nodeId"); + RemoveSwarmNodeCmdImpl removeSwarmNodeCmd = new RemoveSwarmNodeCmdImpl( + dockerCmdExecFactory.removeSwarmNodeCmdExec(), nodeId); + removeSwarmNodeCmd.withForce(true); + removeSwarmNodeCmd.exec(); + } finally { + IoUtil.close(dockerClient); + } + } + + private void updateSwarmNodeCmd(Map parameter) { + DockerClient dockerClient = DockerUtil.build(parameter); + try { + String nodeId = (String) parameter.get("nodeId"); + List nodes = dockerClient.listSwarmNodesCmd() + .withIdFilter(CollUtil.newArrayList(nodeId)).exec(); + SwarmNode swarmNode = CollUtil.getFirst(nodes); + Assert.notNull(swarmNode, "没有对应的节点"); + ObjectVersion version = swarmNode.getVersion(); + Assert.notNull(version, "对应的节点信息不完整不能继续"); + // + String availabilityStr = (String) parameter.get("availability"); + String roleStr = (String) parameter.get("role"); + // + SwarmNodeAvailability availability = EnumUtil.fromString(SwarmNodeAvailability.class, availabilityStr); + SwarmNodeRole role = EnumUtil.fromString(SwarmNodeRole.class, roleStr); + UpdateSwarmNodeCmd swarmNodeCmd = dockerClient.updateSwarmNodeCmd(); + swarmNodeCmd.withSwarmNodeId(nodeId); + SwarmNodeSpec swarmNodeSpec = new SwarmNodeSpec(); + swarmNodeSpec.withAvailability(availability); + swarmNodeSpec.withRole(role); + swarmNodeCmd.withSwarmNodeSpec(swarmNodeSpec); + swarmNodeCmd.withVersion(version.getIndex()); + swarmNodeCmd.exec(); + } finally { + IoUtil.close(dockerClient); + } + } + private void leaveSwarmCmd(Map parameter) { DockerClient dockerClient = DockerUtil.build(parameter); diff --git a/modules/sub-plugin/docker-cli/src/test/java/TestSwarm.java b/modules/sub-plugin/docker-cli/src/test/java/TestSwarm.java index 4adca4c49..bef9b7ff5 100644 --- a/modules/sub-plugin/docker-cli/src/test/java/TestSwarm.java +++ b/modules/sub-plugin/docker-cli/src/test/java/TestSwarm.java @@ -32,10 +32,7 @@ import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.serializer.SerializerFeature; import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.command.*; -import com.github.dockerjava.api.model.Info; -import com.github.dockerjava.api.model.Swarm; -import com.github.dockerjava.api.model.SwarmNode; -import com.github.dockerjava.api.model.SwarmSpec; +import com.github.dockerjava.api.model.*; import com.github.dockerjava.core.DefaultDockerClientConfig; import com.github.dockerjava.core.DockerClientConfig; import com.github.dockerjava.core.DockerClientImpl; @@ -177,6 +174,28 @@ public class TestSwarm { removeSwarmNodeCmd.exec(); } + @Test + public void update() { + DockerClient client = this.client(node1); + + + String nodeId = "rk2gxpql2449t0s1ymtivtyoy"; + List nodes = client.listSwarmNodesCmd().withIdFilter(CollUtil.newArrayList(nodeId)).exec(); + System.out.println(nodes); + SwarmNode swarmNode = CollUtil.getFirst(nodes); + UpdateSwarmNodeCmd swarmNodeCmd = client.updateSwarmNodeCmd(); + + swarmNodeCmd.withSwarmNodeId(nodeId); + SwarmNodeSpec swarmNodeSpec = new SwarmNodeSpec(); + swarmNodeSpec.withAvailability(SwarmNodeAvailability.PAUSE); + swarmNodeSpec.withRole(SwarmNodeRole.WORKER); + swarmNodeCmd.withSwarmNodeSpec(swarmNodeSpec); + + swarmNodeCmd.withVersion(swarmNode.getVersion().getIndex()); + + swarmNodeCmd.exec(); + } + @Test public void testJoin() { diff --git a/web-vue/src/api/docker-swarm.js b/web-vue/src/api/docker-swarm.js index 67c6d32d8..1d78c7e9d 100644 --- a/web-vue/src/api/docker-swarm.js +++ b/web-vue/src/api/docker-swarm.js @@ -69,3 +69,27 @@ export function dockerSwarmNodeList(params) { data: params, }); } + +/** + * 容器集群节点修改 + * @param {JSON} params + */ +export function dockerSwarmNodeUpdate(params) { + return axios({ + url: "/docker-swarm/update", + method: "post", + data: params, + }); +} + +/** + * 容器集群节点剔除 + * @param {JSON} params + */ +export function dockerSwarmNodeLeave(params) { + return axios({ + url: "/docker-swarm/leave", + method: "get", + params: params, + }); +} diff --git a/web-vue/src/pages/docker/list.vue b/web-vue/src/pages/docker/list.vue index a07c9fa9a..91b3bb8ab 100644 --- a/web-vue/src/pages/docker/list.vue +++ b/web-vue/src/pages/docker/list.vue @@ -50,7 +50,7 @@ @@ -158,6 +158,9 @@ export default { handleNode(record) { this.temp = record; this.nodeVisible = true; + this.$nextTick(() => { + this.$refs.swarmNode.refreshData(); + }); }, // 提交 数据 handleEditOk() { @@ -183,7 +186,7 @@ export default { handleUnbind(record) { this.$confirm({ title: "系统提示", - content: "真的要解绑该集群么?解绑至少删除在本系统的关联数据,不会删除容器里面数据", + content: "真的要解绑该集群么?解绑只删除在本系统的关联数据,不会删除容器里面数据", okText: "确认", cancelText: "取消", onOk: () => { diff --git a/web-vue/src/pages/docker/swarm/node.vue b/web-vue/src/pages/docker/swarm/node.vue index 3ffb73c9d..67b0375b1 100644 --- a/web-vue/src/pages/docker/swarm/node.vue +++ b/web-vue/src/pages/docker/swarm/node.vue @@ -9,30 +9,59 @@ 工作节点 管理节点 - - 搜索 - + + 搜索 {{ text }} - - - {{ text }}- + + + {{ text }} + + + + + {{ text }} + + + + + {{ text }} + + {{ text }}-{{ item.description && item.description.platform && item.description.platform.architecture }} + + + {{ text }} + + + + + + + + 工作节点 + 管理节点 + + + + + 活跃 + 暂停 + 排空 + + + +