当前位置: 首页 > news >正文

【GO】k8s 管理系统项目23[前端部分–工作负载-Pod]

k8s 管理系统项目[前端部分–工作负载-Deployment]

1. 代码部分

1.1 准备工作

由于Pod页面和Deployment内容差不多.那么就直接把Deployment的内容复制过来.再做修改.

  1. 替换Deployment为Pod
  2. 替换Deploy为Pod
  3. 替换deployment为pod
  4. 替换deploy为pod
  5. 禁用新增的按钮,删除新增方法,表单
  6. 删除扩容和重启的按钮,方法,抽屉

1.2 Pod前端代码

src/views/workload/Pod.vue

<template><div class="pod"><el-row><!-- 头部1 --><el-col :span="24"><div><el-card class="pod-head-card" shadow="never" :body-style="{padding:'10px'}"><el-row><el-col :span="6"><div><span>命名空间: </span><el-select v-model="namespaceValue" filterable placeholder="请选择"><el-optionv-for="(item, index) in namespaceList":key="index":label="item.metadata.name":value="item.metadata.name"></el-option></el-select></div></el-col><el-col :span="2" :offset="16"><div><el-button style="border-radius:2px;" icon="Refresh" plain @click="getPods()">刷新</el-button></div></el-col></el-row></el-card></div></el-col><!-- 头部2 --><el-col :span="24"><div><el-card class="pod-head-card" shadow="never" :body-style="{padding:'10px'}"><el-row><el-col :span="2"><div><el-button disabled style="border-radius:2px;" icon="Edit" type="primary">创建</el-button></div></el-col><el-col :span="6"><div><el-input class="pod-head-search" clearable placeholder="请输入" v-model="searchInput"></el-input><el-button style="border-radius:2px;" icon="Search" type="primary" plain @click="getPods()">搜索</el-button></div></el-col></el-row></el-card></div></el-col><!-- 数据表格 --><el-col :span="24"><div><el-card class="pod-body-card" shadow="never" :body-style="{padding:'5px'}"><!-- 数据表格 --><!-- row-key 用来定义行数据的key,结合expand-row-keys使用,往expandKeys中增加key来展开行 --><!-- expand-row-keys 展开的行的key数组 --><!-- expand-change 展开触发时,调用这个方法 --><el-tablestyle="width:100%;font-size:12px;margin-bottom:10px;":data="podList"v-loading="appLoading":row-key="getRowKeys":expand-row-keys="expandKeys"@expand-change="expandChange"><el-table-column width="10"></el-table-column><!-- 展开 --><el-table-column type="expand"><!-- 插槽,里面是展开的内容,props标识展开的行的数据 --><template #default="props"><el-tabs v-model="activeName" type="card"><!-- tab容器标签页 --><el-tab-pane label="容器" name="container"><el-card shadow="never" style="border-radius:1px;" :body-style="{padding:'5px'}"><!-- 嵌套数据表格 --><el-tablestyle="width:100%;font-size:12px;":data="props.row.spec.containers"><el-table-column align=left prop="name" label="容器名"></el-table-column><el-table-column align=left prop="image" label="镜像"></el-table-column><el-table-column align=center label="Pod IP"><span>{{ props.row.status.podIP }}</span></el-table-column><el-table-column align=center prop="args" label="启动命令"></el-table-column><el-table-column align=center label="环境变量"><template v-slot="scope"><!-- 气泡弹出框,内容是所有的环境变量 --><el-popover :width="500" placement="left" trigger="hover"><el-table style="width:100%;font-size:12px;" size="mini" :show-header="false" :data="scope.row.env"><el-table-column property="name" label="名称"></el-table-column><el-table-column property="value" label="值"></el-table-column></el-table><template #reference><el-button size="small">此处查看</el-button></template></el-popover></template></el-table-column></el-table></el-card></el-tab-pane><!-- tab日志标签页 --><el-tab-pane label="日志" name="log"><el-card shadow="never" style="border-radius:1px;" :body-style="{padding:'5px'}"><el-row :gutter="10"><el-col :span="3"><!-- 容器选择框 --><el-select size="small" v-model="containerValue" placeholder="请选择"><el-option v-for="item in containerList" :key="item" :value="item"></el-option></el-select></el-col><el-col :span="2"><!-- 查看日志按钮 --><el-button style="border-radius:2px;" size="small" type="primary" @click="getPodLog(props.row.metadata.name)">查看</el-button></el-col><el-col :span="24" style="margin-top: 5px"><!-- 显示日志内容 --><el-card shadow="never" class="pod-body-log-card" :body-style="{padding:'5px'}"><span class="pod-body-log-span">{{ logContent }}</span></el-card></el-col></el-row></el-card></el-tab-pane><!-- tab终端标签页 --><el-tab-pane label="终端" name="shell"><el-card shadow="never" style="border-radius:1px;" :body-style="{padding:'5px'}"><el-row :gutter="10"><el-col :span="3"><!-- 容器选择框 --><el-select size="small" v-model="containerValue" placeholder="请选择"><el-option v-for="item in containerList" :key="item" :value="item"></el-option></el-select></el-col><el-col :span="1"><!-- 连接按钮 --><el-button style="border-radius:2px;" size="small" type="primary" @click="initSocket(props.row)">连接</el-button></el-col><el-col :span="1"><!-- 关闭连接按钮 --><el-button style="border-radius:2px;" size="small" type="danger" @click="closeSocket()">关闭</el-button></el-col><el-col :span="24" style="margin-top: 5px"><el-card shadow="never" class="pod-body-shell-card" :body-style="{padding:'5px'}"><!-- xterm虚拟终端 --><div id="xterm"></div></el-card></el-col></el-row></el-card></el-tab-pane></el-tabs></template></el-table-column><el-table-column align=left label="Pod名"><template v-slot="scope"><!-- 三元运算:expandMap[scope.row.metadata.name]为1则触发关闭(expandedRows为空数组),为0则触发展开expandedRows有值 --><a class="pod-body-podname" @click="expandMap[scope.row.metadata.name] ? expandChange(scope.row, []) : expandChange(scope.row, [scope.row])">{{ scope.row.metadata.name }}</a></template></el-table-column><el-table-column align=center min-width="150" label="节点"><template v-slot="scope"><el-tag v-if="scope.row.spec.nodeName !== undefined" type="warning">{{ scope.row.spec.nodeName }}</el-tag></template></el-table-column><el-table-column align=center label="状态"><template v-slot="scope"><div :class="{'success-dot':scope.row.status.phase == 'Running' || scope.row.status.phase == 'Succeeded', 'warning-dot':scope.row.status.phase == 'Pending', 'error-dot':scope.row.status.phase != 'Running' && scope.row.status.phase != 'Pending' && scope.row.status.phase != 'Succeeded'}"></div><span :class="{'success-status':scope.row.status.phase == 'Running' || scope.row.status.phase == 'Succeeded', 'warning-status':scope.row.status.phase == 'Pending', 'error-status':scope.row.status.phase != 'Running' && scope.row.status.phase != 'Pending' && scope.row.status.phase != 'Succeeded'}">{{ scope.row.status.phase }} </span></template></el-table-column><el-table-column align=center label="重启数"><template v-slot="scope"><span>{{ restartTotal(scope) }} </span></template></el-table-column><el-table-column align=center min-width="100" label="创建时间"><template v-slot="scope"><el-tag type="info">{{ timeTrans(scope.row.metadata.creationTimestamp) }} </el-tag></template></el-table-column><el-table-column align=center label="操作" width="200"><template v-slot="scope"><el-button size="small" style="border-radius:2px;" icon="Edit" type="primary" plain @click="getPodDetail(scope)">YAML</el-button><el-button size="small" style="border-radius:2px;" icon="Delete" type="danger" @click="handleConfirm(scope, '删除', delPod)">删除</el-button></template></el-table-column></el-table><el-paginationclass="pod-body-pagination"background@size-change="handleSizeChange"@current-change="handleCurrentChange":current-page="currentPage":page-sizes="pagesizeList":page-size="pagesize"layout="total, sizes, prev, pager, next, jumper":total="podTotal"></el-pagination></el-card></div></el-col></el-row><!-- 展示YAML信息的弹框 --><el-dialog title="YAML信息" v-model="yamlDialog" width="45%" top="5%"><codemirror:value="contentYaml"border:options="cmOptions"height="500"style="font-size:14px;"@change="onChange"></codemirror><template #footer><span class="dialog-footer"><el-button @click="yamlDialog = false">取 消</el-button><el-button type="primary" @click="updatePod()">更 新</el-button></span></template></el-dialog></div>
</template><script>
import common from "../common/Config";
import httpClient from '../../utils/request';
//引入xterm终端依赖
import { Terminal } from 'xterm';
import { FitAddon } from 'xterm-addon-fit';
import 'xterm/css/xterm.css';
import 'xterm/lib/xterm.js';
import yaml2obj from 'js-yaml';
import json2yaml from 'json2yaml';
export default {data() {return {//编辑器配置cmOptions: common.cmOptions,contentYaml: '',//分页currentPage: 1,pagesize: 10,pagesizeList: [10, 20, 30],//searchInput: '',namespaceValue: 'default',namespaceList: [],namespaceListUrl: common.k8sNamespaceList,appLoading: false,podList: [],podTotal: 0,getPodsData: {url: common.k8sPodList,params: {filter_name: '',namespace: '',page: '',limit: '',}},//详情podDetail: {},getPodDetailData: {url: common.k8sPodDetail,params: {pod_name: '',namespace: ''}},//yaml更新yamlDialog: false,updatePodData: {url: common.k8sPodUpdate,params: {namespace: '',content: ''}},//删除delPodData: {url: common.k8sPodDel,params: {pod_name: '',namespace: ''}},//expand扩展activeName: 'container',expandKeys: [],expandMap: {},//日志containerList: {},containerValue: '',getPodContainerData: {url: common.k8sPodContainer,params: {pod_name: '',namespace: ''}},logContent: '',getPodLogData: {url: common.k8sPodLog,params: {container_name: '',pod_name: '',namespace: ''}},//terminalterm: null,socket: null}},methods: {transYaml(content) {return json2yaml.stringify(content)},transObj(content) {return yaml2obj.load(content)},onChange(val) {this.contentYaml = val},handleSizeChange(size) {this.pagesize = size;this.getPods()},handleCurrentChange(currentPage) {this.currentPage = currentPage;this.getPods()},handleClose(done) {this.$confirm('确认关闭?').then(() => {done();}).catch(() => {});},ellipsis(value) {return value.length>15?value.substring(0,15)+'...':value},timeTrans(timestamp) {let date = new Date(new Date(timestamp).getTime() + 8 * 3600 * 1000)date = date.toJSON();date = date.substring(0, 19).replace('T', ' ')return date},restartTotal(e) {let index, sum = 0let containerStatuses = e.row.status.containerStatusesfor ( index in containerStatuses) {sum = sum + containerStatuses[index].restartCount}return sum},getNamespaces() {httpClient.get(this.namespaceListUrl).then(res => {this.namespaceList = res.data.items}).catch(res => {this.$message.error({message: res.msg})})},getPods() {this.appLoading = truethis.getPodsData.params.filter_name = this.searchInputthis.getPodsData.params.namespace = this.namespaceValuethis.getPodsData.params.page = this.currentPagethis.getPodsData.params.limit = this.pagesizehttpClient.get(this.getPodsData.url, {params: this.getPodsData.params}).then(res => {this.podList = res.data.itemsthis.podTotal = res.data.total}).catch(res => {this.$message.error({message: res.msg})})this.appLoading = false},getPodDetail(e) {this.getPodDetailData.params.pod_name = e.row.metadata.namethis.getPodDetailData.params.namespace = this.namespaceValuehttpClient.get(this.getPodDetailData.url, {params: this.getPodDetailData.params}).then(res => {this.podDetail = res.datathis.contentYaml = this.transYaml(this.podDetail)this.yamlDialog = true}).catch(res => {this.$message.error({message: res.msg})})},updatePod() {let content = JSON.stringify(this.transObj(this.contentYaml))this.updatePodData.params.namespace = this.namespaceValuethis.updatePodData.params.content = contenthttpClient.put(this.updatePodData.url, this.updatePodData.params).then(res => {this.$message.success({message: res.msg})}).catch(res => {this.$message.error({message: res.msg})})this.yamlDialog = false},delPod(e) {this.delPodData.params.pod_name = e.row.metadata.namethis.delPodData.params.namespace = this.namespaceValuehttpClient.delete(this.delPodData.url, {data: this.delPodData.params}).then(res => {this.getPods()this.$message.success({message: res.msg})}).catch(res => {this.$message.error({message: res.msg})})},handleConfirm(obj, operateName, fn) {this.confirmContent = '确认继续 ' + operateName + ' 操作吗?'this.$confirm(this.confirmContent,'提示',{confirmButtonText: '确定',cancelButtonText: '取消',}).then(() => {fn(obj)}).catch(() => {this.$message.info({message: '已取消操作'})})},getRowKeys(row) {return row.metadata.name},//row,展开的当前行的数据//expandedRows,展开的所有行的数据组成的数组,但是这里用法是只会有一行,也就是数组长度永远为1expandChange(row, expandedRows) {//初始化变量//清空expandKeys,代表关闭所有展开的行this.expandKeys = []//清空日志内容this.logContent= ''//清空containervalue,展开时不显示上次的值this.containerValue = ''//将tab标签页顶部页面调成容器this.activeName = 'container'//expandedRows.length == 1表示展开,expandedRows.length == 0 表示关闭if (expandedRows.length > 0) {//expandMap key表示展开过的行的key,值为1表示展开标记,值为0表示关闭标记//expandMap用于数据表格点击name的展开,用于判断这一行是展开还是关闭的行为this.expandMap[row.metadata.name] = 1//将expandMap除了row.metadata.name,其他key的值都置为0this.setExpandMap(row.metadata.name)//这里才是真正的展开,将row.metadata.name添加到expandKeys数组中展开,然后执行方法获取containerthis.expandKeys.push(row.metadata.name)this. getPodContainer(row)} else {//关闭标记this.expandMap[row.metadata.name] = 0}},//匹配expandMap中podName,不相等的全都置为0,意为除了podName这行,其他全都标记关闭setExpandMap(podName) {let keyfor ( key in this.expandMap ) {key !== podName ? this.expandMap[key] = 0 : ''}},getPodContainer(row) {this.getPodContainerData.params.pod_name = row.metadata.namethis.getPodContainerData.params.namespace = this.namespaceValuehttpClient.get(this.getPodContainerData.url, {params: this.getPodContainerData.params}).then(res => {this.containerList = res.datathis.containerValue = this.containerList[0]}).catch(res => {this.$message.error({message: res.msg})})},getPodLog(podName) {this.getPodLogData.params.pod_name = podNamethis.getPodLogData.params.container_name = this.containerValuethis.getPodLogData.params.namespace = this.namespaceValuehttpClient.get(this.getPodLogData.url, {params: this.getPodLogData.params}).then(res => {this.logContent = res.data}).catch(res => {this.$message.error({message: res.msg})})},initTerm() {//初始化xterm实例this.term = new Terminal({rendererType: 'canvas', //渲染类型rows: 30, //行数cols: 110,convertEol: false, //启用时,光标将设置为下一行的开头scrollback: 10, //终端中的回滚量disableStdin: false, //是否应禁用输入cursorStyle: 'underline', //光标样式cursorBlink: true, //光标闪烁theme: {foreground: 'white', //字体background: '#060101', //背景色cursor: 'help' //设置光标}});//绑定domthis.term.open(document.getElementById('xterm'))//终端适应父元素大小const fitAddon = new FitAddon()this.term.loadAddon(fitAddon)fitAddon.fit();//获取终端的焦点this.term.focus();let _this = this; //一定要重新定义一个this,不然this指向会出问题//onData方法用于定义输入的动作this.term.onData(function (key) {// 这里key值是输入的值,数据格式就是后端定义的 {"operation":"stdin","data":"ls"}let msgOrder = {operation: 'stdin',data: key,};//发送数据_this.socket.send(JSON.stringify(msgOrder));});//发送resize请求let msgOrder2 = {operation: 'resize',cols: this.term.cols,rows: this.term.rows,};this.socket.send(JSON.stringify(msgOrder2))},//初始化websocketinitSocket(row) {//定义websocket连接地址let terminalWsUrl = common.k8sTerminalWs + "?pod_name=" + row.metadata.name + "&container_name=" + this.containerValue + "&namespace=" + this.namespaceValue//实例化this.socket = new WebSocket(terminalWsUrl);//关闭连接时的方法this.socketOnClose();//建立连接时的方法this.socketOnOpen();//接收消息的方法this.socketOnMessage();//报错时的方法this.socketOnError();},socketOnOpen() {this.socket.onopen = () => {//简历连接成功后,初始化虚拟终端this.initTerm()}},socketOnMessage() {this.socket.onmessage = (msg) => {//接收到消息后将字符串转为对象,输出data内容let content = JSON.parse(msg.data)this.term.write(content.data)}},socketOnClose() {this.socket.onclose = () => {//关闭连接后打印在终端里this.term.write("链接已关闭")}},socketOnError() {this.socket.onerror = () => {console.log('socket 链接失败')}},//关闭连接closeSocket() {//若没有实例化,则不需要关闭if (this.socket === null) {return}this.term.write("链接关闭中。。。")this.socket.close()}},watch: {namespaceValue: {handler() {localStorage.setItem('namespace', this.namespaceValue)this.currentPage = 1this.getPods()}},//若tab标签页切到日志,则重新加载日志内容activeName: {handler() {if ( this.activeName == 'log' ) {this.expandKeys.length == 1 ? this.getPodLog(this.expandKeys[0]) : ''}}}},beforeMount() {if (localStorage.getItem('namespace') !== undefined && localStorage.getItem('namespace') !== null) {this.namespaceValue = localStorage.getItem('namespace')}this.getNamespaces()this.getPods()},beforeUnmount() {//若websocket连接没有关闭,则在改生命周期关闭if ( this.socket !== null ) {this.socket.close()}},
}
</script><style scoped>
.pod-head-card,.pod-body-card {border-radius: 1px;margin-bottom: 5px;
}
.pod-head-search {width:160px;margin-right:10px;
}
.pod-body-podname {color: #4795EE;
}
.pod-body-podname:hover {color: rgb(84, 138, 238);cursor: pointer;font-weight: bold;
}
/* pod状态栏圆点的css实现 */
.success-dot{display:inline-block;width: 7px;height:7px;background: rgb(27, 202, 21);border-radius:50%;border:1px solid rgb(27, 202, 21);margin-right: 10px;
}
.warning-dot{display:inline-block;width: 7px;height:7px;background: rgb(233, 200, 16);border-radius:50%;border:1px solid rgb(233, 200, 16);margin-right: 10px;
}
.error-dot{display:inline-block;width: 7px;height:7px;background: rgb(226, 23, 23);border-radius:50%;border:1px solid rgb(226, 23, 23);margin-right: 10px;
}
.success-status {color: rgb(27, 202, 21);
}
.warning-status {color: rgb(233, 200, 16);
}
.error-status {color: rgb(226, 23, 23);
}
/deep/ .el-tabs__item {font-size: 12px;
}
/deep/ .el-tabs__header {margin-bottom: 8px;
}
.pod-body-log-card, .pod-body-shell-card {border-radius:1px;height:600px;overflow:auto;background-color: #060101;
}
.pod-body-log-card {color: aliceblue;
}
.pod-body-log-span {white-space:pre;
}
</style>

2. 功能测试

2.1 获取pod清单和分页

请添加图片描述
请添加图片描述

2.2 获取pod详细信息

2.2.1 pod详情

请添加图片描述

2.2.2 Pod环境变量

请添加图片描述

2.2.2 Pod日志

请添加图片描述

2.2.4 控制台

请添加图片描述

2.3 Yaml

请添加图片描述

2.4 删除Pod

请添加图片描述

请添加图片描述

请添加图片描述

3. 两个小bug

3.1 router错误

之前写后端路由时/api/k8s/pod/detail写成了/api/k8s/pods/detail

造成获取yaml时无法获得消息.

尝试用api调用发现问题,修改了router下的路由问题解决

3.2 翻页的问题

之前后端GetPods中取total写成了

	filtered := selectableData.Filter()// 排序和分页data := filtered.Sort().Paginate()total := len(data.GenericDataList)

改了下问题解决

	filtered := selectableData.Filter()total := len(filtered.GenericDataList)// 排序和分页data := filtered.Sort().Paginate()
http://www.lryc.cn/news/22936.html

相关文章:

  • rabbitmq在linux系统下安装步骤
  • 阿里测试员晒薪资条,看完真的扎心了...
  • 内网渗透辅助工具集Yasso
  • Spring笔记(1):概述
  • 工程机械焊接件焊接结构件三维扫描检测外观质量控制-CASAIM三维扫描检测仪
  • 使用linux部署项目步骤
  • pt02-list-tuple-dir
  • 高端电器新十年,求解「竞速突围」
  • [Android Studio] Android Studio使用keytool工具读取Debug 调试版数字证书以及release 发布版数字证书
  • 2023年金三银四必备软件测试常见面试题1500问!!!【测试思维篇】
  • 推荐四款自用的电脑神器
  • CSDN 竞赛 32 期
  • 【路径规划】基于前向动态规划算法在地形上找到最佳路径(Matlab代码实现)
  • spring boot maven打包jar包太大,怎么办?这个方法解决你的烦恼
  • Spring之AOP理解及使用
  • 微信小程序和webview使用postMessage交互
  • pytorch-自动求导机制,构建计算图进行反向传播,需要注意inplace操作导致的报错,梯度属性变化
  • 【Project】项目管理软件学习笔记
  • 【算法设计-分治思想】快速幂与龟速乘
  • Kafka(十一) 如何保证数据的不重复和不丢失
  • 解决树莓派 bullseye (11) 系统无法通过 xrdp 远程连接的问题
  • 微信公众号历史作品定向采集
  • Vue学习笔记(3)
  • Marshmallow 库
  • 【BN层的作用】论文阅读 | How Does Batch Normalization Help Optimization?
  • re.sub()用法的详细介绍
  • 【Python数据挖掘入门】2.2文本分析-中文分词(jieba库cut方法/自定义词典load_userdict/语料库分词)
  • Meta利用视觉信息来优化3D音频模型,未来将用于AR/VR
  • openlayers加载离线地图并实现深色地图
  • socket,tcp,http三者之间的区别和原理