You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1811 lines
57 KiB
1811 lines
57 KiB
/// <reference path="../../includes.ts"/>
|
|
/// <reference path="kubernetesInterfaces.ts"/>
|
|
module Kubernetes {
|
|
|
|
export var context = '/kubernetes';
|
|
export var hash = '#' + context;
|
|
export var defaultRoute = hash + '/apps';
|
|
export var pluginName = 'Kubernetes';
|
|
export var pluginPath = 'plugins/kubernetes/';
|
|
export var templatePath = pluginPath + 'html/';
|
|
export var log:Logging.Logger = Logger.get(pluginName);
|
|
|
|
export var keepPollingModel = true;
|
|
|
|
export var defaultIconUrl = Core.url("/img/kubernetes.svg");
|
|
export var hostIconUrl = Core.url("/img/host.svg");
|
|
|
|
// this gets set as a pre-bootstrap task
|
|
export var osConfig:KubernetesConfig = undefined;
|
|
export var masterUrl = "";
|
|
|
|
export var defaultApiVersion = "v1";
|
|
export var defaultOSApiVersion = "v1";
|
|
export var labelFilterTextSeparator = ",";
|
|
|
|
export var defaultNamespace = "default";
|
|
|
|
export var appSuffix = ".app";
|
|
|
|
// kubernetes service names
|
|
export var kibanaServiceName = "kibana";
|
|
export var fabric8ForgeServiceName = "fabric8-forge";
|
|
export var gogsServiceName = "gogs";
|
|
export var jenkinsServiceName = "jenkins";
|
|
export var apimanServiceName = 'apiman';
|
|
export var isOpenShift = true;
|
|
|
|
export var sshSecretDataKeys = ["ssh-key", "ssh-key.pub"];
|
|
export var httpsSecretDataKeys = ["username", "password"];
|
|
|
|
|
|
export function kubernetesNamespacePath() {
|
|
var ns = currentKubernetesNamespace();
|
|
if (ns) {
|
|
return "/namespaces/" + ns;
|
|
} else {
|
|
return "";
|
|
}
|
|
}
|
|
|
|
export function apiPrefix() {
|
|
var prefix = Core.pathGet(osConfig, ['api', 'k8s', 'prefix']);
|
|
if (!prefix) {
|
|
prefix = 'api';
|
|
}
|
|
return Core.trimLeading(prefix, '/');
|
|
}
|
|
|
|
export function osApiPrefix() {
|
|
var prefix = Core.pathGet(osConfig, ['api', 'openshift', 'prefix']);
|
|
if (!prefix) {
|
|
prefix = 'oapi';
|
|
}
|
|
var answer = Core.trimLeading(prefix, '/');
|
|
if (!isOpenShift) {
|
|
return UrlHelpers.join(apiPrefix(), defaultOSApiVersion, "proxy", kubernetesNamespacePath(), "services/templates", answer);
|
|
}
|
|
return answer;
|
|
}
|
|
|
|
export function masterApiUrl() {
|
|
return masterUrl || "";
|
|
}
|
|
|
|
/** WARNING - this excludes the host name - you probably want to use: kubernetesApiUrl() instead!! */
|
|
export function kubernetesApiPrefix() {
|
|
return UrlHelpers.join(apiPrefix(), defaultApiVersion);
|
|
}
|
|
|
|
export function openshiftApiPrefix() {
|
|
return UrlHelpers.join(osApiPrefix(), defaultOSApiVersion);
|
|
}
|
|
|
|
export function prefixForType(type:string) {
|
|
if (type === WatchTypes.NAMESPACES) {
|
|
return kubernetesApiPrefix();
|
|
}
|
|
if (_.any(NamespacedTypes.k8sTypes, (t) => t === type)) {
|
|
return kubernetesApiPrefix();
|
|
}
|
|
if (_.any(NamespacedTypes.osTypes, (t) => t === type)) {
|
|
return openshiftApiPrefix();
|
|
}
|
|
// lets assume its an OpenShift extension type
|
|
return openshiftApiPrefix();
|
|
}
|
|
|
|
export function kubernetesApiUrl() {
|
|
return UrlHelpers.join(masterApiUrl(), kubernetesApiPrefix());
|
|
}
|
|
|
|
export function openshiftApiUrl() {
|
|
return UrlHelpers.join(masterApiUrl(), openshiftApiPrefix());
|
|
}
|
|
|
|
export function resourcesUriForKind(type, ns = null) {
|
|
if (!ns) {
|
|
ns = currentKubernetesNamespace();
|
|
}
|
|
return UrlHelpers.join(masterApiUrl(), prefixForType(type), namespacePathForKind(type, ns));
|
|
}
|
|
|
|
export function uriTemplateForKubernetesKind(type) {
|
|
var urlTemplate = '';
|
|
switch (type) {
|
|
case WatchTypes.NAMESPACES:
|
|
case "Namespaces":
|
|
urlTemplate = UrlHelpers.join('namespaces');
|
|
break;
|
|
case WatchTypes.OAUTH_CLIENTS:
|
|
case "OAuthClients":
|
|
case "OAuthClient":
|
|
return UrlHelpers.join('oauthclients');
|
|
case WatchTypes.PROJECTS:
|
|
case "Projects":
|
|
urlTemplate = UrlHelpers.join('projects');
|
|
break;
|
|
default:
|
|
urlTemplate = UrlHelpers.join('namespaces/:namespace', type, ':id');
|
|
}
|
|
return urlTemplate;
|
|
}
|
|
|
|
export function namespacePathForKind(type, ns) {
|
|
var urlTemplate = '';
|
|
switch (type) {
|
|
case WatchTypes.NAMESPACES:
|
|
case "Namespaces":
|
|
case "Namespace":
|
|
return UrlHelpers.join('namespaces');
|
|
case WatchTypes.NODES:
|
|
case "Nodes":
|
|
case "node":
|
|
return UrlHelpers.join('nodes');
|
|
case WatchTypes.PROJECTS:
|
|
case "Projects":
|
|
case "Project":
|
|
return UrlHelpers.join('projects');
|
|
case WatchTypes.OAUTH_CLIENTS:
|
|
case "OAuthClients":
|
|
case "OAuthClient":
|
|
return UrlHelpers.join('oauthclients');
|
|
case WatchTypes.PERSISTENT_VOLUMES:
|
|
case "PersistentVolumes":
|
|
case "PersistentVolume":
|
|
return UrlHelpers.join('persistentvolumes');
|
|
default:
|
|
return UrlHelpers.join('namespaces', ns, type);
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns thevalue from the injector if its available or null
|
|
*/
|
|
export function inject<T>(name):T {
|
|
var injector = HawtioCore.injector;
|
|
return injector ? injector.get<T>(name) : null;
|
|
}
|
|
|
|
export function createResource(thing:string, urlTemplate:string, $resource: ng.resource.IResourceService, KubernetesModel) {
|
|
var prefix = prefixForType(thing);
|
|
if (!prefix) {
|
|
log.debug("Invalid type given: ", thing);
|
|
return null;
|
|
}
|
|
|
|
var params = <any> {
|
|
namespace: currentKubernetesNamespace
|
|
}
|
|
switch (thing) {
|
|
case WatchTypes.NAMESPACES:
|
|
case WatchTypes.OAUTH_CLIENTS:
|
|
case WatchTypes.NODES:
|
|
case WatchTypes.PROJECTS:
|
|
case WatchTypes.OAUTH_CLIENTS:
|
|
case WatchTypes.PERSISTENT_VOLUMES:
|
|
params = {};
|
|
}
|
|
|
|
var url = UrlHelpers.join(masterApiUrl(), prefix, urlTemplate);
|
|
log.debug("Url for ", thing, ": ", url);
|
|
var resource = $resource(url, null, {
|
|
query: { method: 'GET', isArray: false, params: params},
|
|
create: { method: 'POST', params: params},
|
|
save: { method: 'PUT', params: params},
|
|
delete: { method: 'DELETE', params: _.extend({
|
|
id: '@id'
|
|
}, params)}
|
|
});
|
|
return resource;
|
|
}
|
|
|
|
export function imageRepositoriesRestURL() {
|
|
return UrlHelpers.join(openshiftApiUrl(), kubernetesNamespacePath(), "/imagestreams");
|
|
}
|
|
|
|
export function deploymentConfigsRestURL() {
|
|
return UrlHelpers.join(openshiftApiUrl(), kubernetesNamespacePath(), "/deploymentconfigs");
|
|
}
|
|
|
|
export function buildsRestURL() {
|
|
return UrlHelpers.join(openshiftApiUrl(), kubernetesNamespacePath(), "/builds");
|
|
}
|
|
|
|
export function buildConfigHooksRestURL() {
|
|
return UrlHelpers.join(openshiftApiUrl(), kubernetesNamespacePath(), "/buildconfighooks");
|
|
}
|
|
|
|
export function buildConfigsRestURL() {
|
|
return UrlHelpers.join(openshiftApiUrl(), kubernetesNamespacePath(), "/buildconfigs");
|
|
}
|
|
|
|
export function routesRestURL() {
|
|
return UrlHelpers.join(openshiftApiUrl(), kubernetesNamespacePath(), "/routes");
|
|
}
|
|
|
|
export function templatesRestURL() {
|
|
return UrlHelpers.join(openshiftApiUrl(), kubernetesNamespacePath(), "/templates");
|
|
}
|
|
|
|
export function getNamespace(entity) {
|
|
var answer = Core.pathGet(entity, ["metadata", "namespace"]);
|
|
return answer ? answer : currentKubernetesNamespace();
|
|
}
|
|
|
|
export function getLabels(entity) {
|
|
var answer = Core.pathGet(entity, ["metadata", "labels"]);
|
|
return answer ? answer : {};
|
|
}
|
|
|
|
export function getName(entity) {
|
|
if (angular.isString(entity)) {
|
|
return entity;
|
|
}
|
|
return Core.pathGet(entity, ["metadata", "name"]) || Core.pathGet(entity, "name") || Core.pathGet(entity, "id");
|
|
}
|
|
|
|
export function getKind(entity) {
|
|
return Core.pathGet(entity, ["metadata", "kind"]) || Core.pathGet(entity, "kind");
|
|
}
|
|
|
|
export function getSelector(entity) {
|
|
return Core.pathGet(entity, ["spec", "selector"]);
|
|
}
|
|
|
|
export function getHost(pod) {
|
|
return Core.pathGet(pod, ["spec", "host"]) || Core.pathGet(pod, ["spec", "nodeName"]) || Core.pathGet(pod, ["status", "hostIP"]);
|
|
}
|
|
|
|
export function getStatus(pod) {
|
|
return Core.pathGet(pod, ["status", "phase"]);
|
|
}
|
|
|
|
export function getPorts(service) {
|
|
return Core.pathGet(service, ["spec", "ports"]);
|
|
}
|
|
|
|
export function getCreationTimestamp(entity) {
|
|
return Core.pathGet(entity, ["metadata", "creationTimestamp"]);
|
|
};
|
|
|
|
//var fabricDomain = Fabric.jmxDomain;
|
|
var fabricDomain = "io.fabric8";
|
|
export var mbean = fabricDomain + ":type=Kubernetes";
|
|
export var managerMBean = fabricDomain + ":type=KubernetesManager";
|
|
export var appViewMBean = fabricDomain + ":type=AppView";
|
|
|
|
export function isKubernetes(workspace?) {
|
|
// return workspace.treeContainsDomainAndProperties(fabricDomain, {type: "Kubernetes"});
|
|
return true;
|
|
}
|
|
|
|
export function isKubernetesTemplateManager(workspace?) {
|
|
// return workspace.treeContainsDomainAndProperties(fabricDomain, {type: "KubernetesTemplateManager"});
|
|
return true;
|
|
}
|
|
|
|
export function isAppView(workspace?) {
|
|
// return workspace.treeContainsDomainAndProperties(fabricDomain, {type: "AppView"});
|
|
return true;
|
|
}
|
|
|
|
export function getStrippedPathName():string {
|
|
var pathName = Core.trimLeading((this.$location.path() || '/'), "#");
|
|
pathName = pathName.replace(/^\//, '');
|
|
return pathName;
|
|
}
|
|
|
|
export function linkContains(...words:String[]):boolean {
|
|
var pathName = this.getStrippedPathName();
|
|
return _.every(words, (word:string) => pathName.indexOf(word) !== 0);
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns true if the given link is active. The link can omit the leading # or / if necessary.
|
|
* The query parameters of the URL are ignored in the comparison.
|
|
* @method isLinkActive
|
|
* @param {String} href
|
|
* @return {Boolean} true if the given link is active
|
|
*/
|
|
export function isLinkActive(href:string):boolean {
|
|
// lets trim the leading slash
|
|
var pathName = getStrippedPathName();
|
|
|
|
var link = Core.trimLeading(href, "#");
|
|
link = link.replace(/^\//, '');
|
|
// strip any query arguments
|
|
var idx = link.indexOf('?');
|
|
if (idx >= 0) {
|
|
link = link.substring(0, idx);
|
|
}
|
|
if (!pathName.length) {
|
|
return link === pathName;
|
|
} else {
|
|
return _.startsWith(pathName, link);
|
|
}
|
|
}
|
|
|
|
export function setJson($scope, id, collection) {
|
|
$scope.id = id;
|
|
if (!$scope.fetched) {
|
|
return;
|
|
}
|
|
if (!id) {
|
|
$scope.json = '';
|
|
return;
|
|
}
|
|
if (!collection) {
|
|
return;
|
|
}
|
|
var item = collection.find((item) => { return getName(item) === id; });
|
|
if (item) {
|
|
$scope.json = angular.toJson(item, true);
|
|
$scope.item = item;
|
|
} else {
|
|
$scope.id = undefined;
|
|
$scope.json = '';
|
|
$scope.item = undefined;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the labels text string using the <code>key1=value1,key2=value2,....</code> format
|
|
*/
|
|
export function labelsToString(labels, seperatorText = labelFilterTextSeparator) {
|
|
var answer = "";
|
|
angular.forEach(labels, (value, key) => {
|
|
var separator = answer ? seperatorText : "";
|
|
|
|
answer += separator + key + "=" + value;
|
|
});
|
|
return answer;
|
|
}
|
|
|
|
|
|
export function initShared($scope, $location, $http, $timeout, $routeParams, KubernetesModel, KubernetesState, KubernetesApiURL) {
|
|
$scope.baseUri = Core.trimTrailing(Core.url("/") || "", "/") || "";
|
|
|
|
var injector = HawtioCore.injector;
|
|
|
|
function hasService(name) {
|
|
if (injector) {
|
|
var ServiceRegistry = injector.get<any>("ServiceRegistry");
|
|
if (ServiceRegistry) {
|
|
return ServiceRegistry.hasService(name);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
$scope.hasServiceKibana = () => hasService(kibanaServiceName);
|
|
$scope.hasServiceGogs = () => hasService(gogsServiceName);
|
|
$scope.hasServiceForge = () => hasService(fabric8ForgeServiceName);
|
|
$scope.hasServiceApiman = () => hasService(apimanServiceName);
|
|
|
|
$scope.viewTemplates = () => {
|
|
var returnTo = $location.url();
|
|
$location.path('/kubernetes/templates').search({'returnTo': returnTo});
|
|
};
|
|
|
|
$scope.namespace = $routeParams.namespace || $scope.namespace || KubernetesState.selectedNamespace || defaultNamespace;
|
|
if ($scope.namespace != KubernetesState.selectedNamespace) {
|
|
KubernetesState.selectedNamespace = $scope.namespace;
|
|
|
|
// lets show page is going to reload
|
|
if ($scope.model) {
|
|
$scope.model.fetched = false;
|
|
}
|
|
}
|
|
Kubernetes.setCurrentKubernetesNamespace($scope.namespace);
|
|
|
|
$scope.forgeEnabled = isForgeEnabled();
|
|
|
|
$scope.projectId = $routeParams["project"] || $scope.projectId || $scope.id;
|
|
var showProjectNavBars = false;
|
|
if ($scope.projectId && showProjectNavBars) {
|
|
$scope.breadcrumbConfig = Developer.createProjectBreadcrumbs($scope.projectId);
|
|
$scope.subTabConfig = Developer.createProjectSubNavBars($scope.projectId, null, $scope);
|
|
} else {
|
|
$scope.breadcrumbConfig = Developer.createEnvironmentBreadcrumbs($scope, $location, $routeParams);
|
|
$scope.subTabConfig = Developer.createEnvironmentSubNavBars($scope, $location, $routeParams);
|
|
}
|
|
|
|
if ($scope.projectId) {
|
|
$scope.$projectLink = Developer.projectLink($scope.projectId);
|
|
}
|
|
|
|
$scope.link = (href) => {
|
|
if (!href) {
|
|
return href;
|
|
}
|
|
if ($scope.$projectLink) {
|
|
return Developer.namespaceLink($scope, $routeParams, href.replace(/^\/kubernetes/, ''));
|
|
} else {
|
|
return href;
|
|
}
|
|
}
|
|
|
|
$scope.codeMirrorOptions = {
|
|
lineWrapping : true,
|
|
lineNumbers: true,
|
|
readOnly: 'nocursor',
|
|
mode: {name: "javascript", json: true}
|
|
};
|
|
|
|
$scope.resizeDialog = {
|
|
controller: null,
|
|
newReplicas: 0,
|
|
dialog: new UI.Dialog(),
|
|
onOk: () => {
|
|
var resizeDialog = $scope.resizeDialog;
|
|
resizeDialog.dialog.close();
|
|
resizeController($http, KubernetesApiURL, resizeDialog.controller, resizeDialog.newReplicas, () => { log.debug("updated number of replicas"); })
|
|
},
|
|
open: (controller) => {
|
|
var resizeDialog = $scope.resizeDialog;
|
|
resizeDialog.controller = controller;
|
|
resizeDialog.newReplicas = Core.pathGet(controller, ["status", "replicas"]);
|
|
resizeDialog.dialog.open();
|
|
|
|
$timeout(() => {
|
|
$('#replicas').focus();
|
|
}, 50);
|
|
},
|
|
close: () => {
|
|
$scope.resizeDialog.dialog.close();
|
|
}
|
|
};
|
|
|
|
$scope.triggerBuild = (buildConfig) => {
|
|
var url = buildConfig.$triggerUrl;
|
|
console.log("triggering build at url: " + url);
|
|
if (url) {
|
|
//var data = {};
|
|
var data = null;
|
|
var config = {
|
|
headers: {
|
|
'Content-Type': "application/json"
|
|
}
|
|
};
|
|
var name = Core.pathGet(buildConfig, ["metadata", "name"]);
|
|
Core.notification('info', "Triggering build " + name);
|
|
$http.post(url, data, config).
|
|
success(function (data, status, headers, config) {
|
|
console.log("trigger worked! got data " + angular.toJson(data, true));
|
|
// TODO should we show some link to the build
|
|
Core.notification('info', "Building " + name);
|
|
}).
|
|
error(function (data, status, headers, config) {
|
|
log.warn("Failed to load " + url + " " + data + " " + status);
|
|
Core.notification('error', "Failed to trigger build for " + name + ". Returned code: " + status + " " + data);
|
|
});
|
|
};
|
|
}
|
|
|
|
// update the URL if the filter is changed
|
|
$scope.$watch("tableConfig.filterOptions.filterText", (text) => {
|
|
//var filterText = Kubernetes.findValeOfLabels(text);
|
|
$location.search("q", text);
|
|
});
|
|
|
|
$scope.$on("labelFilterUpdate", ($event, text) => {
|
|
var filterOptions = ($scope.tableConfig || {}).filterOptions || {};
|
|
var currentFilter = filterOptions.filterText;
|
|
if (Core.isBlank(currentFilter)) {
|
|
filterOptions.filterText = text;
|
|
}else{
|
|
var expressions = currentFilter.split(/\s+/);
|
|
if (expressions.indexOf(text) !== -1) {
|
|
// lets exclude this filter expression
|
|
expressions = expressions.remove(text);
|
|
filterOptions.filterText = expressions.join(" ");
|
|
} else {
|
|
filterOptions.filterText = currentFilter + " " + text;
|
|
}
|
|
}
|
|
$scope.id = undefined;
|
|
});
|
|
|
|
}
|
|
|
|
/**
|
|
* Returns the number of pods that are ready
|
|
*/
|
|
export function readyPodCount(service) {
|
|
var count = 0;
|
|
angular.forEach((service || {}).$pods, (pod)=> {
|
|
if (pod.$ready) {
|
|
count++;
|
|
}
|
|
});
|
|
return count;
|
|
}
|
|
|
|
/**
|
|
* Returns the service link URL for either the service name or the service object
|
|
*/
|
|
export function serviceLinkUrl(service, httpOnly = false) {
|
|
if (angular.isObject(service)) {
|
|
var portalIP = service.$host;
|
|
// lets assume no custom port for now for external routes
|
|
var port = null;
|
|
var protocol = "http://";
|
|
var spec = service.spec;
|
|
if (spec) {
|
|
if (!portalIP) {
|
|
portalIP = spec.portalIP;
|
|
}
|
|
var hasHttps = false;
|
|
var hasHttp = false;
|
|
angular.forEach(spec.ports, (portSpec) => {
|
|
var p = portSpec.port;
|
|
if (p) {
|
|
if (p === 443) {
|
|
hasHttps = true;
|
|
} else if (p === 80) {
|
|
hasHttp = true;
|
|
}
|
|
if (!port) {
|
|
port = p;
|
|
}
|
|
}
|
|
});
|
|
if (!hasHttps && !hasHttp && port) {
|
|
// lets treat 8080 as http which is a common service to export
|
|
if (port === 8080) {
|
|
hasHttp = true;
|
|
} else if (port === 8443) {
|
|
hasHttps = true;
|
|
}
|
|
}
|
|
}
|
|
if (portalIP) {
|
|
if (hasHttps) {
|
|
return "https://" + portalIP;
|
|
} else if (hasHttp) {
|
|
return "http://" + portalIP;
|
|
} else if (!httpOnly) {
|
|
if (port) {
|
|
return protocol + portalIP + ":" + port + "/";
|
|
} else {
|
|
return protocol + portalIP;
|
|
}
|
|
}
|
|
}
|
|
} else if (service) {
|
|
var serviceId = service.toString();
|
|
if (serviceId) {
|
|
var ServiceRegistry = getServiceRegistry();
|
|
if (ServiceRegistry) {
|
|
return ServiceRegistry.serviceLink(serviceId) || "";
|
|
}
|
|
}
|
|
}
|
|
return "";
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns the total number of counters for the podCounters object
|
|
*/
|
|
export function podCounterTotal($podCounters) {
|
|
var answer = 0;
|
|
if ($podCounters) {
|
|
angular.forEach(["ready", "valid", "waiting", "error"], (name) => {
|
|
var value = $podCounters[name] || 0;
|
|
answer += value;
|
|
});
|
|
}
|
|
return answer;
|
|
}
|
|
|
|
/**
|
|
* Given the list of pods lets iterate through them and find all pods matching the selector
|
|
* and return counters based on the status of the pod
|
|
*/
|
|
export function createPodCounters(selector, pods, outputPods = [], podLinkQuery = null, podLinkUrl = null) {
|
|
if (!podLinkUrl) {
|
|
podLinkUrl = "/kubernetes/pods";
|
|
}
|
|
var filterFn;
|
|
if (angular.isFunction(selector)) {
|
|
filterFn = selector;
|
|
} else {
|
|
filterFn = (pod) => selectorMatches(selector, getLabels(pod));
|
|
}
|
|
var answer = {
|
|
podsLink: "",
|
|
ready: 0,
|
|
valid: 0,
|
|
waiting: 0,
|
|
error: 0
|
|
};
|
|
if (selector) {
|
|
if (!podLinkQuery) {
|
|
podLinkQuery = Kubernetes.labelsToString(selector, " ");
|
|
}
|
|
answer.podsLink = podLinkUrl + "?q=" + encodeURIComponent(podLinkQuery);
|
|
angular.forEach(pods, pod => {
|
|
if (filterFn(pod)) {
|
|
outputPods.push(pod);
|
|
var status = getStatus(pod);
|
|
if (status) {
|
|
var lower = status.toLowerCase();
|
|
if (lower.startsWith("run")) {
|
|
if (isReady(pod)) {
|
|
answer.ready += 1;
|
|
} else {
|
|
answer.valid += 1;
|
|
}
|
|
} else if (lower.startsWith("wait") || lower.startsWith("pend")) {
|
|
answer.waiting += 1;
|
|
} else if (lower.startsWith("term") || lower.startsWith("error") || lower.startsWith("fail")) {
|
|
answer.error += 1;
|
|
}
|
|
} else {
|
|
answer.error += 1;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
return answer;
|
|
}
|
|
|
|
/**
|
|
* Converts the given json into an array of items. If the json contains a nested set of items then that is sorted; so that services
|
|
* are processed first; then turned into an array. Otherwise the json is put into an array so it can be processed polymorphically
|
|
*/
|
|
export function convertKubernetesJsonToItems(json) {
|
|
var items = json.items;
|
|
if (angular.isArray(items)) {
|
|
// TODO we could check for List or Config types here and warn if not
|
|
|
|
// sort the services first
|
|
var answer = [];
|
|
items.forEach((item) => {
|
|
if (item.kind === "Service") {
|
|
answer.push(item);
|
|
}
|
|
});
|
|
items.forEach((item) => {
|
|
if (item.kind !== "Service") {
|
|
answer.push(item);
|
|
}
|
|
});
|
|
return answer;
|
|
} else {
|
|
return [json];
|
|
}
|
|
}
|
|
|
|
export function isV1beta1Or2() {
|
|
return defaultApiVersion === "v1beta1" || defaultApiVersion === "v1beta2";
|
|
}
|
|
|
|
/**
|
|
* Returns a link to the detail page for the given entity
|
|
*/
|
|
export function entityPageLink(obj) {
|
|
if (obj) {
|
|
function getLink(entity) {
|
|
var viewLink = entity["$viewLink"];
|
|
if (viewLink) {
|
|
return viewLink;
|
|
}
|
|
var id = getName(entity);
|
|
var kind = getKind(entity);
|
|
if (kind && id) {
|
|
var path = kind.substring(0, 1).toLowerCase() + kind.substring(1) + "s";
|
|
var namespace = getNamespace(entity);
|
|
if (namespace && !isIgnoreNamespaceKind(kind)) {
|
|
return Core.url(UrlHelpers.join('/kubernetes/namespace', namespace, path, id));
|
|
} else {
|
|
return Core.url(UrlHelpers.join('/kubernetes', path, id));
|
|
}
|
|
}
|
|
}
|
|
var baseLink = getLink(obj);
|
|
if (!HawtioCore.injector || !baseLink) {
|
|
return baseLink;
|
|
}
|
|
var $routeParams = HawtioCore.injector.get<ng.route.IRouteParamsService>('$routeParams');
|
|
var projectId = $routeParams['project'] || $routeParams['project'];
|
|
if (!projectId) {
|
|
return baseLink;
|
|
}
|
|
return UrlHelpers.join(Developer.projectLink(projectId), baseLink.replace(/^\/kubernetes\//, ''));
|
|
}
|
|
return null;
|
|
}
|
|
|
|
|
|
export function resourceKindToUriPath(kind) {
|
|
var kindPath = kind.toLowerCase() + "s";
|
|
if (kindPath === "replicationControllers" && !isV1beta1Or2()) {
|
|
kindPath = "replicationcontrollers";
|
|
}
|
|
return kindPath;
|
|
}
|
|
|
|
function isIgnoreNamespaceKind(kind) {
|
|
return kind === "Host" || kind === "Minion";
|
|
}
|
|
|
|
/**
|
|
* Returns the root URL for the kind
|
|
*/
|
|
export function kubernetesUrlForKind(KubernetesApiURL, kind, namespace = null, path = null) {
|
|
var pathSegment = "";
|
|
if (path) {
|
|
pathSegment = "/" + Core.trimLeading(path, "/");
|
|
}
|
|
var kindPath = resourceKindToUriPath(kind);
|
|
var ignoreNamespace = isIgnoreNamespaceKind(kind);
|
|
if (isV1beta1Or2() || ignoreNamespace) {
|
|
var postfix = "";
|
|
if (namespace && !ignoreNamespace) {
|
|
postfix = "?namespace=" + namespace;
|
|
}
|
|
return UrlHelpers.join(KubernetesApiURL, kindPath, pathSegment, postfix);
|
|
} else {
|
|
return UrlHelpers.join(KubernetesApiURL, "/namespaces/", namespace , kindPath, pathSegment);
|
|
}
|
|
};
|
|
|
|
/**
|
|
* Returns the base URL for the kind of kubernetes resource or null if it cannot be found
|
|
*/
|
|
export function kubernetesUrlForItemKind(KubernetesApiURL, json) {
|
|
var kind = json.kind;
|
|
if (kind) {
|
|
return kubernetesUrlForKind(KubernetesApiURL, kind, json.namespace);
|
|
} else {
|
|
log.warn("Ignoring missing kind " + kind + " for kubernetes json: " + angular.toJson(json));
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export function kubernetesProxyUrlForService(KubernetesApiURL, service, path = null) {
|
|
var pathSegment = "";
|
|
if (path) {
|
|
pathSegment = "/" + Core.trimLeading(path, "/");
|
|
} else {
|
|
pathSegment = "/";
|
|
}
|
|
var namespace = getNamespace(service);
|
|
if (isV1beta1Or2()) {
|
|
var postfix = "?namespace=" + namespace;
|
|
return UrlHelpers.join(KubernetesApiURL, "/proxy", kubernetesNamespacePath(), "/services/" + getName(service) + pathSegment + postfix);
|
|
} else {
|
|
return UrlHelpers.join(KubernetesApiURL, "/proxy/namespaces/", namespace, "/services/" + getName(service) + pathSegment);
|
|
}
|
|
}
|
|
|
|
|
|
export function kubernetesProxyUrlForServiceCurrentNamespace(service, path = null) {
|
|
var apiPrefix = UrlHelpers.join(kubernetesApiUrl());
|
|
return kubernetesProxyUrlForService(apiPrefix, service, path);
|
|
|
|
}
|
|
export function buildConfigRestUrl(id) {
|
|
return UrlHelpers.join(buildConfigsRestURL(), id);
|
|
}
|
|
|
|
export function deploymentConfigRestUrl(id) {
|
|
return UrlHelpers.join(deploymentConfigsRestURL(), id);
|
|
}
|
|
|
|
export function imageRepositoryRestUrl(id) {
|
|
return UrlHelpers.join(imageRepositoriesRestURL(), id);
|
|
}
|
|
|
|
export function buildRestUrl(id) {
|
|
return UrlHelpers.join(buildsRestURL(), id);
|
|
}
|
|
|
|
export function buildLogsRestUrl(id) {
|
|
return UrlHelpers.join(buildsRestURL(), id, "log");
|
|
}
|
|
|
|
/**
|
|
* Runs the given application JSON
|
|
*/
|
|
export function runApp($location, $scope, $http, KubernetesApiURL, json, name = "App", onSuccessFn = null, namespace = null, onCompleteFn = null) {
|
|
if (json) {
|
|
if (angular.isString(json)) {
|
|
json = angular.fromJson(json);
|
|
}
|
|
name = name || "App";
|
|
var postfix = namespace ? " in namespace " + namespace : "";
|
|
Core.notification('info', "Running " + name + postfix);
|
|
|
|
var items = convertKubernetesJsonToItems(json);
|
|
angular.forEach(items, (item) => {
|
|
var url = kubernetesUrlForItemKind(KubernetesApiURL, item);
|
|
if (url) {
|
|
$http.post(url, item).
|
|
success(function (data, status, headers, config) {
|
|
log.debug("Got status: " + status + " on url: " + url + " data: " + data + " after posting: " + angular.toJson(item));
|
|
if (angular.isFunction(onCompleteFn)) {
|
|
onCompleteFn();
|
|
}
|
|
Core.$apply($scope);
|
|
}).
|
|
error(function (data, status, headers, config) {
|
|
var message = null;
|
|
if (angular.isObject(data)) {
|
|
message = data.message;
|
|
var reason = data.reason;
|
|
if (reason === "AlreadyExists") {
|
|
// lets ignore duplicates
|
|
log.debug("entity already exists at " + url);
|
|
return;
|
|
}
|
|
}
|
|
if (!message) {
|
|
message = "Failed to POST to " + url + " got status: " + status;
|
|
}
|
|
log.warn("Failed to save " + url + " status: " + status + " response: " + angular.toJson(data, true));
|
|
Core.notification('error', message);
|
|
});
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns true if the current status of the pod is running
|
|
*/
|
|
export function isRunning(podCurrentState) {
|
|
var status = (podCurrentState || {}).phase;
|
|
if (status) {
|
|
var lower = status.toLowerCase();
|
|
return lower.startsWith("run");
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns true if the labels object has all of the key/value pairs from the selector
|
|
*/
|
|
export function selectorMatches(selector, labels) {
|
|
if (angular.isObject(labels)) {
|
|
var answer = true;
|
|
var count = 0;
|
|
angular.forEach(selector, (value, key) => {
|
|
count++;
|
|
if (answer && labels[key] !== value) {
|
|
answer = false;
|
|
}
|
|
});
|
|
return answer && count > 0;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Returns the service registry
|
|
*/
|
|
export function getServiceRegistry() {
|
|
var injector = HawtioCore.injector;
|
|
return injector ? injector.get<any>("ServiceRegistry") : null;
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns a link to the kibana logs web application
|
|
*/
|
|
export function kibanaLogsLink(ServiceRegistry) {
|
|
var link = ServiceRegistry.serviceLink(kibanaServiceName);
|
|
if (link) {
|
|
if (!link.endsWith("/")) {
|
|
link += "/";
|
|
}
|
|
return link + "#/dashboard/Fabric8";
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
export function openLogsForPods(ServiceRegistry, $window, namespace, pods) {
|
|
var link = kibanaLogsLink(ServiceRegistry);
|
|
if (link) {
|
|
var query = "";
|
|
var count = 0;
|
|
angular.forEach(pods, (item) => {
|
|
var id = getName(item);
|
|
if (id) {
|
|
var space = query ? " OR " : "";
|
|
count++;
|
|
query += space + '"' + id + '"';
|
|
}
|
|
});
|
|
if (query) {
|
|
if (count > 1) {
|
|
query = "(" + query + ")";
|
|
}
|
|
query = 'kubernetes.namespace_name:"' + namespace + '" AND kubernetes.pod_name:' + query;
|
|
link += "?_a=(query:(query_string:(query:'" + query + "')))";
|
|
var newWindow = $window.open(link, "viewLogs");
|
|
}
|
|
}
|
|
}
|
|
|
|
export function resizeController($http, KubernetesApiURL, replicationController, newReplicas, onCompleteFn = null) {
|
|
var id = getName(replicationController);
|
|
var namespace = getNamespace(replicationController) || "";
|
|
var url = kubernetesUrlForKind(KubernetesApiURL, "ReplicationController", namespace, id);
|
|
$http.get(url).
|
|
success(function (data, status, headers, config) {
|
|
if (data) {
|
|
var desiredState = data.spec;
|
|
if (!desiredState) {
|
|
desiredState = {};
|
|
data.spec = desiredState;
|
|
}
|
|
desiredState.replicas = newReplicas;
|
|
$http.put(url, data).
|
|
success(function (data, status, headers, config) {
|
|
log.debug("updated controller " + url);
|
|
if (angular.isFunction(onCompleteFn)) {
|
|
onCompleteFn();
|
|
}
|
|
}).
|
|
error(function (data, status, headers, config) {
|
|
log.warn("Failed to save " + url + " " + data + " " + status);
|
|
});
|
|
}
|
|
}).
|
|
error(function (data, status, headers, config) {
|
|
log.warn("Failed to load " + url + " " + data + " " + status);
|
|
});
|
|
}
|
|
|
|
export function statusTextToCssClass(text, ready = false) {
|
|
if (text) {
|
|
var lower = text.toLowerCase();
|
|
if (lower.startsWith("run") || lower.startsWith("ok")) {
|
|
if (!ready) {
|
|
return "fa fa-spinner fa-spin green";
|
|
}
|
|
return 'fa fa-play-circle green';
|
|
} else if (lower.startsWith("wait") || lower.startsWith("pend")) {
|
|
return 'fa fa-download';
|
|
} else if (lower.startsWith("term") || lower.startsWith("error") || lower.startsWith("fail")) {
|
|
return 'fa fa-off orange';
|
|
} else if (lower.startsWith("succeeded")) {
|
|
return 'fa fa-check-circle-o green';
|
|
}
|
|
}
|
|
return 'fa fa-question red';
|
|
}
|
|
|
|
export function podStatus(pod) {
|
|
return getStatus(pod);
|
|
}
|
|
|
|
export function isReady(pod) {
|
|
var status = pod.status || {};
|
|
var answer = false;
|
|
angular.forEach(status.conditions, (condition) => {
|
|
var t = condition.type;
|
|
if (t && t === "Ready") {
|
|
var status = condition.status;
|
|
if (status === "True") {
|
|
answer = true;
|
|
}
|
|
}
|
|
});
|
|
return answer;
|
|
}
|
|
|
|
export function createAppViewPodCounters(appView) {
|
|
var array = [];
|
|
var map = {};
|
|
var pods = appView.pods;
|
|
var lowestDate = null;
|
|
angular.forEach(pods, pod => {
|
|
var selector = getLabels(pod);
|
|
var selectorText = Kubernetes.labelsToString(selector, " ");
|
|
var answer = map[selector];
|
|
if (!answer) {
|
|
answer = {
|
|
labelText: selectorText,
|
|
podsLink: UrlHelpers.join("/kubernetes/namespace/", pod.metadata.namespace, "pods?q=" + encodeURIComponent(selectorText)),
|
|
valid: 0,
|
|
waiting: 0,
|
|
error: 0
|
|
};
|
|
map[selector] = answer;
|
|
array.push(answer);
|
|
}
|
|
var status = (podStatus(pod) || "Error").toLowerCase();
|
|
if (status.startsWith("run") || status.startsWith("ok")) {
|
|
answer.valid += 1;
|
|
} else if (status.startsWith("wait") || status.startsWith("pwnd")) {
|
|
answer.waiting += 1;
|
|
} else {
|
|
answer.error += 1;
|
|
}
|
|
var creationTimestamp = getCreationTimestamp(pod);
|
|
if (creationTimestamp) {
|
|
var d = new Date(creationTimestamp);
|
|
if (!lowestDate || d < lowestDate) {
|
|
lowestDate = d;
|
|
}
|
|
}
|
|
});
|
|
appView.$creationDate = lowestDate;
|
|
return array;
|
|
}
|
|
|
|
export function createAppViewServiceViews(appView) {
|
|
var array = [];
|
|
var pods = appView.pods;
|
|
angular.forEach(pods, pod => {
|
|
var id = getName(pod);
|
|
if (id) {
|
|
var abbrev = id;
|
|
var idx = id.indexOf("-");
|
|
if (idx > 1) {
|
|
abbrev = id.substring(0, idx);
|
|
}
|
|
pod.idAbbrev = abbrev;
|
|
}
|
|
pod.statusClass = statusTextToCssClass(podStatus(pod), isReady(pod));
|
|
});
|
|
|
|
var services = appView.services || [];
|
|
var replicationControllers = appView.replicationControllers || [];
|
|
var size = Math.max(services.length, replicationControllers.length, 1);
|
|
var appName = appView.$info.name;
|
|
for (var i = 0; i < size; i++) {
|
|
var service = services[i];
|
|
var replicationController = replicationControllers[i];
|
|
var controllerId = getName(replicationController);
|
|
var name = getName(service) || controllerId;
|
|
var address = Core.pathGet(service, ["spec", "portalIP"]);
|
|
if (!name && pods.length) {
|
|
name = pods[0].idAbbrev;
|
|
}
|
|
if (!appView.$info.name) {
|
|
appView.$info.name = name;
|
|
}
|
|
if (!appView.id && pods.length) {
|
|
appView.id = getName(pods[0]);
|
|
}
|
|
if (i > 0) {
|
|
appName = name;
|
|
}
|
|
var podCount = pods.length;
|
|
var podCountText = podCount + " pod" + (podCount > 1 ? "s" : "");
|
|
var view = {
|
|
appName: appName || name,
|
|
name: name,
|
|
createdDate: appView.$creationDate,
|
|
podCount: podCount,
|
|
podCountText: podCountText,
|
|
address: address,
|
|
controllerId: controllerId,
|
|
service: service,
|
|
replicationController: replicationController,
|
|
pods: pods
|
|
};
|
|
array.push(view);
|
|
}
|
|
return array;
|
|
}
|
|
|
|
/**
|
|
* converts a git path into an accessible URL for the browser
|
|
*/
|
|
export function gitPathToUrl(iconPath, branch = "master") {
|
|
return (HawtioCore.injector.get<string>('AppLibraryURL') || '') + "/git/" + branch + iconPath;
|
|
}
|
|
|
|
function asDate(value) {
|
|
return value ? new Date(value) : null;
|
|
}
|
|
|
|
export function enrichBuildConfig(buildConfig, sortedBuilds) {
|
|
if (buildConfig) {
|
|
var triggerUrl:string = null;
|
|
var metadata = buildConfig.metadata || {};
|
|
var name = metadata.name;
|
|
buildConfig.$name = name;
|
|
var projectLink = Developer.projectLink(name);
|
|
var ns = metadata.namespace || currentKubernetesNamespace();
|
|
buildConfig.$namespace = ns;
|
|
buildConfig.environments = [];
|
|
buildConfig.$creationDate = asDate(Kubernetes.getCreationTimestamp(buildConfig));
|
|
buildConfig.$labelsText = Kubernetes.labelsToString(getLabels(buildConfig));
|
|
|
|
if (name) {
|
|
buildConfig.$viewLink = UrlHelpers.join("workspaces", ns, "projects", name, "environments");
|
|
buildConfig.$editLink = UrlHelpers.join("workspaces", ns, "projects", name, "buildConfigEdit");
|
|
|
|
angular.forEach([false, true], (flag) => {
|
|
angular.forEach(buildConfig.triggers, (trigger) => {
|
|
if (!triggerUrl) {
|
|
var type = trigger.type;
|
|
if (type === "generic" || flag) {
|
|
var generic = trigger[type];
|
|
if (type && generic) {
|
|
var secret = generic.secret;
|
|
if (secret) {
|
|
triggerUrl = UrlHelpers.join(buildConfigHooksRestURL(), name, secret, type);
|
|
buildConfig.$triggerUrl = triggerUrl;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
// lets find the latest build...
|
|
if (sortedBuilds) {
|
|
buildConfig.$lastBuild = _.find(sortedBuilds, {
|
|
metadata: {
|
|
labels: {
|
|
buildconfig: name
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
var $fabric8Views = {};
|
|
|
|
function defaultPropertiesIfNotExist(name, object, autoCreate = false) {
|
|
var view = $fabric8Views[name];
|
|
if (autoCreate && !view) {
|
|
view = {}
|
|
$fabric8Views[name] = view;
|
|
}
|
|
if (view) {
|
|
angular.forEach(object, (value, property) => {
|
|
var current = view[property];
|
|
if (!current) {
|
|
view[property] = value;
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
function defaultPropertiesIfNotExistStartsWith(prefix, object, autoCreate = false) {
|
|
angular.forEach($fabric8Views, (view, name) => {
|
|
if (view && name.startsWith(prefix)) {
|
|
angular.forEach(object, (value, property) => {
|
|
var current = view[property];
|
|
if (!current) {
|
|
view[property] = value;
|
|
}
|
|
});
|
|
}
|
|
});
|
|
}
|
|
|
|
var labels = metadata.labels || {};
|
|
var annotations = metadata.annotations || {};
|
|
|
|
// lets default the repo and user
|
|
buildConfig.$user = annotations["fabric8.jenkins/user"] || labels["user"];
|
|
buildConfig.$repo = annotations["fabric8.jenkins/repo"] || labels["repo"];
|
|
|
|
angular.forEach(annotations, (value, key) => {
|
|
var parts = key.split('/', 2);
|
|
if (parts.length > 1) {
|
|
var linkId = parts[0];
|
|
var property = parts[1];
|
|
if (linkId && property && linkId.startsWith("fabric8.link")) {
|
|
var link = $fabric8Views[linkId];
|
|
if (!link) {
|
|
link = {
|
|
class: linkId
|
|
};
|
|
$fabric8Views[linkId] = link;
|
|
}
|
|
link[property] = value;
|
|
}
|
|
}
|
|
});
|
|
|
|
if (buildConfig.$user && buildConfig.$repo) {
|
|
// browse gogs repo view
|
|
var gogsUrl = serviceLinkUrl(gogsServiceName);
|
|
if (gogsUrl) {
|
|
defaultPropertiesIfNotExist("fabric8.link.browseGogs.view", {
|
|
label: "Browse...",
|
|
url: UrlHelpers.join(gogsUrl, buildConfig.$user, buildConfig.$repo),
|
|
description: "Browse the source code of this repository",
|
|
iconClass: "fa fa-external-link"
|
|
}, true);
|
|
}
|
|
|
|
// run forge commands view
|
|
defaultPropertiesIfNotExist("fabric8.link.forgeCommand.view", {
|
|
label: "Command...",
|
|
url: UrlHelpers.join(projectLink, "/forge/commands/user", buildConfig.$user, buildConfig.$repo),
|
|
description: "Perform an action on this project",
|
|
iconClass: "fa fa-play-circle"
|
|
}, true);
|
|
|
|
|
|
// configure devops view
|
|
defaultPropertiesIfNotExist("fabric8.link.forgeCommand.devops.settings", {
|
|
label: "Settings",
|
|
url: UrlHelpers.join(projectLink, "/forge/command/devops-edit/user", buildConfig.$user, buildConfig.$repo),
|
|
description: "Configure the DevOps settings for this project",
|
|
iconClass: "fa fa-pencil-square-o"
|
|
}, true);
|
|
|
|
}
|
|
|
|
// add some icons and descriptions
|
|
defaultPropertiesIfNotExist("fabric8.link.repository.browse", {
|
|
label: "Browse...",
|
|
description: "Browse the source code of this repository",
|
|
iconClass: "fa fa-external-link"
|
|
});
|
|
defaultPropertiesIfNotExist("fabric8.link.jenkins.job", {
|
|
iconClass: "fa fa-tasks",
|
|
description: "View the Jenkins Job for this build"
|
|
});
|
|
defaultPropertiesIfNotExist("fabric8.link.jenkins.monitor", {
|
|
iconClass: "fa fa-tachometer",
|
|
description: "View the Jenkins Monitor dashboard for this project"
|
|
});
|
|
defaultPropertiesIfNotExist("fabric8.link.jenkins.pipeline", {
|
|
iconClass: "fa fa-arrow-circle-o-right",
|
|
description: "View the Jenkins Pipeline for this project"
|
|
});
|
|
defaultPropertiesIfNotExist("fabric8.link.letschat.room", {
|
|
iconClass: "fa fa-comment",
|
|
description: "Chat room for this project"
|
|
});
|
|
defaultPropertiesIfNotExist("fabric8.link.letschat.room", {
|
|
iconClass: "fa fa-comment",
|
|
description: "Chat room for this project"
|
|
});
|
|
defaultPropertiesIfNotExist("fabric8.link.taiga", {
|
|
iconClass: "fa fa-check-square-o",
|
|
description: "Issue tracker for this project"
|
|
});
|
|
defaultPropertiesIfNotExist("fabric8.link.issues", {
|
|
iconClass: "fa fa-check-square-o",
|
|
description: "Issues for this project"
|
|
});
|
|
defaultPropertiesIfNotExist("fabric8.link.releases", {
|
|
iconClass: "fa fa-tag",
|
|
description: "Issues for this project"
|
|
});
|
|
defaultPropertiesIfNotExist("fabric8.link.taiga.team", {
|
|
iconClass: "fa fa-users",
|
|
description: "Team members for this project"
|
|
});
|
|
defaultPropertiesIfNotExist("fabric8.link.team", {
|
|
iconClass: "fa fa-users",
|
|
description: "Team members for this project"
|
|
});
|
|
defaultPropertiesIfNotExistStartsWith("fabric8.link.environment.", {
|
|
iconClass: "fa fa-cloud",
|
|
description: "The kubernetes namespace for this environment"
|
|
});
|
|
|
|
|
|
// lets put the views into sections...
|
|
var $fabric8CodeViews = {};
|
|
var $fabric8BuildViews = {};
|
|
var $fabric8TeamViews = {};
|
|
var $fabric8EnvironmentViews = {};
|
|
angular.forEach($fabric8Views, (value, key) => {
|
|
var view;
|
|
if (key.indexOf("taiga") > 0 || key.indexOf(".issue") > 0 || key.indexOf("letschat") > 0|| key.indexOf(".team") > 0) {
|
|
view = $fabric8TeamViews;
|
|
} else if (key.indexOf("jenkins") > 0) {
|
|
view = $fabric8BuildViews;
|
|
} else if (key.indexOf(".environment.") > 0) {
|
|
view = $fabric8EnvironmentViews;
|
|
} else {
|
|
view = $fabric8CodeViews;
|
|
}
|
|
view[key] = value;
|
|
});
|
|
|
|
|
|
buildConfig.$fabric8Views = $fabric8Views;
|
|
buildConfig.$fabric8CodeViews = $fabric8CodeViews;
|
|
buildConfig.$fabric8BuildViews = $fabric8BuildViews;
|
|
buildConfig.$fabric8EnvironmentViews = $fabric8EnvironmentViews;
|
|
buildConfig.$fabric8TeamViews = $fabric8TeamViews;
|
|
|
|
var $jenkinsJob = annotations["fabric8.io/jenkins-job"];
|
|
if (!$jenkinsJob && $fabric8Views["fabric8.link.jenkins.job"]) {
|
|
$jenkinsJob = name;
|
|
}
|
|
buildConfig.$jenkinsJob = $jenkinsJob;
|
|
|
|
angular.forEach($fabric8EnvironmentViews, (env) => {
|
|
var c = env.class;
|
|
var prefix = "fabric8.link.environment.";
|
|
if (c && c.startsWith(prefix)) {
|
|
var ens = c.substring(prefix.length);
|
|
env.namespace = ens;
|
|
env.url = UrlHelpers.join("/workspaces", ns, "projects", name, "namespace", ens);
|
|
}
|
|
buildConfig.environments.push(env);
|
|
});
|
|
if (!buildConfig.environments.length) {
|
|
// lets create a single environment
|
|
var ens = ns;
|
|
var env = {
|
|
namespace: ens,
|
|
label: "Current",
|
|
description: "The environemnt that this project is built and run inside",
|
|
iconClass: "fa fa-cloud",
|
|
url: UrlHelpers.join("/workspaces", ns, "projects", name, "namespace", ens)
|
|
};
|
|
buildConfig.environments.push(env);
|
|
|
|
}
|
|
|
|
buildConfig.environments = buildConfig.environments.reverse();
|
|
|
|
buildConfig.tools = [];
|
|
angular.forEach($fabric8CodeViews, (env) => {
|
|
buildConfig.tools.push(env);
|
|
});
|
|
angular.forEach($fabric8TeamViews, (env) => {
|
|
buildConfig.tools.push(env);
|
|
});
|
|
|
|
}
|
|
}
|
|
|
|
export function enrichBuildConfigs(buildConfigs, sortedBuilds = null) {
|
|
angular.forEach(buildConfigs, (buildConfig) => {
|
|
enrichBuildConfig(buildConfig, sortedBuilds);
|
|
});
|
|
return buildConfigs;
|
|
}
|
|
|
|
export function enrichBuilds(builds) {
|
|
angular.forEach(builds, (build) => {
|
|
enrichBuild(build);
|
|
});
|
|
return _.sortBy(builds, "$creationDate").reverse();
|
|
}
|
|
|
|
export function enrichBuild(build) {
|
|
if (build) {
|
|
var metadata = build.metadata || {};
|
|
var annotations = metadata.annotations || {};
|
|
var name = getName(build);
|
|
var namespace = getNamespace(build);
|
|
build.$name = name;
|
|
build.$namespace = namespace;
|
|
|
|
var nameArray = name.split("-");
|
|
var nameArrayLength = nameArray.length;
|
|
build.$shortName = (nameArrayLength > 4) ? nameArray.slice(0, nameArrayLength - 4).join("-") : name.substring(0, 30);
|
|
|
|
var labels = getLabels(build);
|
|
var configId = labels.buildconfig;
|
|
build.$configId = configId;
|
|
if (configId) {
|
|
//build.$configLink = UrlHelpers.join("kubernetes/buildConfigs", configId);
|
|
build.$configLink = UrlHelpers.join("workspaces", currentKubernetesNamespace(), "projects", configId);
|
|
}
|
|
var creationTimestamp = getCreationTimestamp(build);
|
|
if (creationTimestamp) {
|
|
var d = new Date(creationTimestamp);
|
|
build.$creationDate = d;
|
|
}
|
|
if (name) {
|
|
//build.$viewLink = UrlHelpers.join("kubernetes/builds", name);
|
|
var projectLink = UrlHelpers.join("workspaces", currentKubernetesNamespace(), "projects", configId);
|
|
build.$viewLink = UrlHelpers.join(projectLink, "builds", name);
|
|
//build.$logsLink = UrlHelpers.join("kubernetes/buildLogs", name);
|
|
build.$logsLink = UrlHelpers.join(projectLink, "buildLogs", name);
|
|
}
|
|
build.podName = build.podName || annotations["openshift.io/build.pod-name"];
|
|
var podName = build.podName;
|
|
if (podName && namespace) {
|
|
var podNameArray = podName.split("-");
|
|
var podNameArrayLength = podNameArray.length;
|
|
build.$podShortName = (podNameArrayLength > 5) ? podNameArray[podNameArrayLength - 5] : podName.substring(0, 30);
|
|
build.$podLink = UrlHelpers.join("kubernetes/namespace", namespace, "pods", podName);
|
|
}
|
|
}
|
|
return build;
|
|
}
|
|
|
|
|
|
export function enrichDeploymentConfig(deploymentConfig) {
|
|
if (deploymentConfig) {
|
|
var triggerUrl:string = null;
|
|
var name = Core.pathGet(deploymentConfig, ["metadata", "name"]);
|
|
deploymentConfig.$name = name;
|
|
var found = false;
|
|
angular.forEach(deploymentConfig.triggers, (trigger) => {
|
|
var type = trigger.type;
|
|
if (!deploymentConfig.$imageChangeParams && type === "ImageChange") {
|
|
var imageChangeParams = trigger.imageChangeParams;
|
|
if (imageChangeParams) {
|
|
var containerNames = imageChangeParams.containerNames || [];
|
|
imageChangeParams.$containerNames = containerNames.join(" ");
|
|
deploymentConfig.$imageChangeParams = imageChangeParams;
|
|
}
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
export function enrichDeploymentConfigs(deploymentConfigs) {
|
|
angular.forEach(deploymentConfigs, (deploymentConfig) => {
|
|
enrichDeploymentConfig(deploymentConfig);
|
|
});
|
|
return deploymentConfigs;
|
|
}
|
|
|
|
|
|
export function enrichEvent(event) {
|
|
if (event) {
|
|
var metadata = event.metadata || {};
|
|
|
|
var firstTimestamp = event.firstTimestamp;
|
|
if (firstTimestamp) {
|
|
var d = new Date(firstTimestamp);
|
|
event.$firstTimestamp = d;
|
|
}
|
|
var lastTimestamp = event.lastTimestamp;
|
|
if (lastTimestamp) {
|
|
var d = new Date(lastTimestamp);
|
|
event.$lastTimestamp = d;
|
|
}
|
|
var labels = angular.copy(event.source || {});
|
|
var involvedObject = event.involvedObject || {};
|
|
var name = involvedObject.name;
|
|
var kind = involvedObject.kind;
|
|
if (name) {
|
|
labels['name'] = name;
|
|
}
|
|
if (kind) {
|
|
labels['kind'] = kind;
|
|
}
|
|
event.$labelsText = Kubernetes.labelsToString(labels);
|
|
|
|
}
|
|
}
|
|
|
|
export function enrichEvents(events, model = null) {
|
|
angular.forEach(events, (event) => {
|
|
enrichEvent(event);
|
|
});
|
|
|
|
// lets update links to the events for each pod and RC
|
|
if (model) {
|
|
function clearEvents(entity) {
|
|
entity.$events = [];
|
|
entity.$eventsLink = null;
|
|
entity.$eventCount = 0;
|
|
}
|
|
|
|
function updateEvent(entity, event) {
|
|
if (entity) {
|
|
entity.$events.push(event);
|
|
if (!entity.$eventsLink) {
|
|
entity.$eventsLink = UrlHelpers.join("/kubernetes/namespace/", currentKubernetesNamespace(), "events") + "?q=kind%3D" + entity.kind + "%20name%3D" + entity.metadata.name;
|
|
}
|
|
entity.$eventCount = entity.$events.length;
|
|
}
|
|
}
|
|
|
|
var pods = model.pods || [];
|
|
var rcs = model.replicationControllers || [];
|
|
angular.forEach(pods, clearEvents);
|
|
angular.forEach(rcs, clearEvents);
|
|
|
|
angular.forEach(events, (event) => {
|
|
var involvedObject = event.involvedObject || {};
|
|
var name = involvedObject.name;
|
|
var kind = involvedObject.kind;
|
|
var ns = model.currentNamespace();
|
|
if (name && kind && ns) {
|
|
var entity = null;
|
|
if (kind === "ReplicationController") {
|
|
entity = model.getReplicationController(ns, name);
|
|
} else if (kind === "Pod") {
|
|
entity = model.getPod(ns, name);
|
|
}
|
|
if (entity) {
|
|
updateEvent(entity, event);
|
|
}
|
|
}
|
|
});
|
|
}
|
|
return events;
|
|
}
|
|
|
|
export function enrichImageRepository(imageRepository) {
|
|
if (imageRepository) {
|
|
var triggerUrl:string = null;
|
|
var name = Core.pathGet(imageRepository, ["metadata", "name"]);
|
|
imageRepository.$name = name;
|
|
}
|
|
}
|
|
|
|
export function enrichImageRepositories(imageRepositories) {
|
|
angular.forEach(imageRepositories, (imageRepository) => {
|
|
enrichImageRepository(imageRepository);
|
|
});
|
|
return imageRepositories;
|
|
}
|
|
|
|
|
|
var labelColors = {
|
|
'batch': 'k8s-badge-batch',
|
|
'region': 'k8s-badge-region',
|
|
'type': 'k8s-badge-type',
|
|
'system': 'k8s-badge-system'
|
|
};
|
|
|
|
export function containerLabelClass(labelType:string) {
|
|
if (!(labelType in labelColors)) {
|
|
return 'mouse-pointer';
|
|
}
|
|
else return labelColors[labelType] + ' mouse-pointer';
|
|
}
|
|
|
|
|
|
/**
|
|
* Returns true if the fabric8 forge plugin is enabled
|
|
*/
|
|
export function isForgeEnabled() {
|
|
// TODO should return true if the service "fabric8-forge" is valid
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Returns the current kubernetes selected namespace or the default one
|
|
*/
|
|
export function currentKubernetesNamespace() {
|
|
var injector = HawtioCore.injector;
|
|
if (injector) {
|
|
var KubernetesState = injector.get<any>("KubernetesState") || {};
|
|
return KubernetesState.selectedNamespace || defaultNamespace;
|
|
}
|
|
return defaultNamespace;
|
|
}
|
|
|
|
export function setCurrentKubernetesNamespace(ns) {
|
|
if (ns) {
|
|
var KubernetesState = inject<any>("KubernetesState") || {};
|
|
KubernetesState.selectedNamespace = ns;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Configures the json schema
|
|
*/
|
|
export function configureSchema() {
|
|
angular.forEach(schema.definitions, (definition, name) => {
|
|
var properties = definition.properties;
|
|
if (properties) {
|
|
var hideProperties = ["creationTimestamp", "kind", "apiVersion", "annotations", "additionalProperties", "namespace", "resourceVersion", "selfLink", "uid"];
|
|
angular.forEach(hideProperties, (propertyName) => {
|
|
var property = properties[propertyName];
|
|
if (property) {
|
|
property["hidden"] = true;
|
|
}
|
|
});
|
|
angular.forEach(properties, (property, propertyName) => {
|
|
var ref = property["$ref"];
|
|
var type = property["type"];
|
|
if (ref && (!type || type === "object")) {
|
|
property["type"] = ref;
|
|
}
|
|
if (type === "array") {
|
|
var items = property["items"];
|
|
if (items) {
|
|
var ref = items["$ref"];
|
|
var type = items["type"];
|
|
if (ref && (!type || type === "object")) {
|
|
items["type"] = ref;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
}
|
|
|
|
schema.definitions.os_build_WebHookTrigger.properties.secret.type = "password";
|
|
})
|
|
}
|
|
|
|
/**
|
|
* Lets remove any enriched data to leave the original json intact
|
|
*/
|
|
export function unenrich(item) {
|
|
var o = _.cloneDeep(item);
|
|
angular.forEach(o, (value, key) => {
|
|
if (key.startsWith("$") || key.startsWith("_")) {
|
|
delete o[key];
|
|
}
|
|
});
|
|
delete o['connectTo'];
|
|
return o;
|
|
}
|
|
|
|
/**
|
|
* Returns the unenriched JSON representation of an object
|
|
*/
|
|
export function toRawJson(item) {
|
|
var o = unenrich(item);
|
|
return JSON.stringify(o, null, 2); // spacing level = 2
|
|
}
|
|
|
|
/**
|
|
* Returns the unenriched YAML representation of an object
|
|
*/
|
|
export function toRawYaml(item) {
|
|
var o = unenrich(item);
|
|
return jsyaml.dump(o, { indent: 2 });
|
|
}
|
|
|
|
|
|
export function watch($scope: any, $element: any, kind, ns, fn, labelSelector = null) {
|
|
var connection = KubernetesAPI.watch({
|
|
kind: kind,
|
|
namespace: ns,
|
|
labelSelector: labelSelector,
|
|
success: function (objects) {
|
|
fn(objects);
|
|
Core.$apply($scope);
|
|
}
|
|
});
|
|
$element.on('$destroy', () => {
|
|
console.log("Static controller[" + kind + ", " + ns + "] element destroyed");
|
|
$scope.$destroy();
|
|
});
|
|
$scope.$on('$destroy', () => {
|
|
console.log("Static controller[" + kind + ", " + ns + "] scope destroyed");
|
|
connection.disconnect();
|
|
});
|
|
var oldDeleteScopeFn = $scope.deleteScope;
|
|
$scope.deleteScope = function () {
|
|
$element.remove();
|
|
if (angular.isFunction(oldDeleteScopeFn)) {
|
|
oldDeleteScopeFn();
|
|
}
|
|
}
|
|
}
|
|
|
|
export function createKubernetesClient(kind, ns = null) {
|
|
var K8SClientFactory = inject<any>("K8SClientFactory");
|
|
if (!K8SClientFactory) {
|
|
log.warn("Could not find injected K8SClientFactory!");
|
|
return null;
|
|
}
|
|
if (kind === "projects" || kind === "namespaces") {
|
|
ns = null;
|
|
} else if (!ns) {
|
|
ns = Kubernetes.currentKubernetesNamespace();
|
|
}
|
|
return K8SClientFactory.create(kind, ns);
|
|
}
|
|
|
|
|
|
export function currentUserName() {
|
|
var userDetails = HawtioOAuth.getUserProfile();
|
|
var answer = null;
|
|
if (userDetails) {
|
|
answer = getName(userDetails);
|
|
}
|
|
return answer || "admin";
|
|
}
|
|
|
|
export function createNamespace(ns, client?) {
|
|
if (!client) {
|
|
client = isOpenShift ? Kubernetes.createKubernetesClient('projects') : Kubernetes.createKubernetesClient('namespaces');
|
|
}
|
|
if (ns && ns !== currentKubernetesNamespace()) {
|
|
var object = {
|
|
apiVersion: Kubernetes.defaultApiVersion,
|
|
kind: isOpenShift ? 'Project' : 'Namespace',
|
|
metadata: {
|
|
name: ns,
|
|
labels: {
|
|
}
|
|
}
|
|
};
|
|
client.put(object,
|
|
(data) => {
|
|
log.info("Created namespace: " + ns)
|
|
},
|
|
(err) => {
|
|
log.warn("Failed to create namespace: " + ns + ": " + angular.toJson(err));
|
|
});
|
|
}
|
|
}
|
|
|
|
export function createRC(obj, onCompleteFn = null){
|
|
var client = Kubernetes.createKubernetesClient('replicationcontrollers','default');
|
|
var RCTemplate = new resourceRCTemplate();
|
|
var rcTemplate = RCTemplate.createRC(obj);
|
|
client.put(rcTemplate, function(obj) {
|
|
console.log("Created: ", obj);
|
|
if (angular.isFunction(onCompleteFn)) {
|
|
onCompleteFn(obj);
|
|
}
|
|
});
|
|
}
|
|
|
|
export function updateReplicationControllerLabels($http, KubernetesApiURL, replicationController, newLabels, onCompleteFn = null){
|
|
var id = getName(replicationController);
|
|
var namespace = getNamespace(replicationController) || "";
|
|
var url = kubernetesUrlForKind(KubernetesApiURL, "ReplicationController", namespace, id);
|
|
$http.get(url).
|
|
success(function (data, status, headers, config) {
|
|
if (data) {
|
|
var desiredLabels = data.metadata;
|
|
if (!desiredLabels) {
|
|
desiredLabels = {};
|
|
data.metadata = desiredLabels;
|
|
}
|
|
desiredLabels.labels = newLabels;
|
|
$http.put(url, data).
|
|
success(function (data, status, headers, config) {
|
|
log.debug("updated controller " + url);
|
|
if (angular.isFunction(onCompleteFn)) {
|
|
onCompleteFn();
|
|
}
|
|
}).
|
|
error(function (data, status, headers, config) {
|
|
log.warn("Failed to save " + url + " " + data + " " + status);
|
|
});
|
|
}
|
|
}).
|
|
error(function (data, status, headers, config) {
|
|
log.warn("Failed to load " + url + " " + data + " " + status);
|
|
});
|
|
}
|
|
|
|
export function connectOracle($http, $timeout, url, connectParam, rcName, delayTime){
|
|
$timeout(() => {
|
|
$http({
|
|
url: url,
|
|
method:'POST',
|
|
params:{oracleName: rcName, param: connectParam}
|
|
}).success(function(data,header,config,status){
|
|
console.log("success");
|
|
}).error(function(data,header,config,status){
|
|
log.warn("Failed to connect " + connectParam + " " + data + " " + status);
|
|
});
|
|
}, delayTime);
|
|
}
|
|
|
|
export function getOracleStatus(labels){
|
|
var answer = -1;
|
|
if(typeof(labels) === 'object' && labels.hasOwnProperty("status")){
|
|
switch(labels.status){
|
|
case '0':
|
|
answer = 0;
|
|
break;
|
|
case '1':
|
|
answer = 1;
|
|
break;
|
|
case '2':
|
|
answer = 2;
|
|
break;
|
|
default:
|
|
answer = -1;
|
|
}
|
|
}
|
|
return answer;
|
|
}
|
|
}
|