feat: Adding terminal feature to Zun detail page.

Console feature was added to the detail page with
the attach api of the Zun service using Websocket.

Change-Id: Ie6884c21c23c39406f8be49e28941d3a05af1d7c
This commit is contained in:
resitdemir 2023-11-14 08:31:22 +00:00
parent 081ff7ee8b
commit b87846c27a
5 changed files with 99 additions and 0 deletions

View File

@ -0,0 +1,4 @@
---
features:
- |
Adding terminal feature to Zun detail page.

View File

@ -66,6 +66,10 @@ export class ZunClient extends Base {
key: 'execute',
method: 'post',
},
{
key: 'attach',
method: 'get',
},
{
key: 'network_attach',
method: 'post',

View File

@ -0,0 +1,78 @@
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
import React, { useEffect } from "react";
import globalContainersStore from 'src/stores/zun/containers';
export default function Console(props) {
useEffect(() => {
globalContainersStore.attach(props.detail.uuid).then((res) => {
const head = document.head;
const xtermCssLink = document.createElement("link");
xtermCssLink.rel = "stylesheet";
xtermCssLink.href = "https://cdn.jsdelivr.net/npm/xterm@4.19.0/css/xterm.css";
head.appendChild(xtermCssLink);
const xtermScript = document.createElement("script");
xtermScript.src = "https://cdnjs.cloudflare.com/ajax/libs/xterm/3.14.5/xterm.min.js";
xtermScript.onload = () => {
const term = new window.Terminal({
cursorBlink: true,
});
term.write(" >$ ");
term.open(document.getElementById('terminal'));
let socket = new WebSocket(res, ['binary', 'base64']);
term.on('data', function (data) {
socket.send(str2ab(data));
});
socket.onmessage = function (e) {
if (e.data instanceof Blob) {
let f = new FileReader();
f.onload = function () {
term.write(f.result);
};
f.readAsText(e.data);
} else {
term.write(e.data);
}
};
function str2ab(str) {
let buf = new ArrayBuffer(str.length); // 2 bytes for each char
let bufView = new Uint8Array(buf);
for (let i = 0, strLen = str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
};
head.appendChild(xtermScript);
return () => {
head.removeChild(xtermCssLink);
head.removeChild(xtermScript);
};
});
}, []);
return (
<div>
<div id="terminal">
</div>
</div >
);
}

View File

@ -19,6 +19,7 @@ import actionConfigs from '../actions';
import BaseDetail from './BaseDetail';
import ActionLogs from './ActionLogs';
import Logs from './Logs';
import Console from './Console';
export class ContainerDetail extends Base {
init() {
@ -84,6 +85,13 @@ export class ContainerDetail extends Base {
component: Logs,
});
}
if (this.detailData.interactive === true) {
items.push({
title: t('Console'),
key: 'console',
component: Console,
});
}
return items;
}
}

View File

@ -98,6 +98,11 @@ export class ContainersStore extends Base {
return this.client.execute(id, data);
}
@action
async attach(id) {
return this.client.attach(id);
}
@action
async attachNetwork(id, data) {
return this.client.network_attach(id, null, data);