Skip to content

Commit

Permalink
feat: advance function docker info
Browse files Browse the repository at this point in the history
  • Loading branch information
zyyzyykk committed Feb 17, 2025
1 parent 1e1da87 commit 63f4507
Show file tree
Hide file tree
Showing 8 changed files with 334 additions and 15 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@ public class AdvanceController {

public static final String COOPERATE_SECRET_KEY = "o4D1fYuVp2js9xKX";

public static final String[] DOCKER_INFO_CMD = new String[]{
"echo -n \"$(docker ps --format \"{{.ID}}@{{.Names}}@{{.Status}}@{{.Image}}@{{.Ports}}\" | paste -sd '$' -)\"",
"echo -n \"$(docker images --format \"{{.ID}}@{{.Repository}}@{{.Size}}@{{.CreatedAt}}\" | paste -sd '$' -)\"",
"echo -n \"$(docker network ls --format \"{{.Name}}\" | xargs -I {} docker network inspect {} --format \"{{.Name}}@{{range .IPAM.Config}}{{.Subnet}}{{end}}@{{range .IPAM.Config}}{{.Gateway}}{{end}}@{{.Created}}\" | paste -sd'$' -)\"",
"echo -n \"$(docker volume ls --format \"{{.Name}}\" | xargs -I {} docker volume inspect {} --format \"{{.Name}}@{{.Mountpoint}}@{{.CreatedAt}}\" | paste -sd'$' -)\"\n"
};

/**
* 获取协作Key
*/
Expand Down Expand Up @@ -117,5 +124,44 @@ public Result monitor(String sshKey) {
return Result.success(200, successMsg, status);
}

// 完整命令
// echo -n "$(docker ps --format "{{.ID}}@{{.Names}}@{{.Status}}@{{.Image}}@{{.Ports}}" | paste -sd '$' -)" && \
// echo -n "^" && \
// echo -n "$(docker images --format "{{.ID}}@{{.Repository}}@{{.Size}}@{{.CreatedAt}}" | paste -sd '$' -)" && \
// echo -n "^" && \
// echo -n "$(docker network ls --format "{{.Name}}" | xargs -I {} docker network inspect {} --format "{{.Name}}@{{range .IPAM.Config}}{{.Subnet}}{{end}}@{{range .IPAM.Config}}{{.Gateway}}{{end}}@{{.Created}}" | paste -sd'$' -)" && \
// echo -n "^" && \
// echo -n "$(docker volume ls --format "{{.Name}}" | xargs -I {} docker volume inspect {} --format "{{.Name}}@{{.Mountpoint}}@{{.CreatedAt}}" | paste -sd'$' -)"
/**
* 获取Docker信息
*/
@PostMapping("/docker/info")
public Result dockerContainerInfo(String sshKey, Integer type) {
String errorMsg = "获取Docker信息失败";
String successMsg = "获取Docker信息成功";

SSHClient ssh = WebSocketServer.sshClientMap.get(sshKey);
if(ssh == null) {
return Result.error(FileBlockStateEnum.SSH_NOT_EXIST.getState(),"连接断开," + errorMsg);
}
String docker = "";
String command = DOCKER_INFO_CMD[type % DOCKER_INFO_CMD.length];
try(Session session = ssh.startSession();
Session.Command cmd = session.exec(command))
{
// 读取命令执行结果
try (BufferedReader reader = new BufferedReader(new InputStreamReader(cmd.getInputStream()))) {
docker += reader.readLine();
}
// 等待命令执行完毕
cmd.join();
int exitStatus = cmd.getExitStatus();
if (exitStatus != 0) return Result.error(errorMsg);
} catch (Exception e) {
e.printStackTrace();
return Result.error(errorMsg);
}
return Result.success(200, successMsg, docker);
}

}
4 changes: 3 additions & 1 deletion backend/terminal/src/main/resources/locales/en_US.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,5 +49,7 @@
"协作Key生成成功": "Cooperate Key Generate Success",
"协作Key生成失败": "Cooperate Key Generate Failed",
"获取监控状态成功": "Obtain Monitor Status Success",
"获取监控状态失败": "Obtain Monitor Status Failed"
"获取监控状态失败": "Obtain Monitor Status Failed",
"获取Docker信息成功": "Obtain Docker Info Success",
"获取Docker信息失败": "Obtain Docker Info Failed"
}
2 changes: 1 addition & 1 deletion front/terminal/src/components/OptionBlock.vue
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<div v-if="Object.keys(sshOptions).length > 0" class="kk-border">
<div v-for="(value, key) in sshOptions" :key="key" >
<div :class="['item-class', (aimOption == key) ? 'item-selected' : '']" @click="aimOption = key">
<FileIcons :style="{display: 'flex', alignItems: 'center'}" name="kk.txt" :width="20" :height="20" :isFolder="false" />
<FileIcons :style="{display: 'flex', alignItems: 'center'}" name="kk.ini" :width="20" :height="20" :isFolder="false" />
<div class="ellipsis" style="margin: 0 10px;">{{ key }}</div>
<div style="flex: 1;" ></div>
<div @click="confirmDeleteOption(key)" ><el-icon><CircleClose /></el-icon></div>
Expand Down
248 changes: 248 additions & 0 deletions front/terminal/src/components/advance/DockerBlock.vue
Original file line number Diff line number Diff line change
@@ -0,0 +1,248 @@
<template>
<el-dialog
v-model="computedDialogVisible"
destroy-on-close
:width="720"
title="Docker"
:modal="false"
modal-class="kk-dialog-class"
header-class="kk-header-class"
body-class="kk-body-class-6"
:before-close="closeDialog"
draggable
>
<div class="no-select" >
<el-tabs element-loading-text="Loading..." v-loading="loading" @tab-click="handleTabClick" type="border-card" >
<el-tab-pane :label="$t('容器')">
<el-table style="height: 200px; width: 100%" v-if="dockerInfo.container.length > 0" :data="dockerInfo.container" border stripe >
<el-table-column show-overflow-tooltip type="selection" :selectable="selectable" width="40" />
<el-table-column show-overflow-tooltip prop="id" label="ID" width="130" />
<el-table-column show-overflow-tooltip prop="name" :label="$t('名称')" width="120" />
<el-table-column show-overflow-tooltip prop="image" :label="$t('镜像')" width="140" />
<el-table-column show-overflow-tooltip prop="port" :label="$t('端口')" />
</el-table>
<div v-else >
<NoData height="200px" msg="No Data"></NoData>
</div>
</el-tab-pane>
<el-tab-pane :label="$t('镜像')">
<el-table style="height: 200px; width: 100%" v-if="dockerInfo.image.length > 0" :data="dockerInfo.image" border stripe >
<el-table-column type="selection" :selectable="selectable" width="40" />
<el-table-column show-overflow-tooltip prop="id" label="ID" width="130" />
<el-table-column show-overflow-tooltip prop="repository" :label="$t('仓库')" />
<el-table-column show-overflow-tooltip prop="size" :label="$t('大小')" width="100" />
<el-table-column show-overflow-tooltip prop="createTime" :label="$t('创建时间')" width="140" />
</el-table>
<div v-else >
<NoData height="200px" msg="No Data"></NoData>
</div>
</el-tab-pane>
<el-tab-pane :label="$t('网络')">
<el-table style="height: 200px; width: 100%" v-if="dockerInfo.network.length > 0" :data="dockerInfo.network" border stripe >
<el-table-column type="selection" :selectable="selectable" width="40" />
<el-table-column show-overflow-tooltip prop="name" :label="$t('名称')" width="140" />
<el-table-column show-overflow-tooltip prop="ip" label="IP" />
<el-table-column show-overflow-tooltip prop="gateway" :label="$t('网关')" width="120" />
<el-table-column show-overflow-tooltip prop="createTime" :label="$t('创建时间')" width="140" />
</el-table>
<div v-else >
<NoData height="200px" msg="No Data"></NoData>
</div>
</el-tab-pane>
<el-tab-pane :label="$t('数据卷')">
<el-table style="height: 200px; width: 100%" v-if="dockerInfo.volume.length > 0" :data="dockerInfo.volume" border stripe >
<el-table-column type="selection" :selectable="selectable" width="40" />
<el-table-column show-overflow-tooltip prop="name" :label="$t('名称')" width="140" />
<el-table-column show-overflow-tooltip prop="mount" :label="$t('挂载点')" />
<el-table-column show-overflow-tooltip prop="createTime" :label="$t('创建时间')" width="140" />
</el-table>
<div v-else >
<NoData height="200px" msg="No Data"></NoData>
</div>
</el-tab-pane>
</el-tabs>
</div>
</el-dialog>
</template>

