feat(add) 新增 docker swarm 集群管理

This commit is contained in:
bwcx_jzy 2022-02-14 12:35:40 +08:00
parent 273011c210
commit 2e1e4aaaf4
No known key found for this signature in database
GPG Key ID: 5E48E9372088B9E5
10 changed files with 331 additions and 108 deletions

View File

@ -7,6 +7,8 @@
1. nginx 删除操作新增还原配置 1. nginx 删除操作新增还原配置
2. 【server】新增用户登录日志记录 2. 【server】新增用户登录日志记录
3. 【server】log view 新增正则参考 3. 【server】log view 新增正则参考
4. 【server】docker 控制台新增 docker 信息查看
5. 【server】新增 docker swarm 集群管理
### 🐞 解决BUG、优化功能 ### 🐞 解决BUG、优化功能

View File

@ -275,11 +275,7 @@ public class DockerInfoController extends BaseServerController {
parameter.put("force", true); parameter.put("force", true);
plugin.execute("leaveSwarm", parameter, JSONObject.class); plugin.execute("leaveSwarm", parameter, JSONObject.class);
// //
DockerInfoModel update = new DockerInfoModel(); dockerInfoService.unbind(id);
update.setId(id);
update.setSwarmId(StrUtil.EMPTY);
update.setSwarmNodeId(StrUtil.EMPTY);
dockerInfoService.update(update);
return new JsonMessage<>(200, "强制解绑成功"); return new JsonMessage<>(200, "强制解绑成功");
} }
} }

View File

