gin框架使用websocket实现进入容器内部执行命令
文章目录
- 1. 先决条件
- 2. gin框架实现
- 3. 测试用html文件
- 4. 需要完善
1. 先决条件
docker开放远程API端口
2. gin框架实现
type GetCommandResultRequire struct {
IpAddr string `json:"ip_addr"` //传入要控制容器的ip地址
ContainerUuid string `json:"container_uuid"` //容器id
}
func GetCommendResult(c *gin.Context) {
var sendCommandRequire GetCommandResultRequire
sendCommandRequire.IpAddr = c.Query("ip_addr")
sendCommandRequire.ContainerUuid = c.Query("container_uuid")
//升级接口
ws, err := upgrader.Upgrade(c.Writer, c.Request, nil)
if err != nil {
fmt.Print("升级websocket连接错误:", err)
return
}
defer ws.Close()
execHr := new(types.HijackedResponse)
dockerCli, err := ConnectDocker(sendCommandRequire.IpAddr)
if err != nil {
tools.SetErr(c, 500, err, err.Error())//此处是自己实现的gin返回
return
}
//启动携程,持续接收信息、传入命令、异常关闭docker链接
go func() {
for {
_, message, err := ws.ReadMessage()
if err != nil {
logger.Error("接口断开:", err)
execHr.Close()
ws.Close()
return
}
logger.Info(string(message))
//发送命令
err = SendCommend(string(message), execHr)
if err != nil {
logger.Error("命令发送错误")
}
}
}()
err = ExecContainer(dockerCli, execHr, ws, sendCommandRequire.ContainerUuid)
if err != nil {
tools.SetErr(c, 500, err, err.Error())
return
}
logger.Info("链接关闭")
}
//定义一个函数,链接容器,并持续输出。
func ExecContainer(dockerCli *client.Client, execHr *types.HijackedResponse, conn *websocket.Conn, containerId string) error {
ctx := context.Background()
// 在指定容器中执行/bin/bash命令
ir, err := dockerCli.ContainerExecCreate(ctx, containerId, types.ExecConfig{
AttachStdin: true,
AttachStdout: true,
AttachStderr: true,
Cmd: []string{"/bin/sh"},
Tty: true,
})
if err != nil {
return err
}
// 保持链接
*execHr, err = dockerCli.ContainerExecAttach(ctx, ir.ID, types.ExecStartCheck{Detach: false, Tty: true})
if err != nil {
return err
}
//关闭I/O
defer execHr.Close()
// 输入一个测试命令(非必要)
_, err = execHr.Conn.Write([]byte("ls\r"))
if err != nil {
return err
}
// 输出
scanner := bufio.NewScanner(execHr.Conn)
for scanner.Scan() {
fmt.Println(scanner.Text())
text := scanner.Text()
b, _ := json.Marshal(text)
err = conn.WriteMessage(websocket.TextMessage, b)
if err != nil {
log.Println("写入错误", err)
//return err
continue
}
}
logger.Info("容器输出结束")
return nil
}
//写一个函数,向容器中发送命令。
func SendCommend(cmdString string, execHr *types.HijackedResponse) (err error) {
_, err = execHr.Conn.Write([]byte(cmdString + "\r"))
if err != nil {
return
}
return nil
}
3. 测试用html文件
仅是一个测试文件,很丑,但是后端测试够了。
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>TestWebsocket</title>
<script type="text/javascript">
var Server_Com;
function LinkServer() {
// 声明连接
if ("WebSocket" in window) {
Server_Com = new WebSocket("ws://127.0.0.1:18000/v1/m6e/docker/container/exec?ip_addr=10.10.239.32&container_uuid=48bcf6811212");
console.log("新建连接到->127.0.0.1:18000");
}
// 建立连接后发送
Server_Com.onopen = function() {
Server_Com.send("Hello Server!"); // Web Socket 已连接上,使用 send() 方法发送数据
console.log("已连接上服务器");
}
// 接收服务器消息
Server_Com.onmessage = function(event) {
var recv_msg = event.data;
if (recv_msg == "Hello Client!") {
console.log("接收到服务器的问候: " + recv_msg); // 用于提示收到信息
} else {
document.getElementById("Time").textContent = document.getElementById("Time").textContent + "\r\n" + recv_msg; // 实时更新显示服务器发回的时间
console.log("接收到服务器数据: " + recv_msg);
}
}
}
function SendMessage(){
var inputMessage = document.getElementById("sendcmd").value;
Server_Com.send(inputMessage);
}
</script>
</head>
<body>
<p>接收到的信息:</p>
<p id="Time">crow-exec测试</p>
<button onclick="SendMessage()">发送消息</button>
<input id="sendcmd" name="sendcmd"></input>
<button onclick="LinkServer()">连接</button>
</body>
</html>
4. 需要完善
vi 编辑器无法实现,需要进一步完善。