mirror of
https://github.com/portainer/portainer.git
synced 2025-07-24 15:59:41 +02:00
feat(kubernetes/shell): kubectl web shell and kubeconfig functionality EE-448 (#5229)
* feat(kubernetes/shell): backport kubectl shell backend functionality EE-849 (#5168) * backported core backend kubectl shell functionality * - backported kubectl shell unit tests - backported k8s cli interface update - backported k8s client library fake patch * refactored backend to match EE * fixed test error typo * GetServiceAccountName -> GetServiceAccount - making the function reusable in multiple contexts * feat(kubernetes/shell): backport kubeconfig generation backend functionality EE-1004 (#5213) * backported core backend kubectl shell functionality * refactored backend to match EE * - backported kubernetes backend handler implementation - backported kubernetes config endpoint - backported kubeconfig file generation - backported kubeconfig and yaml unit tests - backported updates to kubeclient interfaces * feat(app): kubectl shell ui backport EE-927 (#5221) * Kubectl UI backport to CE * fix authentication redirect issue * comment out redirect function * fix shell full width & change name of shell * disable button when terminal connected * fixed whitespace changes for css * fixed whitespace changes for html * linting fixes Co-authored-by: zees-dev <dev.786zshan@gmail.com> * feat(kubernetes/shell): backport of kubeconfig export functionality EE-926 (#5228) * EE backport of kubeconfig UI functionality * using angularjs constant instead of hardcoded URL * updated portainer kubectl shell image * fix kubectl button position issue in ce * fix pod keep running when switching page * feat(app): Kubectl shell ui EE-833 EE-1099 (#5271) * fix kubectl shell css * fix mini css issue * fix tech issue for ui changes from review * delete unuse file * - refactored variable names - restored content-wrapper scroll - created object to store wrapper css Co-authored-by: zees-dev <dev.786zshan@gmail.com> * addressing PR issues * fix required changes from tech reviews (#5319) * fix required changes from tech reviews * remove unuse css variable * component refactor accoridng to PR and style guidelines Co-authored-by: zees-dev <dev.786zshan@gmail.com> * removed redundant dockerhub api endpoint variable * - autoHeight -> terminal-window - removed redundant try-catch - saving config.yaml file as config * fix(kube/shell): show error on failure * fixed default https bug * resolved merge conflicts Co-authored-by: Richard Wei <54336863+WaysonWei@users.noreply.github.com> Co-authored-by: richard <richard@richards-iMac-Pro.local> Co-authored-by: Chaim Lev-Ari <chiptus@gmail.com>
This commit is contained in:
parent
ec71720ceb
commit
665bf2c887
28 changed files with 1281 additions and 21 deletions
|
@ -0,0 +1,125 @@
|
|||
import { Terminal } from 'xterm';
|
||||
import * as fit from 'xterm/lib/addons/fit/fit';
|
||||
|
||||
export default class KubectlShellController {
|
||||
/* @ngInject */
|
||||
constructor(TerminalWindow, $window, $async, EndpointProvider, LocalStorage, KubernetesConfigService, Notifications) {
|
||||
this.$async = $async;
|
||||
this.$window = $window;
|
||||
this.TerminalWindow = TerminalWindow;
|
||||
this.EndpointProvider = EndpointProvider;
|
||||
this.LocalStorage = LocalStorage;
|
||||
this.KubernetesConfigService = KubernetesConfigService;
|
||||
this.Notifications = Notifications;
|
||||
}
|
||||
|
||||
disconnect() {
|
||||
this.state.checked = false;
|
||||
this.state.icon = 'fas fa-window-minimize';
|
||||
this.state.shell.socket.close();
|
||||
this.state.shell.term.dispose();
|
||||
this.state.shell.connected = false;
|
||||
this.TerminalWindow.terminalclose();
|
||||
}
|
||||
|
||||
screenClear() {
|
||||
this.state.shell.term.clear();
|
||||
}
|
||||
|
||||
miniRestore() {
|
||||
if (this.state.css === 'mini') {
|
||||
this.state.css = 'normal';
|
||||
this.state.icon = 'fas fa-window-minimize';
|
||||
this.TerminalWindow.terminalopen();
|
||||
} else {
|
||||
this.state.css = 'mini';
|
||||
this.state.icon = 'fas fa-window-restore';
|
||||
this.TerminalWindow.terminalclose();
|
||||
}
|
||||
}
|
||||
|
||||
configureSocketAndTerminal(socket, term) {
|
||||
socket.onopen = function () {
|
||||
const terminal_container = document.getElementById('terminal-container');
|
||||
term.open(terminal_container);
|
||||
term.setOption('cursorBlink', true);
|
||||
term.focus();
|
||||
term.fit();
|
||||
term.writeln('#Run kubectl commands inside here');
|
||||
term.writeln('#e.g. kubectl get all');
|
||||
term.writeln('');
|
||||
};
|
||||
|
||||
term.on('data', function (data) {
|
||||
socket.send(data);
|
||||
});
|
||||
|
||||
this.$window.onresize = function () {
|
||||
term.fit();
|
||||
};
|
||||
|
||||
socket.onmessage = function (msg) {
|
||||
term.write(msg.data);
|
||||
};
|
||||
|
||||
socket.onerror = function (err) {
|
||||
this.disconnect();
|
||||
this.Notifications.error('Failure', err, 'Websocket connection error');
|
||||
}.bind(this);
|
||||
|
||||
this.state.shell.socket.onclose = this.disconnect.bind(this);
|
||||
|
||||
this.state.shell.connected = true;
|
||||
}
|
||||
|
||||
connectConsole() {
|
||||
this.TerminalWindow.terminalopen();
|
||||
this.state.checked = true;
|
||||
this.state.css = 'normal';
|
||||
|
||||
const params = {
|
||||
token: this.LocalStorage.getJWT(),
|
||||
endpointId: this.EndpointProvider.endpointID(),
|
||||
};
|
||||
|
||||
const wsProtocol = this.state.isHTTPS ? 'wss://' : 'ws://';
|
||||
const path = '/api/websocket/kubernetes-shell';
|
||||
const queryParams = Object.entries(params)
|
||||
.map(([k, v]) => `${k}=${v}`)
|
||||
.join('&');
|
||||
const url = `${wsProtocol}${window.location.host}${path}?${queryParams}`;
|
||||
|
||||
Terminal.applyAddon(fit);
|
||||
this.state.shell.socket = new WebSocket(url);
|
||||
this.state.shell.term = new Terminal();
|
||||
|
||||
this.configureSocketAndTerminal(this.state.shell.socket, this.state.shell.term);
|
||||
}
|
||||
|
||||
async downloadKubeconfig() {
|
||||
await this.KubernetesConfigService.downloadConfig();
|
||||
}
|
||||
|
||||
$onInit() {
|
||||
return this.$async(async () => {
|
||||
this.state = {
|
||||
css: 'normal',
|
||||
checked: false,
|
||||
icon: 'fa-window-minimize',
|
||||
isHTTPS: this.$window.location.protocol === 'https:',
|
||||
shell: {
|
||||
connected: false,
|
||||
socket: null,
|
||||
term: null,
|
||||
},
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
$onDestroy() {
|
||||
if (this.state.shell.connected) {
|
||||
this.disconnect();
|
||||
this.$window.onresize = null;
|
||||
}
|
||||
}
|
||||
}
|
75
app/kubernetes/components/kubectl-shell/kubectl-shell.css
Normal file
75
app/kubernetes/components/kubectl-shell/kubectl-shell.css
Normal file
|
@ -0,0 +1,75 @@
|
|||
.shell-container {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
color: #424242;
|
||||
background: rgb(245, 245, 245);
|
||||
border-top: 1px solid rgb(190, 190, 190);
|
||||
}
|
||||
|
||||
.shell-item {
|
||||
width: 150px;
|
||||
font-weight: bold;
|
||||
text-align: center;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.shell-item-center {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
ul.sidebar li .shell-item-center a {
|
||||
border-left: 1px solid rgb(143, 143, 143);
|
||||
padding: 0px 5px;
|
||||
color: #424242;
|
||||
}
|
||||
|
||||
ul.sidebar li .shell-item-center a:hover {
|
||||
color: rgb(51, 122, 183);
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.shell-item-center a {
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.shell-item-right {
|
||||
height: 16px;
|
||||
width: 180px;
|
||||
text-align: right;
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
.shell-item-right i {
|
||||
padding: 0px 5px;
|
||||
cursor: pointer;
|
||||
color: rgb(89, 89, 89);
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.shell-item-right i:hover {
|
||||
color: rgb(51, 122, 183);
|
||||
}
|
||||
|
||||
.shell-item + .shell-item {
|
||||
margin-left: 20%;
|
||||
}
|
||||
|
||||
.normal-kubectl-shell {
|
||||
position: fixed;
|
||||
background: #000;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 480px;
|
||||
z-index: 1000;
|
||||
}
|
||||
|
||||
.mini-kubectl-shell {
|
||||
position: fixed;
|
||||
background: #000;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
width: 100vw;
|
||||
height: 35px;
|
||||
z-index: 1000;
|
||||
}
|
27
app/kubernetes/components/kubectl-shell/kubectl-shell.html
Normal file
27
app/kubernetes/components/kubectl-shell/kubectl-shell.html
Normal file
|
@ -0,0 +1,27 @@
|
|||
<button type="button" class="btn btn-xs btn-primary" ng-click="$ctrl.connectConsole()" ng-disabled="$ctrl.state.shell.connected">
|
||||
<i class="fa fa-terminal" style="margin-right: 2px;"></i>
|
||||
kubectl shell
|
||||
</button>
|
||||
|
||||
<div ng-if="$ctrl.state.checked" class="{{ $ctrl.state.css }}-kubectl-shell">
|
||||
<div class="shell-container">
|
||||
<div class="shell-item"><i class="fas fa-terminal" style="margin-right: 5px;"></i>kubectl shell</div>
|
||||
<div ng-if="$ctrl.state.isHTTPS" class="shell-item-center">
|
||||
<a href="" ng-click="$ctrl.downloadKubeconfig()"><i class="fas fa-file-download" style="margin-right: 5px;"></i>Download Kubeconfig</a>
|
||||
</div>
|
||||
<div class="shell-item-right">
|
||||
<i class="fas fa-redo-alt" ng-click="$ctrl.screenClear();"></i>
|
||||
<i class="fas {{ $ctrl.state.icon }}" ng-click="$ctrl.miniRestore();"></i>
|
||||
<i class="fas fa-times" ng-click="$ctrl.disconnect()"></i>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div style="padding: 0px;">
|
||||
<div>
|
||||
<div id="terminal-container" class="terminal-container">
|
||||
<div style="position: fixed; color: #fff;">Loading Terminal...</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
8
app/kubernetes/components/kubectl-shell/kubectl-shell.js
Normal file
8
app/kubernetes/components/kubectl-shell/kubectl-shell.js
Normal file
|
@ -0,0 +1,8 @@
|
|||
import angular from 'angular';
|
||||
import controller from './kubectl-shell.controller';
|
||||
import './kubectl-shell.css';
|
||||
|
||||
angular.module('portainer.kubernetes').component('kubectlShell', {
|
||||
templateUrl: './kubectl-shell.html',
|
||||
controller,
|
||||
});
|
Loading…
Add table
Add a link
Reference in a new issue