343 lines
13 KiB
JavaScript
343 lines
13 KiB
JavaScript
/*
|
|
* This file is part of Cockpit.
|
|
*
|
|
* Copyright (C) 2015 Red Hat, Inc.
|
|
*
|
|
* Cockpit is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation; either version 2.1 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* Cockpit is distributed in the hope that it will be useful, but
|
|
* WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with Cockpit; If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
(function() {
|
|
var angular = require('angular');
|
|
require('angular-route');
|
|
require('angular-dialog.js');
|
|
require('./kube-client');
|
|
require('./listing');
|
|
|
|
require('kubernetes-container-terminal/dist/container-terminal.js');
|
|
|
|
angular.module('kubernetes.containers', [
|
|
'ngRoute',
|
|
'ui.cockpit',
|
|
'kubernetesUI',
|
|
'kubeClient',
|
|
'kubernetes.listing'
|
|
])
|
|
|
|
.config([
|
|
'$routeProvider',
|
|
function($routeProvider) {
|
|
$routeProvider
|
|
.when('/pods/:pod_namespace/:pod_name/:container_name', {
|
|
templateUrl: 'views/container-page.html',
|
|
controller: 'ContainerCtrl'
|
|
})
|
|
.when('/pods/:pod_namespace/:pod_name', {
|
|
redirectTo: '/pods'
|
|
})
|
|
.when('/pods/:pod_namespace?', {
|
|
templateUrl: 'views/containers-page.html',
|
|
controller: 'ContainersCtrl'
|
|
});
|
|
}
|
|
])
|
|
|
|
/*
|
|
* The controller for the containers view.
|
|
*/
|
|
.controller('ContainersCtrl', [
|
|
'$scope',
|
|
'KubeContainers',
|
|
'kubeLoader',
|
|
'kubeSelect',
|
|
'ListingState',
|
|
'$routeParams',
|
|
'$location',
|
|
function($scope, containers, loader, select, ListingState, $routeParams, $location) {
|
|
var selector = {};
|
|
var qs = $location.search();
|
|
for (var key in qs) {
|
|
if (key !== "namespace")
|
|
selector[key] = qs[key];
|
|
}
|
|
|
|
loader.listen(function() {
|
|
var pods = select().kind("Pod");
|
|
if ($routeParams.pod_namespace)
|
|
pods.namespace($routeParams.pod_namespace);
|
|
if (!angular.equals({}, selector))
|
|
pods.label(selector);
|
|
|
|
$scope.pods = pods;
|
|
}, $scope);
|
|
|
|
loader.watch("Pod", $scope);
|
|
|
|
$scope.listing = new ListingState($scope);
|
|
|
|
$scope.containers = containers;
|
|
|
|
$scope.$on("activate", function(ev, id) {
|
|
ev.preventDefault();
|
|
$location.path(id);
|
|
});
|
|
|
|
$scope.should_mask = function(name) {
|
|
return name.toLowerCase().indexOf("password") !== -1;
|
|
};
|
|
}
|
|
])
|
|
|
|
/*
|
|
* The controller for the containers view.
|
|
*/
|
|
.controller('ContainerCtrl', [
|
|
'$scope',
|
|
'KubeContainers',
|
|
'kubeLoader',
|
|
'kubeSelect',
|
|
'$routeParams',
|
|
'$route',
|
|
function($scope, containers, loader, select, $routeParams, $route) {
|
|
var target = $routeParams["container_name"] || "";
|
|
$scope.target = target;
|
|
|
|
loader.listen(function() {
|
|
$scope.pod = select().kind("Pod")
|
|
.namespace($routeParams.pod_namespace || "")
|
|
.name($routeParams.pod_name || "")
|
|
.one();
|
|
if ($scope.pod) {
|
|
angular.forEach(containers($scope.pod) || [], function (con) {
|
|
if (con.spec && con.spec.name === target)
|
|
$scope.container = con;
|
|
});
|
|
}
|
|
}, $scope);
|
|
|
|
loader.watch("Pod", $scope);
|
|
|
|
$scope.back = function() {
|
|
$route.updateParams({ "container_name" : undefined });
|
|
};
|
|
|
|
$scope.should_mask = function(name) {
|
|
return name.toLowerCase().indexOf("password") !== -1;
|
|
};
|
|
}
|
|
])
|
|
|
|
/**
|
|
* Build an array of container objects where each object contains the data from both
|
|
* the spec and status sections of the pod. Looks like this:
|
|
* { id: id, spec: pod.spec.containers[n], status: pod.status.containerStatuses[n] }
|
|
*
|
|
* The returned array will not change once created for a given pod item.
|
|
*/
|
|
.factory('KubeContainers', [
|
|
'KubeMapNamedArray',
|
|
function(mapNamedArray) {
|
|
return function (item) {
|
|
var specs, statuses, pod_id;
|
|
if (!item.containers) {
|
|
pod_id = "pods/" + item.metadata.namespace + "/" + item.metadata.name;
|
|
if (item.spec)
|
|
specs = mapNamedArray(item.spec.containers);
|
|
else
|
|
specs = { };
|
|
|
|
if (item.status)
|
|
statuses = mapNamedArray(item.status.containerStatuses);
|
|
else
|
|
statuses = { };
|
|
|
|
item.containers = Object.keys(specs).map(function(name) {
|
|
var key = pod_id + "/" + name;
|
|
return { spec: specs[name], status: statuses[name], key: key };
|
|
});
|
|
}
|
|
return item.containers;
|
|
};
|
|
}
|
|
])
|
|
|
|
.directive('containersListing',
|
|
function() {
|
|
return {
|
|
restrict: 'A',
|
|
templateUrl: 'views/containers-listing.html'
|
|
};
|
|
}
|
|
)
|
|
|
|
.directive('containerPageInline',
|
|
function() {
|
|
return {
|
|
restrict: 'A',
|
|
templateUrl: 'views/container-page-inline.html'
|
|
};
|
|
}
|
|
)
|
|
|
|
.directive('kubeContainerBody',
|
|
function() {
|
|
return {
|
|
restrict: 'E',
|
|
templateUrl: 'views/container-body.html'
|
|
};
|
|
}
|
|
)
|
|
|
|
.directive('kubePodBody',
|
|
function() {
|
|
return {
|
|
restrict: 'E',
|
|
templateUrl: 'views/pod-body.html'
|
|
};
|
|
}
|
|
)
|
|
|
|
/*
|
|
* Displays a container console.
|
|
*
|
|
* <kube-console namespace="ns" container="name"></kube-console>
|
|
*/
|
|
.directive('kubeConsole', [
|
|
'kubernetesContainerSocket',
|
|
function(socket) {
|
|
return {
|
|
restrict: 'E',
|
|
scope: {
|
|
pod: '&',
|
|
container: '&',
|
|
command: '@',
|
|
prevent: '='
|
|
},
|
|
link: function(scope, element, attrs) {
|
|
var limit = 64 * 1024;
|
|
|
|
var outer = angular.element("<div>");
|
|
outer.addClass("console-ct");
|
|
element.append(outer);
|
|
var pre = angular.element("<pre>");
|
|
outer.append(pre);
|
|
var wait = null;
|
|
var ws = null;
|
|
|
|
function connect() {
|
|
pre.empty();
|
|
|
|
var url = "";
|
|
var pod = scope.pod();
|
|
if (pod.metadata)
|
|
url += pod.metadata.selfLink;
|
|
else
|
|
url += pod;
|
|
url += "/log";
|
|
if (url.indexOf('?') === -1)
|
|
url += '?';
|
|
url += "follow=1";
|
|
|
|
var container = scope.container ? scope.container() : null;
|
|
if (container)
|
|
url += "&container=" + encodeURIComponent(container);
|
|
|
|
var writing = [];
|
|
var count = 0;
|
|
|
|
function drain() {
|
|
wait = null;
|
|
var at_bottom = pre[0].scrollHeight - pre[0].scrollTop <= pre[0].offsetHeight;
|
|
var text = writing.join("");
|
|
|
|
/*
|
|
* Stay under the limit. I wish we could use some other mechanism
|
|
* for limiting the log output, such as:
|
|
*
|
|
* https://github.com/kubernetes/kubernetes/issues/12447
|
|
*/
|
|
count += text.length;
|
|
var children, first, removed;
|
|
while (count > limit) {
|
|
children = pre.children();
|
|
if (children.length < 1)
|
|
break;
|
|
|
|
first = angular.element(children[0]);
|
|
removed = first.text().length;
|
|
first.remove();
|
|
count -= removed;
|
|
}
|
|
|
|
/* And add our text */
|
|
var span = angular.element("<span>").text(text);
|
|
writing.length = 0;
|
|
pre.append(span);
|
|
if (at_bottom)
|
|
pre[0].scrollTop = pre[0].scrollHeight;
|
|
}
|
|
|
|
ws = socket(url);
|
|
ws.onclose = function(ev) {
|
|
writing.push(ev.reason);
|
|
drain();
|
|
disconnect();
|
|
ws = null;
|
|
};
|
|
ws.onmessage = function(ev) {
|
|
writing.push(ev.data);
|
|
if (wait === null)
|
|
wait = window.setTimeout(drain, 50);
|
|
};
|
|
}
|
|
|
|
function disconnect() {
|
|
if (ws) {
|
|
ws.onopen = ws.onmessage = ws.onerror = ws.onclose = null;
|
|
if (ws.readyState < 2) // CLOSING
|
|
ws.close();
|
|
ws = null;
|
|
}
|
|
window.clearTimeout(wait);
|
|
wait = null;
|
|
}
|
|
|
|
scope.$watch("prevent", function(prevent) {
|
|
if (!prevent && !ws)
|
|
connect();
|
|
});
|
|
|
|
scope.$on("$destroy", disconnect);
|
|
}
|
|
};
|
|
}
|
|
])
|
|
|
|
/*
|
|
* Filter to display short docker ids
|
|
*
|
|
* {{ myid | kube-identifier }}
|
|
*
|
|
* Removes docker:// prefix and shortens.
|
|
*/
|
|
.filter('kubeIdentifier', function() {
|
|
var regex = /docker:\/\/([\w]{12})\w+/;
|
|
return function(item) {
|
|
var match = regex.exec(item);
|
|
if (match)
|
|
return match[1];
|
|
return item;
|
|
};
|
|
});
|
|
}());
|