<script>
import NoData from "@/components/NoData.vue";
import { computed, ref } from "vue";
import $ from "jquery";
import { http_base_url } from "@/env/BaseUrl";
export default {
name: 'DockerBlock',
components: {
NoData,
},
props:['sshKey', 'advance'],
setup(props) {
// 控制Dialog显示
const DialogVisible = ref(false);
const computedDialogVisible = computed(() => {
return DialogVisible.value && props.advance;
});
const loading = ref(false);
// Docker信息
const isDocker = ref(true);
const dockerInfo = ref({
container: [],
image: [],
network: [],
volume: [],
});
const getDockerInfo = (type) => {
const currentType = (type || 0) % 4;
$.ajax({
url: http_base_url + '/docker/info',
type:'post',
data:{
sshKey:props.sshKey,
type:currentType,
},
success(resp) {
if(resp.status == 'success') {
isDocker.value = true;
let container = dockerInfo.value.container;
let image = dockerInfo.value.image;
let network = dockerInfo.value.network;
let volume = dockerInfo.value.volume;
// 容器
if(currentType == 0) {
const containerArr = resp.data.split('$');
container = [];
for(let i = 0; i < containerArr.length; i++) {
const containerInfo = containerArr[i].split('@');
container.push({
id: containerInfo[0],
name: containerInfo[1],
status: containerInfo[2],
image: containerInfo[3],
port: containerInfo[4],
});
}
}
// 镜像
else if(currentType == 1) {
const imageArr = resp.data.split('$');
image = [];
for(let i = 0; i < imageArr.length; i++) {
const imageInfo = imageArr[i].split('@');
image.push({
id: imageInfo[0],
repository: imageInfo[1],
size: imageInfo[2],
createTime: imageInfo[3].split(' ')[0].substring(0, 10),
});
}
}
// 网络
else if(currentType == 2) {
const networkArr = resp.data.split('$');
network = [];
for(let i = 0; i < networkArr.length; i++) {
const networkInfo = networkArr[i].split('@');
network.push({
name: networkInfo[0],
ip: networkInfo[1],
gateway: networkInfo[2],
createTime: networkInfo[3].split(' ')[0].substring(0, 10),
});
}
}
// 数据卷
else if(currentType == 3) {
const volumeArr = resp.data.split('$');
volume = [];
for(let i = 0; i < volumeArr.length; i++) {
const volumeInfo = volumeArr[i].split('@');
volume.push({
name: volumeInfo[0],
mount: volumeInfo[1],
createTime: volumeInfo[2].split(' ')[0].substring(0, 10),
});
}
}
dockerInfo.value = {
container: container,
image: image,
network: network,
volume: volume,
};
}
else isDocker.value = false;
loading.value = false;
}
});
};
const handleTabClick = (tab) => {
loading.value = true;
getDockerInfo(tab.index);
};
// 重置
const reset = (deep=false) => {
if(deep) {
isDocker.value = true;
dockerInfo.value = {
container: [],
image: [],
network: [],
volume: [],
};
}
loading.value = false;
DialogVisible.value = false;
};
// 关闭
const closeDialog = (done) => {
setTimeout(() => {
reset();
},400);
DialogVisible.value = false;
if(done) done();
};
// 深度关闭
const deepCloseDialog = (done) => {
setTimeout(() => {
reset(true);
},400);
DialogVisible.value = false;
if(done) done();
};
return {
computedDialogVisible,
DialogVisible,
reset,
closeDialog,
deepCloseDialog,
dockerInfo,
getDockerInfo,
handleTabClick,
loading,
}
}
}
</script>