@ -2,7 +2,6 @@ package io.jpom.controller.docker;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ArrayUtil; import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.db.Entity; import cn.hutool.db.Entity;
import cn.jiangzeyin.common.JsonMessage; import cn.jiangzeyin.common.JsonMessage;
@ -25,7 +24,10 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController; import org.springframework.web.bind.annotation.RestController;
import javax.servlet.http.HttpServletRequest; 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; import java.util.function.Consumer;
/** /**
@ -104,7 +106,7 @@ public class DockerSwarmInfoController extends BaseServerController {
DockerSwarmInfoMode dockerSwarmInfoMode = infoModeBuilder.build(); DockerSwarmInfoMode dockerSwarmInfoMode = infoModeBuilder.build();
consumer.accept(dockerSwarmInfoMode); consumer.accept(dockerSwarmInfoMode);
// 更新 docker id // 更新 docker id
this.bindDockerSwarm(dockerInfoModel1, tag, null, id); dockerInfoService.bindDockerSwarm(dockerInfoModel1, tag, null, id);
} }
private void check(String id, String swarmId) { private void check(String id, String swarmId) {
@ -144,26 +146,6 @@ public class DockerSwarmInfoController extends BaseServerController {
return JsonMessage.getString(200, "修改成功"); 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<String> 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 已经加入到其他集群啦"); Assert.state(optional.isPresent(), "当前 docker 已经加入到其他集群啦");
// 绑定数据 // 绑定数据
String managerId = managerSwarmInfo.getString("id"); String managerId = managerSwarmInfo.getString("id");
this.bindDockerSwarm(joinSwarmDocker, swarmInfoMode1.getTag(), swarm, managerId); dockerInfoService.bindDockerSwarm(joinSwarmDocker, swarmInfoMode1.getTag(), swarm, managerId);
return JsonMessage.getString(200, "集群绑定成功"); return JsonMessage.getString(200, "集群绑定成功");
} }
} }
@ -231,7 +213,7 @@ public class DockerSwarmInfoController extends BaseServerController {
JSONObject swarm = info.getJSONObject("swarm"); JSONObject swarm = info.getJSONObject("swarm");
Assert.notNull(swarm, "获取 docker 集群信息失败:-1"); Assert.notNull(swarm, "获取 docker 集群信息失败:-1");
String managerId = managerSwarmInfo.getString("id"); String managerId = managerSwarmInfo.getString("id");
this.bindDockerSwarm(joinSwarmDocker, swarmInfoMode1.getTag(), swarm, managerId); dockerInfoService.bindDockerSwarm(joinSwarmDocker, swarmInfoMode1.getTag(), swarm, managerId);
return JsonMessage.getString(200, "集群创建成功"); return JsonMessage.getString(200, "集群创建成功");
} }
@ -265,14 +247,9 @@ public class DockerSwarmInfoController extends BaseServerController {
public JsonMessage<List<JSONObject>> nodeList( public JsonMessage<List<JSONObject>> nodeList(
@ValidatorItem String id, @ValidatorItem String id,
String nodeId, String nodeName, String nodeRole) throws Exception { 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); IPlugin plugin = PluginFactory.getPlugin(DockerSwarmInfoService.DOCKER_PLUGIN_NAME);
Map<String, Object> map = managerSwarmDocker.toParameter(); Map<String, Object> map = this.getPluginMap(id);
map.put("id", nodeId); map.put("id", nodeId);
map.put("name", nodeName); map.put("name", nodeName);
map.put("role", nodeRole); map.put("role", nodeRole);
@ -280,19 +257,65 @@ public class DockerSwarmInfoController extends BaseServerController {
return new JsonMessage<>(200, "", listSwarmNodes); return new JsonMessage<>(200, "", listSwarmNodes);
} }
private Map<String, Object> 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 * @param id 集群ID
// * @return json * @return json
// */ */
// @GetMapping(value = "leave", produces = MediaType.APPLICATION_JSON_VALUE) @PostMapping(value = "update", produces = MediaType.APPLICATION_JSON_VALUE)
// @Feature(method = MethodFeature.DEL) @Feature(method = MethodFeature.EXECUTE)
// public JsonMessage<String> leave(@ValidatorItem String id) { public JsonMessage<String> update(@ValidatorItem String id, @ValidatorItem String nodeId, @ValidatorItem String availability, @ValidatorItem String role) throws Exception {
// HttpServletRequest request = getRequest(); //
// DockerSwarmInfoMode swarmInfoMode1 = dockerSwarmInfoService.getByKey(id, request); IPlugin plugin = PluginFactory.getPlugin(DockerSwarmInfoService.DOCKER_PLUGIN_NAME);
// Assert.notNull(swarmInfoMode1, "没有对应的集群"); Map<String, Object> map = this.getPluginMap(id);
// return new JsonMessage<>(200, "解绑成功"); 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<String> 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<DockerInfoModel> dockerInfoModels = dockerInfoService.queryList(dockerInfoModel, 2);
Assert.state(CollUtil.size(dockerInfoModels) == 1, "当前节点未在系统中绑定或者绑定信息异常,不能操作");
//
{
DockerInfoModel dockerInfoModel1 = CollUtil.getFirst(dockerInfoModels);
Map<String, Object> parameter = dockerInfoModel1.toParameter();
parameter.put("force", true);
plugin.execute("leaveSwarm", parameter, JSONObject.class);
}
{
Map<String, Object> map = this.getPluginMap(id);
map.put("nodeId", nodeId);
plugin.execute("removeSwarmNode", map);
}
//
dockerInfoService.unbind(id);
return new JsonMessage<>(200, "剔除成功");
}
} }

View File

@ -25,6 +25,7 @@ package io.jpom.service.docker;
import cn.hutool.core.collection.CollUtil; import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.SystemClock; import cn.hutool.core.date.SystemClock;
import cn.hutool.core.thread.ThreadUtil; import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil; import cn.hutool.core.util.StrUtil;
import cn.hutool.cron.task.Task; import cn.hutool.cron.task.Task;
import cn.hutool.db.Entity; import cn.hutool.db.Entity;
@ -40,6 +41,7 @@ import io.jpom.service.h2db.BaseWorkspaceService;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service; import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -181,4 +183,45 @@ public class DockerInfoService extends BaseWorkspaceService<DockerInfoModel> imp
String sql = StrUtil.format("SELECT * FROM {} where workspaceId=? and instr(tags,?)", super.getTableName()); String sql = StrUtil.format("SELECT * FROM {} where workspaceId=? and instr(tags,?)", super.getTableName());
return (int) super.count(sql, workspaceId, tag); 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<String> 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);
}
} }

