1
0
Fork 0
mirror of https://github.com/portainer/portainer.git synced 2025-08-02 20:35:25 +02:00

feat(global): add container exec support (#97)

This commit is contained in:
Anthony Lapenna 2016-08-03 15:12:53 +12:00 committed by GitHub
parent b0ebbdf68c
commit 1aaa5acbef
16 changed files with 438 additions and 75 deletions

View file

@ -9,13 +9,14 @@ angular.module('uifordocker', [
'uifordocker.filters',
'dashboard',
'container',
'containerConsole',
'containerLogs',
'containers',
'createContainer',
'docker',
'events',
'images',
'image',
'containerLogs',
'stats',
'swarm',
'network',
@ -57,6 +58,11 @@ angular.module('uifordocker', [
templateUrl: 'app/components/containerLogs/containerlogs.html',
controller: 'ContainerLogsController'
})
.state('console', {
url: "^/containers/:id/console",
templateUrl: 'app/components/containerConsole/containerConsole.html',
controller: 'ContainerConsoleController'
})
.state('actions', {
abstract: true,
url: "/actions",

View file

@ -64,6 +64,7 @@
<div class="btn-group" role="group" aria-label="...">
<a class="btn btn-default" type="button" ui-sref="stats({id: container.Id})">Stats</a>
<a class="btn btn-default" type="button" ui-sref="logs({id: container.Id})">Logs</a>
<a class="btn btn-default" type="button" ui-sref="console({id: container.Id})">Console</a>
</div>
</div>
<div class="comment">

View file

@ -0,0 +1,43 @@
<rd-header>
<rd-header-title title="Container console">
<i id="loadingViewSpinner" class="fa fa-cog fa-spin"></i>
</rd-header-title>
<rd-header-content>
Containers > <a ui-sref="container({id: container.Id})">{{ container.Name|trimcontainername }}</a> > Console
</rd-header-content>
</rd-header>
<div class="row">
<div class="col-lg-12 col-md-12 col-xs-12">
<rd-widget>
<rd-widget-header icon="fa-terminal" title="Console">
<div class="pull-right">
<i id="loadConsoleSpinner" class="fa fa-cog fa-2x fa-spin" style="margin-top: 5px; display: none;"></i>
</div>
</rd-widget-header>
<rd-widget-body>
<form class="form-horizontal">
<!-- command-list -->
<div class="form-group">
<div class="col-sm-3">
<select class="selectpicker form-control" ng-model="state.command">
<option value="bash">/bin/bash</option>
<option value="sh">/bin/sh</option>
</select>
</div>
<div class="col-sm-9 pull-left">
<button type="button" class="btn btn-primary" ng-click="connect()" ng-disabled="connected">Connect</button>
<button type="button" class="btn btn-default" ng-click="disconnect()" ng-disabled="!connected">Disconnect</button>
</div>
</div>
</form>
</rd-widget-body>
</rd-widget>
</div>
</div>
<div class="row">
<div class="col-lg-12 col-md-12 col-xs-12">
<div id="terminal-container" class="terminal-container"></div>
</div>
</div>

View file

@ -0,0 +1,104 @@
angular.module('containerConsole', [])
.controller('ContainerConsoleController', ['$scope', '$stateParams', 'Settings', 'Container', 'Exec', '$timeout', 'Messages', 'errorMsgFilter',
function ($scope, $stateParams, Settings, Container, Exec, $timeout, Messages, errorMsgFilter) {
$scope.state = {};
$scope.state.command = "bash";
$scope.connected = false;
var socket, term;
// Ensure the socket is closed before leaving the view
$scope.$on('$stateChangeStart', function (event, next, current) {
if (socket !== null) {
socket.close();
}
});
Container.get({id: $stateParams.id}, function(d) {
$scope.container = d;
$('#loadingViewSpinner').hide();
});
$scope.connect = function() {
$('#loadConsoleSpinner').show();
var termWidth = Math.round($('#terminal-container').width() / 8.2);
var termHeight = 30;
var execConfig = {
id: $stateParams.id,
AttachStdin: true,
AttachStdout: true,
AttachStderr: true,
Tty: true,
Cmd: $scope.state.command.replace(" ", ",").split(",")
};
Container.exec(execConfig, function(d) {
if (d.Id) {
var execId = d.Id;
resizeTTY(execId, termHeight, termWidth);
var url = window.location.href.split('#')[0].replace('http://', 'ws://') + 'ws/exec?id=' + execId;
initTerm(url, termHeight, termWidth);
} else {
$('#loadConsoleSpinner').hide();
Messages.error('Error', errorMsgFilter(d));
}
}, function (e) {
$('#loadConsoleSpinner').hide();
Messages.error("Failure", e.data);
});
};
$scope.disconnect = function() {
$scope.connected = false;
if (socket !== null) {
socket.close();
}
if (term !== null) {
term.destroy();
}
};
function resizeTTY(execId, height, width) {
$timeout(function() {
Exec.resize({id: execId, height: height, width: width}, function (d) {
var error = errorMsgFilter(d);
if (error) {
Messages.error('Error', 'Unable to resize TTY');
}
});
}, 2000);
}
function initTerm(url, height, width) {
socket = new WebSocket(url);
$scope.connected = true;
socket.onopen = function(evt) {
$('#loadConsoleSpinner').hide();
term = new Terminal({
cols: width,
rows: height,
cursorBlink: true
});
term.on('data', function (data) {
socket.send(data);
});
term.open(document.getElementById('terminal-container'));
socket.onmessage = function (e) {
term.write(e.data);
};
socket.onerror = function (error) {
$scope.connected = false;
};
socket.onclose = function(evt) {
$scope.connected = false;
// term.write("Session terminated");
// term.destroy();
};
};
}
}]);

View file

@ -18,9 +18,17 @@ angular.module('uifordocker.services', ['ngResource', 'ngSanitize'])
create: {method: 'POST', params: {action: 'create'}},
remove: {method: 'DELETE', params: {id: '@id', v: 0}},
rename: {method: 'POST', params: {id: '@id', action: 'rename'}, isArray: false},
stats: {method: 'GET', params: {id: '@id', stream: false, action: 'stats'}, timeout: 5000}
stats: {method: 'GET', params: {id: '@id', stream: false, action: 'stats'}, timeout: 5000},
exec: {method: 'POST', params: {id: '@id', action: 'exec'}}
});
}])
.factory('Exec', ['$resource', 'Settings', function ExecFactory($resource, Settings) {
'use strict';
// https://docs.docker.com/engine/reference/api/docker_remote_api_<%= remoteApiVersion %>/#/exec-resize
return $resource(Settings.url + '/exec/:id/:action', {}, {
resize: {method: 'POST', params: {id: '@id', action: 'resize', h: '@height', w: '@width'}}
});
}])
.factory('ContainerCommit', ['$resource', '$http', 'Settings', function ContainerCommitFactory($resource, $http, Settings) {
'use strict';
// http://docs.docker.com/reference/api/docker_remote_api_<%= remoteApiVersion %>/#create-a-new-image-from-a-container-s-changes