<style>
/* 禁止图片拖拽 */
img {
-webkit-user-drag: none; /* Safari */
-khtml-user-drag: none; /* Konqueror HTML */
-moz-user-drag: none; /* Firefox */
-o-user-drag: none; /* Opera */
}
/* 文本不可选中 */
.no-select {
user-select: none;
}
</style>
4 changes: 2 additions & 2 deletions front/terminal/src/components/advance/StatusMonitor.vue
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ export default {
},
processes: [],
network: {},
disk: [],
disk: {},
time: [],
});
const selectedNetwork = ref('all');
Expand Down Expand Up @@ -252,7 +252,7 @@ export default {
});
}
if(!disk[selectedDisk.value]) selectedDisk.value = "all";
// time
// Time
const time = statusInfo.value.time;
time.push(newTime);
Expand Down
2 changes: 1 addition & 1 deletion front/terminal/src/components/tcode/Tcode.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ export const SysTcode = {
'SAC': {
desc: i18n.global.t('协作'),
execFlow(context) {
if(!(context.proxy.sshKey && context.proxy.env.advance && context.proxy.env.server_user === 'root')) return;
if(!(context.proxy.sshKey && context.proxy.env.advance)) return;
context.proxy.cooperateGenRef.DialogVisible = true;
}
},
Expand Down
23 changes: 16 additions & 7 deletions front/terminal/src/locales/en_US.json
Original file line number Diff line number Diff line change
Expand Up @@ -81,13 +81,13 @@
"内容为空": "Empty",
"复制成功": "Copied",
"属性": "Attribute",
"位置": "path",
"包含": "contain",
"大小": "size",
"位置": "Path",
"包含": "Contain",
"大小": "Size",
"字节": "Bytes",
"修改时间": "mtime",
"访问时间": "atime",
"权限": "permissions",
"修改时间": "MTime",
"访问时间": "ATime",
"权限": "Permissions",
"权限修改": "Permissions Edit",
"0 个文件,0 个文件夹": "0 files, 0 folders",
" 个文件,": " files, ",
Expand Down Expand Up @@ -185,5 +185,14 @@
"上行": "Up",
"下行": "Down",
"读": "Read",
"写": "Write"
"写": "Write",
"容器": "Container",
"镜像": "Image",
"数据卷": "Volume",
"名称": "Name",
"端口": "Port",
"仓库": "Repository",
"创建时间": "Create Time",
"网关": "Gateway",
"挂载点": "Mount Point"
}
Loading

0 comments on commit 63f4507

Please sign in to comment.