View File

@ -22,20 +22,21 @@
*/ */
package io.jpom; package io.jpom;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.convert.Convert; import cn.hutool.core.convert.Convert;
import cn.hutool.core.io.IoUtil; 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 cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject; import com.alibaba.fastjson.JSONObject;
import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.command.JoinSwarmCmd; import com.github.dockerjava.api.command.*;
import com.github.dockerjava.api.command.LeaveSwarmCmd; import com.github.dockerjava.api.model.*;
import com.github.dockerjava.api.command.ListSwarmNodesCmd; import com.github.dockerjava.core.command.RemoveSwarmNodeCmdImpl;
import com.github.dockerjava.api.model.Swarm;
import com.github.dockerjava.api.model.SwarmNode;
import com.github.dockerjava.api.model.SwarmSpec;
import io.jpom.plugin.IDefaultPlugin; import io.jpom.plugin.IDefaultPlugin;
import io.jpom.plugin.PluginConfig; import io.jpom.plugin.PluginConfig;
import lombok.extern.slf4j.Slf4j; import lombok.extern.slf4j.Slf4j;
import org.springframework.util.Assert;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
@ -67,12 +68,62 @@ public class DefaultDockerSwarmPluginImpl implements IDefaultPlugin {
case "leaveSwarm": case "leaveSwarm":
this.leaveSwarmCmd(parameter); this.leaveSwarmCmd(parameter);
return null; return null;
case "updateSwarmNode":
this.updateSwarmNodeCmd(parameter);
return null;
case "removeSwarmNode":
this.removeSwarmNodeCmd(parameter);
return null;
default: default:
break; break;
} }
return null; return null;
} }
private void removeSwarmNodeCmd(Map<String, Object> 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<String, Object> parameter) {
DockerClient dockerClient = DockerUtil.build(parameter);
try {
String nodeId = (String) parameter.get("nodeId");
List<SwarmNode> 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<String, Object> parameter) { private void leaveSwarmCmd(Map<String, Object> parameter) {
DockerClient dockerClient = DockerUtil.build(parameter); DockerClient dockerClient = DockerUtil.build(parameter);

View File

@ -32,10 +32,7 @@ import com.alibaba.fastjson.JSONObject;
import com.alibaba.fastjson.serializer.SerializerFeature; import com.alibaba.fastjson.serializer.SerializerFeature;
import com.github.dockerjava.api.DockerClient; import com.github.dockerjava.api.DockerClient;
import com.github.dockerjava.api.command.*; import com.github.dockerjava.api.command.*;
import com.github.dockerjava.api.model.Info; import com.github.dockerjava.api.model.*;
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.core.DefaultDockerClientConfig; import com.github.dockerjava.core.DefaultDockerClientConfig;
import com.github.dockerjava.core.DockerClientConfig; import com.github.dockerjava.core.DockerClientConfig;
import com.github.dockerjava.core.DockerClientImpl; import com.github.dockerjava.core.DockerClientImpl;
@ -177,6 +174,28 @@ public class TestSwarm {
removeSwarmNodeCmd.exec(); removeSwarmNodeCmd.exec();
} }
@Test
public void update() {
DockerClient client = this.client(node1);
String nodeId = "rk2gxpql2449t0s1ymtivtyoy";
List<SwarmNode> 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 @Test
public void testJoin() { public void testJoin() {

View File

@ -69,3 +69,27 @@ export function dockerSwarmNodeList(params) {
data: 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,
});
}

View File

@ -50,7 +50,7 @@
</template> </template>
<template v-else> <template v-else>
<a-tooltip> <a-tooltip>
<a-tooltip :title="`${parseInt(record.status) !== 1 ? '已经离线' : '已经创建过集群啦'}`"> <a-tooltip :title="`${parseInt(record.status) !== 1 ? '已经离线' : '已经集群啦'}`">
<a-button size="small" disabled type="primary">集群</a-button> <a-button size="small" disabled type="primary">集群</a-button>
</a-tooltip> </a-tooltip>
</a-tooltip> </a-tooltip>
@ -140,7 +140,7 @@
</a-modal> </a-modal>
<!-- 创建集群 --> <!-- 创建集群 -->
<a-modal v-model="initSwarmVisible" title="创建 Docker 集群" @ok="handleSwarm" :maskClosable="false"> <a-modal v-model="initSwarmVisible" title="创建 Docker 集群" @ok="handleSwarm" :maskClosable="false">
<a-form-model ref="editForm" :rules="rules" :model="temp" :label-col="{ span: 4 }" :wrapper-col="{ span: 18 }"> <a-form-model ref="initForm" :rules="rules" :model="temp" :label-col="{ span: 4 }" :wrapper-col="{ span: 18 }">
<a-form-model-item label="集群名称" prop="name"> <a-form-model-item label="集群名称" prop="name">
<a-input v-model="temp.name" placeholder="容器名称" /> <a-input v-model="temp.name" placeholder="容器名称" />
</a-form-model-item> </a-form-model-item>
@ -150,7 +150,7 @@
</a-modal> </a-modal>
<!-- 加入集群 --> <!-- 加入集群 -->
<a-modal v-model="joinSwarmVisible" title="加入 Docker 集群" @ok="handleSwarmJoin" :maskClosable="false"> <a-modal v-model="joinSwarmVisible" title="加入 Docker 集群" @ok="handleSwarmJoin" :maskClosable="false">
<a-form-model ref="editForm" :rules="rules" :model="temp" :label-col="{ span: 4 }" :wrapper-col="{ span: 18 }"> <a-form-model ref="joinForm" :rules="rules" :model="temp" :label-col="{ span: 4 }" :wrapper-col="{ span: 18 }">
<a-form-model-item label="选择集群" prop="swarmId"> <a-form-model-item label="选择集群" prop="swarmId">
<a-select <a-select
show-search show-search
@ -416,7 +416,13 @@ export default {
}, },
// //
handleLeaveForce(record) { handleLeaveForce(record) {
const html = "<h1 style='color:red;'>真的要强制退出集群吗?</h1><ul style='color:red;'>" + "<li>请提前备份数据再操作奥</li>" + "<li>操作不能撤回奥</li>" + " </ul>"; const html =
"<h1 style='color:red;'>真的要强制退出集群吗?</h1> " +
"<h3 style='color:red;'>如果当前集群还存在可能出现数据不一致问题奥</h3> " +
"<ul style='color:red;'>" +
"<li>请提前备份数据再操作奥</li>" +
"<li>操作不能撤回奥</li>" +
" </ul>";
const h = this.$createElement; const h = this.$createElement;
this.$confirm({ this.$confirm({
title: "系统提示", title: "系统提示",
@ -480,7 +486,7 @@ export default {
dockerId: record.id, dockerId: record.id,
}; };
this.initSwarmVisible = true; this.initSwarmVisible = true;
this.$refs["editForm"]?.resetFields(); this.$refs["initForm"]?.resetFields();
}, },
// //
joinSwarm(record) { joinSwarm(record) {
@ -490,11 +496,11 @@ export default {
dockerId: record.id, dockerId: record.id,
}; };
this.joinSwarmVisible = true; this.joinSwarmVisible = true;
this.$refs["editForm"]?.resetFields(); this.$refs["joinForm"]?.resetFields();
}); });
}, },
handleSwarm() { handleSwarm() {
this.$refs["editForm"].validate((valid) => { this.$refs["initForm"].validate((valid) => {
if (!valid) { if (!valid) {
return false; return false;
} }
@ -512,7 +518,7 @@ export default {
}, },
// //
handleSwarmJoin() { handleSwarmJoin() {
this.$refs["editForm"].validate((valid) => { this.$refs["joinForm"].validate((valid) => {
if (!valid) { if (!valid) {
return false; return false;
} }

View File

@ -50,7 +50,7 @@
</a-modal> </a-modal>
<!-- 节点信息 --> <!-- 节点信息 -->
<a-modal v-model="nodeVisible" width="90vw" title="集群节点" :footer="null" :maskClosable="false"> <a-modal v-model="nodeVisible" width="90vw" title="集群节点" :footer="null" :maskClosable="false">
<swarm-node :id="this.temp.id" /> <swarm-node ref="swarmNode" :id="this.temp.id" />
</a-modal> </a-modal>
</div> </div>
</template> </template>
@ -158,6 +158,9 @@ export default {
handleNode(record) { handleNode(record) {
this.temp = record; this.temp = record;
this.nodeVisible = true; this.nodeVisible = true;
this.$nextTick(() => {
this.$refs.swarmNode.refreshData();
});
}, },
// //
handleEditOk() { handleEditOk() {
@ -183,7 +186,7 @@ export default {
handleUnbind(record) { handleUnbind(record) {
this.$confirm({ this.$confirm({
title: "系统提示", title: "系统提示",
content: "真的要解绑该集群么?解绑至少删除在本系统的关联数据,不会删除容器里面数据", content: "真的要解绑该集群么?解绑删除在本系统的关联数据,不会删除容器里面数据",
okText: "确认", okText: "确认",
cancelText: "取消", cancelText: "取消",
onOk: () => { onOk: () => {

View File

@ -9,30 +9,59 @@
<a-select-option key="worker">工作节点</a-select-option> <a-select-option key="worker">工作节点</a-select-option>
<a-select-option key="manager">管理节点</a-select-option> <a-select-option key="manager">管理节点</a-select-option>
</a-select> </a-select>
<a-tooltip title="按住 Ctr 或者 Alt 键点击按钮快速回到第一页">
<a-button type="primary" @click="loadData" :loading="loading">搜索</a-button> <a-button type="primary" @click="loadData" :loading="loading">搜索</a-button>
</a-tooltip>
</a-space> </a-space>
</template> </template>
<a-tooltip slot="tooltip" slot-scope="text" placement="topLeft" :title="text"> <a-tooltip slot="tooltip" slot-scope="text" placement="topLeft" :title="text">
<span>{{ text }}</span> <span>{{ text }}</span>
</a-tooltip> </a-tooltip>
<a-tooltip slot="status" slot-scope="text, item" placement="topLeft" :title="text"> <a-tooltip slot="status" slot-scope="text, item" placement="topLeft" :title="`节点状态:${text} 节点可用性:${item.spec ? item.spec.availability || '' : ''}`">
<a-tag :color="text === 'READY' ? 'green' : text === 'DOWN' ? 'red' : ''"> <a-tag :color="(item.spec && item.spec.availability) === 'ACTIVE' ? 'green' : 'red'">
{{ text }}-<template v-if="item.spec">{{ item.spec.role }}</template> {{ text }}
<template v-if="item.spec">{{ item.spec.availability }}</template>
</a-tag> </a-tag>
</a-tooltip> </a-tooltip>
<!-- 角色显示 -->
<a-tooltip
slot="role"
slot-scope="text, item"
placement="topLeft"
:title="`角色:${text} ${item.managerStatus && item.managerStatus.reachability === 'REACHABLE' ? '管理状态:' + item.managerStatus.reachability : ''}`"
>
<a-tag :color="`${item.managerStatus && item.managerStatus.reachability === 'REACHABLE' ? 'green' : ''}`">
{{ text }}
</a-tag>
</a-tooltip>
<a-tooltip slot="address" slot-scope="text, item" placement="topLeft" :title="text">
<a-icon v-if="item.managerStatus && item.managerStatus.leader" type="cloud-server" />
{{ text }}
</a-tooltip>
<a-tooltip slot="os" slot-scope="text, item" placement="topLeft" :title="text"> <a-tooltip slot="os" slot-scope="text, item" placement="topLeft" :title="text">
<span> <span>
<a-tag>{{ text }}-{{ item.description && item.description.platform && item.description.platform.architecture }}</a-tag> <a-tag>{{ text }}-{{ item.description && item.description.platform && item.description.platform.architecture }}</a-tag>
</span> </span>
</a-tooltip> </a-tooltip>
<a-tooltip slot="updatedAt" slot-scope="text, item" placement="topLeft" :title="`修改时间:${text} 创建时间:${item.createdAt}`">
<span>
<a-tag>{{ text }}</a-tag>
</span>
</a-tooltip>
<template slot="operation" slot-scope="text, record"> <template slot="operation" slot-scope="text, record">
<a-space> <a-space>
<a-button size="small" type="primary" @click="handleEdit(record)">编辑</a-button> <template v-if="record.managerStatus && record.managerStatus.leader"> - </template>
<a-dropdown> <template v-else>
<a-button size="small" type="primary" @click="handleEdit(record)">修改</a-button>
<a-button size="small" type="danger" @click="handleLeava(record)">剔除</a-button>
</template>
<!-- <template v-else>
<a-button size="small" type="danger" v-if="!item.managerStatus.leader" @click="handleLeava(record)">剔除</a-button>
</template> -->
<!-- <a-dropdown>
<a class="ant-dropdown-link" @click="(e) => e.preventDefault()"> 更多 <a-icon type="down" /> </a> <a class="ant-dropdown-link" @click="(e) => e.preventDefault()"> 更多 <a-icon type="down" /> </a>
<a-menu slot="overlay"> <a-menu slot="overlay">
<a-menu-item> </a-menu-item> <a-menu-item> </a-menu-item>
@ -40,15 +69,33 @@
<a-button size="small" type="danger" @click="handleUnbind(record)">解绑</a-button> <a-button size="small" type="danger" @click="handleUnbind(record)">解绑</a-button>
</a-menu-item> </a-menu-item>
</a-menu></a-dropdown </a-menu></a-dropdown
> > -->
</a-space> </a-space>
</template> </template>
</a-table> </a-table>
<!-- 编辑节点 -->
<a-modal v-model="editVisible" title="编辑节点" @ok="handleEditOk" :maskClosable="false">
<a-form-model ref="editForm" :rules="rules" :model="temp" :label-col="{ span: 4 }" :wrapper-col="{ span: 18 }">
<a-form-model-item label="角色" prop="role">
<a-radio-group name="role" v-model="temp.role">
<a-radio value="WORKER"> 工作节点</a-radio>
<a-radio value="MANAGER"> 管理节点 </a-radio>
</a-radio-group>
</a-form-model-item>
<a-form-model-item label="状态" prop="availability">
<a-radio-group name="availability" v-model="temp.availability">
<a-radio value="ACTIVE"> 活跃</a-radio>
<a-radio value="PAUSE"> 暂停 </a-radio>
<a-radio value="DRAIN"> 排空 </a-radio>
</a-radio-group>
</a-form-model-item>
</a-form-model>
</a-modal>
</div> </div>
</template> </template>
<script> <script>
import { editDockerSwarm, dockerSwarmNodeList, unbindSwarm } from "@/api/docker-swarm"; import { dockerSwarmNodeList, dockerSwarmNodeLeave, dockerSwarmNodeUpdate } from "@/api/docker-swarm";
export default { export default {
components: {}, components: {},
@ -65,42 +112,51 @@ export default {
temp: {}, temp: {},
editVisible: false, editVisible: false,
initSwarmVisible: false, initSwarmVisible: false,
rules: {
role: [{ required: true, message: "请选择节点角色", trigger: "blur" }],
availability: [{ required: true, message: "请选择节点状态", trigger: "blur" }],
},
columns: [ columns: [
{ title: "节点Id", dataIndex: "id", ellipsis: true, scopedSlots: { customRender: "tooltip" } }, { title: "节点Id", dataIndex: "id", ellipsis: true, scopedSlots: { customRender: "tooltip" } },
{ title: "节点地址", dataIndex: "status.address", ellipsis: true, scopedSlots: { customRender: "tooltip" } },
{ title: "状态", width: 160, align: "center", dataIndex: "status.state", ellipsis: true, scopedSlots: { customRender: "status" } },
{ title: "主机名", dataIndex: "description.hostname", ellipsis: true, scopedSlots: { customRender: "tooltip" } }, { title: "主机名", dataIndex: "description.hostname", ellipsis: true, scopedSlots: { customRender: "tooltip" } },
{ title: "系统类型", width: 140, align: "center", dataIndex: "description.platform.os", ellipsis: true, scopedSlots: { customRender: "os" } }, { title: "节点地址", width: 150, dataIndex: "status.address", ellipsis: true, scopedSlots: { customRender: "address" } },
{ { title: "状态", width: 140, dataIndex: "status.state", ellipsis: true, scopedSlots: { customRender: "status" } },
title: "创建时间", { title: "角色", width: 110, dataIndex: "spec.role", ellipsis: true, scopedSlots: { customRender: "role" } },
dataIndex: "createdAt",
ellipsis: true, { title: "系统类型", width: 140, align: "center", dataIndex: "description.platform.os", ellipsis: true, scopedSlots: { customRender: "os" } },
scopedSlots: { customRender: "tooltip" }, // {
width: 170, // title: "",
}, // dataIndex: "createdAt",
// ellipsis: true,
// scopedSlots: { customRender: "tooltip" },
// width: 170,
// },
{ {
title: "修改时间", title: "修改时间",
dataIndex: "updatedAt", dataIndex: "updatedAt",
ellipsis: true, ellipsis: true,
scopedSlots: { customRender: "tooltip" }, scopedSlots: { customRender: "updatedAt" },
width: 170, width: 170,
}, },
{ title: "操作", dataIndex: "operation", scopedSlots: { customRender: "operation" }, align: "center", width: 140 }, { title: "操作", dataIndex: "operation", scopedSlots: { customRender: "operation" }, align: "center", width: 120 },
], ],
}; };
}, },
computed: {}, computed: {},
mounted() { mounted() {
this.list = []; // this.loadData();
this.loadData();
}, },
methods: { methods: {
refreshData() {
this.list = [];
this.loadData();
},
// //
loadData(pointerEvent) { loadData() {
this.loading = true; this.loading = true;
this.listQuery.page = pointerEvent?.altKey || pointerEvent?.ctrlKey ? 1 : this.listQuery.page;
this.listQuery.id = this.id; this.listQuery.id = this.id;
dockerSwarmNodeList(this.listQuery).then((res) => { dockerSwarmNodeList(this.listQuery).then((res) => {
if (res.code === 200) { if (res.code === 200) {
@ -109,22 +165,21 @@ export default {
this.loading = false; this.loading = false;
}); });
}, },
//
handleEdit(record) { handleEdit(record) {
this.temp = record;
this.editVisible = true; this.editVisible = true;
this.temp = {
this.$refs["editForm"]?.resetFields(); nodeId: record.id,
role: record.spec.role,
availability: record.spec.availability,
};
}, },
//
handleEditOk() { handleEditOk() {
//
this.$refs["editForm"].validate((valid) => { this.$refs["editForm"].validate((valid) => {
if (!valid) { if (!valid) {
return false; return false;
} }
this.temp.id = this.id;
editDockerSwarm(this.temp).then((res) => { dockerSwarmNodeUpdate(this.temp).then((res) => {
if (res.code === 200) { if (res.code === 200) {
// //
this.$notification.success({ this.$notification.success({
@ -137,18 +192,19 @@ export default {
}); });
}, },
// //
handleUnbind(record) { handleLeava(record) {
this.$confirm({ this.$confirm({
title: "系统提示", title: "系统提示",
content: "真的要解绑该集群么?解绑至少删除在本系统的关联数据,不会删除容器里面数据", content: "真的要在该集群剔除此节点么?",
okText: "确认", okText: "确认",
cancelText: "取消", cancelText: "取消",
onOk: () => { onOk: () => {
// //
const params = { const params = {
id: record.id, nodeId: record.id,
id: this.id,
}; };
unbindSwarm(params).then((res) => { dockerSwarmNodeLeave(params).then((res) => {
if (res.code === 200) { if (res.code === 200) {
this.$notification.success({ this.$notification.success({
message: res.msg, message: res.msg,