首页 > 其他分享 >Gin+Xterm.js实现远程Kubernetes Pod(一)

Gin+Xterm.js实现远程Kubernetes Pod(一)

时间:2023-08-01 21:44:05浏览次数:47  
标签:Xterm Kubernetes xterm js ws 终端 pod resize

Xterm.js简介

xterm.js (https://xtermjs.org/)是一个开源的 JavaScript 库,它模拟了一个终端接口,可以在网页中嵌入一个完全功能的终端。这个库非常灵活,并且具有很多定制选项和插件系统。
下面是一些使用 xterm.js 的基本步骤:
  • 首先,需要在项目中安装 xterm.js。你可以直接从 npm 安装:
npm install xterm
  • 然后在 HTML 中创建一个容器来承载终端
<div id="terminal"></div>
  • 在你的 JavaScript 文件中,导入 Terminal 类并创建一个新的实例
import { Terminal } from 'xterm';

const term = new Terminal();
  • 把这个终端附加到 HTML 元素上
term.open(document.getElementById('terminal'));
  • 现在你就可以向终端写入数据了
term.write('Hello, World!');
  • 如果你想读取用户在终端中的输入,可以监听 onData 事件
term.onData(data => {
  console.log(data);
});

以上只是最基础的使用方法。xterm.js 提供了许多其他功能,如主题定制、附加插件(例如 FitAddon 可以自动调整终端大小,WebLinksAddon 可以捕获 URL 并将其变为可点击链接)、设置光标样式、更改字体大小等等。你可以访问 xterm.js 的 GitHub (https://github.com/xtermjs/xterm.js)仓库 或者 文档 来获取更详细的信息。

 

使用Gin、client-go的SPDYExecutor来执行远程命令

package main

import (
 "context"
 "encoding/json"
 "github.com/gin-gonic/gin"
 "github.com/gorilla/websocket"
 corev1 "k8s.io/api/core/v1"
 "k8s.io/client-go/kubernetes"
 "k8s.io/client-go/kubernetes/scheme"
 "k8s.io/client-go/tools/clientcmd"
 "k8s.io/client-go/tools/remotecommand"
 "log"
 "net/http"
)

// websocket 升级器配置
var upgrader = websocket.Upgrader{
 CheckOrigin: func(r *http.Request) bool {
  return true
 },
}

// WSClient 结构体,封装了 WebSocket 连接和 resize 通道,用于在 WebSocket 和 remotecommand 之间进行数据交换。
type WSClient struct {
 // WebSocket 连接对象
 ws *websocket.Conn
 // TerminalSize 类型的通道,用于传输窗口大小调整事件
 resize chan remotecommand.TerminalSize
}

// MSG 结构体,用于解析从 WebSocket 接收到的消息。
type MSG struct {
 // 消息类型字段
 MsgType string `json:"msg_type"`
 Rows uint16 `json:"rows"`
 Cols uint16 `json:"cols"`
 // 输入消息的数据字段
 Data string `json:"data"`
}

// WSClient 的 Read 方法,实现了 io.Reader 接口,从 websocket 中读取数据。
func (c *WSClient) Read(p []byte) (n int, err error) {
 // 从 WebSocket 中读取消息
 _, message, err := c.ws.ReadMessage()
 if err != nil {
  return 0, err
 }
 var msg MSG
 if err := json.Unmarshal(message, &msg); err != nil {
  return 0, err
 }

 // 根据消息类型进行不同的处理
 switch msg.MsgType {
 // 如果是窗口调整消息
 case "resize":
  winSize := remotecommand.TerminalSize{
   Width: msg.Cols,
   Height: msg.Rows,
  }
  // 将 TerminalSize 对象发送到 resize 通道
  c.resize <- winSize
  return 0, nil
 // 如果是输入消息
 case "input":
  copy(p, msg.Data)
  return len(msg.Data), err
 }
 return 0, nil
}

// WSClient 的 Write 方法,实现了 io.Writer 接口,将数据写入 websocket。
func (c *WSClient) Write(p []byte) (n int, err error) {
 // 将数据作为文本消息写入 WebSocket
 err = c.ws.WriteMessage(websocket.TextMessage, p)
 return len(p), err
}

// Next WSClient 的 Next 方法,用于从 resize 通道获取下一个 TerminalSize 事件。
func (c *WSClient) Next() *remotecommand.TerminalSize {
 // 从 resize 通道读取 TerminalSize 对象
 size := <-c.resize
 return &size
}

// podSSH 函数,这是主要的 SSH 功能逻辑,使用 kubernetes client-go 的 SPDY executor 来执行远程命令。
func podSSH(wsClient *WSClient, q query) {
 // 使用 kubeconfig 文件初始化 kubernetes 客户端配置
 // 请注意,你应该替换 ./config 为你的 kubeconfig 文件路径
 restClientConfig, err := clientcmd.BuildConfigFromFlags("", "./config")
 if err != nil {
  log.Fatalf("Failed to build config: %v", err)
 }

 // 根据配置创建 kubernetes 客户端
 clientSet, err := kubernetes.NewForConfig(restClientConfig)
 if err != nil {
  log.Fatalf("Failed to create client: %v", err)
 }
 // 构造一个用于执行远程命令的请求
 request := clientSet.CoreV1().RESTClient().Post().
  Resource("pods").
  Namespace(q.Namespace).
  Name(q.PodName).
  SubResource("exec").
  VersionedParams(&corev1.PodExecOptions{
   Container: q.ContainerName,
   Command: []string{
    q.Command,
   },
   Stdout: true,
   Stdin:  true,
   Stderr: true,
   TTY:    true,
  }, scheme.ParameterCodec)
 // 创建 SPDY executor,用于后续的 Stream 操作
 exec, err := remotecommand.NewSPDYExecutor(restClientConfig, "POST", request.URL())
 if err != nil {
  log.Fatalf("Failed to initialize executor: %v", err)
 }

 // 开始进行 Stream 操作,即通过 websocket 执行命令
 err = exec.StreamWithContext(context.Background(), remotecommand.StreamOptions{
  Stderr:            wsClient,
  Stdout:            wsClient,
  Stdin:             wsClient,
  Tty:               true,
  TerminalSizeQueue: wsClient,
 })
 if err != nil {
  log.Fatalf("Failed to start stream: %v", err)
 }
}

// query 结构体,用于解析和验证查询参数
type query struct {
 Namespace     string `form:"namespace" binding:"required"`
 PodName       string `form:"pod_name" binding:"required"`
 ContainerName string `form:"container_name" binding:"required"`
 Command       string `form:"command" binding:"required"`
}

func main() {
 router := gin.Default()
 router.LoadHTMLGlob("templates/*")
 router.GET("/", func(ctx *gin.Context) {
  ctx.HTML(http.StatusOK, "ssh.html", nil)
 })

 // 设置 /ssh 路由
 router.GET("/ssh", func(ctx *gin.Context) {
  var r query
  if err := ctx.ShouldBindQuery(&r); err != nil {
   ctx.JSON(http.StatusBadRequest, gin.H{
    "err": err.Error(),
   })
   return
  }
  // 将 HTTP 连接升级为 websocket 连接
  ws, err := upgrader.Upgrade(ctx.Writer, ctx.Request, nil)
  if err != nil {
   log.Printf("Failed to upgrade connection: %v", err)
   return
  }
  // 使用 podSSH 函数处理 websocket 连接
  podSSH(&WSClient{
   ws:     ws,
   resize: make(chan remotecommand.TerminalSize),
  }, r)
 })
 router.Run(":9191")
}
  • 后端全部代码
https://gitee.com/KubeSec/pod-webssh/tree/master/pod-ssh

使用vue-admin-template和Xterm.js实现Web终端

https://github.com/PanJiaChen/vue-admin-template

https://github.com/xtermjs/xterm.js

  • 下载vue-admin-template项目

https://github.com/PanJiaChen/vue-admin-template.git

  • 安装xterm.js及插件

npm install
npm install xterm
npm install --save xterm-addon-web-links
npm install --save xterm-addon-fit
npm install -S xterm-style
  • 打开vue-admin-template项目,在src/views目录下新建目录pod-ssh,在pod-ssh目录下新建index.vue代码如下

<template>
  <div class="app-container">
    <!-- 使用 Element UI 的表单组件创建一个带有标签和输入框的表单 -->
    <el-form ref="form" :model="form" :inline="true" label-width="120px">
      <el-form-item label="namespace"> <!-- namespace 输入框 -->
        <el-input v-model="form.namespace" />
      </el-form-item>
      <el-form-item label="pod name"> <!-- pod 名称输入框 -->
        <el-input v-model="form.pod_name" />
      </el-form-item>
      <el-form-item label="container name"> <!-- 容器名称输入框 -->
        <el-input v-model="form.container_name" />
      </el-form-item>
      <el-form-item label="Command"> <!-- 命令选择框 -->
        <el-select v-model="form.command" placeholder="bash">
          <el-option label="bash" value="bash" />
          <el-option label="sh" value="sh" />
        </el-select>
      </el-form-item>
      <el-form-item> <!-- 提交按钮 -->
        <el-button type="primary" @click="onSubmit">Create</el-button>
      </el-form-item>
      <div id="terminal" /> <!-- 终端视图容器 -->
    </el-form>
  </div>
</template>

<script>
import { Terminal } from 'xterm' // 导入 xterm 包,用于创建和操作终端对象
import { common as xtermTheme } from 'xterm-style' // 导入 xterm 样式主题
import 'xterm/css/xterm.css' // 导入 xterm CSS 样式
import { FitAddon } from 'xterm-addon-fit' // 导入 xterm fit 插件,用于调整终端大小
import { WebLinksAddon } from 'xterm-addon-web-links' // 导入 xterm web-links 插件,可以捕获 URL 并将其转换为可点击链接
import 'xterm/lib/xterm.js' // 导入 xterm 库

export default {
  data() {
    return {
      form: {
        namespace: 'default', // 默认命名空间为 "default"
        command: 'bash', // 默认 shell 命令为 "bash"
        pod_name: 'nginx', // 默认 Pod 名称为 "nginx"
        container_name: 'nginx' // 默认容器名称为 "nginx"
      },
    }
  },
  methods: {
    onSubmit() {
      // 创建一个新的 Terminal 对象
      const xterm = new Terminal({
        theme: xtermTheme,
        rendererType: 'canvas',
        convertEol: true,
        cursorBlink: true
      })

      // 创建并加载 FitAddon 和 WebLinksAddon
      const fitAddon = new FitAddon()
      xterm.loadAddon(fitAddon)
      xterm.loadAddon(new WebLinksAddon())

      // 打开这个终端,并附加到 HTML 元素上
      xterm.open(document.getElementById('terminal'))

      // 调整终端的大小以适应其父元素
      fitAddon.fit()

      // 创建一个新的 WebSocket 连接,并通过 URL 参数传递 pod, namespace, container 和 command 信息
      const ws = new WebSocket('ws://127.0.0.1:9191/ssh?namespace=' + this.form.namespace + '&pod_name=' + this.form.pod_name + '&container_name=' + this.form.container_name + '&command=' + this.form.command)

      // 当 WebSocket 连接打开时,发送一个 resize 消息给服务器,告诉它终端的尺寸
      ws.onopen = function() {
        ws.send(JSON.stringify({
          msg_type: 'resize',
          rows: xterm.rows,
          cols: xterm.cols
        }))
      }

      // 当从服务器收到消息时,写入终端显示
      ws.onmessage = function(evt) {
        xterm.write(evt.data)
      }

      // 当发生错误时,也写入终端显示
      ws.onerror = function(evt) {
        xterm.write(evt.data)
      }

      // 当窗口尺寸变化时,重新调整终端的尺寸,并发送一个新的 resize 消息给服务器
      window.addEventListener('resize', function() {
        fitAddon.fit()
        ws.send(JSON.stringify({
          msg_type: 'resize',
          rows: xterm.rows,
          cols: xterm.cols
        }))
      })

      // 当在终端中键入字符时,发送一个 input 消息给服务器
      xterm.onData((b) => {
        ws.send(JSON.stringify({
          msg_type: 'input',
          data: b
        }))
      })
    }
  }
}
</script>

<style scoped>
.line{
  text-align: center;
}
</style>

在src/router/index.js文件中增加路由

{
    path: '/pod-ssh',
    component: Layout,
    children: [
      {
        path: 'pod-ssh',
        name: 'SSH',
        component: () => import('@/views/pod-ssh/index'),
        meta: { title: 'SSH', icon: 'form' }
      }
    ]
  },
  • 启动项目

npm install
npm run dev
  • 前端全部代码

https://gitee.com/KubeSec/pod-webssh/tree/master/pod-webssh

 

测试

  • 在kubernetes中创建测试的Pod

apiVersion: v1
kind: Pod
metadata:
  name: nginx
spec:
  containers:
  - name: nginx
    image: nginx:1.14.2
    ports:
    - containerPort: 80

访问http://localhost:9528/#/pod-ssh/pod-ssh

 

标签:Xterm,Kubernetes,xterm,js,ws,终端,pod,resize
From: https://www.cnblogs.com/0x00000/p/17557770.html

相关文章

  • 基于NodeJS电子商城管理系统
    随着Internet的发展,人们的日常生活已经离不开网络。未来人们的生活与工作将变得越来越数字化、网络化和电子化。电子商城,它将是直接市场营销的最新形式。本论文是以构建电子商城管理系统为目标,使用Node.js制作,由前台和后台两大部分组成。着重论述了系统设计分析,系统的实现包括前台:......
  • 21 os/sys/json/subprocess模块
    os模块使用前老规矩:加上importos1.创建目录(文件夹)makedirsos.mkdir(r'aaa')#在os所在的路径下创建一个aaa文件夹(只能创建一个单级目录)os.makedir((r'ccc')os.makedirs(r'bbb/ccc/ddd')#makedirs支持创建多级目录2.删除目录(文件夹)removediros.rmdir(r'aaa')#可......
  • 最新中国地区各种数据大全如JSON、CSV等
    ✍️作者简介:沫小北(专注于Android、Web、TCP/IP等技术方向)......
  • Json格式的数据获取,电商API接口获取商品详情
    为了进行淘宝的API开发,首先我们需要做下面几件事情。1)开发者注册一个账号注册链接2)然后为每个淘宝应用注册一个应用程序键(AppKey)。3)下载淘宝API的SDK并掌握基本的API基础知识和调用4)利用SDK接口和对象,传入AppKey或者必要的时候获取并传入SessionKey来进行程序开......
  • JSON格式电商数据API接口,便捷式用法
    item_search-按关键字搜索商品请求参数注册Key和secret测试请求参数:q=女装&start_price=0&end_price=0&page=1&cat=0&discount_only=&sort=&page_size=40&seller_info=no&nick=&seller_info=&nick=&ppath=&imgid=&filter=参数说明:q:搜索关键字cat:......
  • 淘宝1688拼多多京东商品详情,JSON格式API数据
    API接口入口请求参数请求参数:num_iid=652874751412&is_promotion=1参数说明:num_iid:淘宝商品IDis_promotion:是否获取取促销价响应参数Version:Date:2022-04-04名称类型必须示例值描述itemitem[]1宝贝详情数据num_iidBigint1520813250866宝贝IDtitleString1三刃木折叠刀过安检创......
  • 商品API接口聚全,价格监控,订单类接口,json格式数据
    行业背景:电商行业数据分析api接口,品牌监控api接口,商品价格监控api接口,电商数据采集,对品牌和电商企业推出的的数据分析产品,致力于帮助品牌方全面掌握市场行情和趋势,定位竞争对手,发现市场机会点,对企业制定电商策略形成有力支持。接口简介:目前支持JD和tmall及政采云3个平台,以可视化数......
  • PHPJSON解析 原理与用法
    PHPJSON解析原理与用法在现代编程语言中,JSON已经成为最流行的数据交换格式之一。而PHP作为一门流行的服务器端编程语言,自然也提供了一套完整的JSON解析库。本文将介绍PHPJSON解析的原理与用法,以帮助PHP开发者更好地使用JSON格式数据进行开发。什么是JSON?JSON代表“JavaScript......
  • js处理数组,删除指定元素
    //获取元素下标Array.prototype.indexOf=function(val){for(vari=0;i<this.length;i++){if(this[i]==val){returni;}}return-1;}//根据下标删除元素Ar......
  • 在 浏览器中的找到 span 标签中内容是 “加入购物车” 的按钮 并用js代码模拟点击
    在浏览器中的找到span标签中内容是“加入购物车”的按钮并用js代码模拟点击functionsimulateButtonClick(){//找到包含“加入购物车”文本的所有span标签constspanElements=document.getElementsByTagName("span");//遍历所有的span标签for(leti=0;i......