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.
331 lines
11 KiB
331 lines
11 KiB
9 years ago
|
/// <reference path="kubernetesPlugin.ts"/>
|
||
|
|
||
|
module Kubernetes {
|
||
|
export var TemplateController = controller("TemplateController", [
|
||
|
"$scope", "$location", "$http", "$timeout", "$routeParams", "marked", "$templateCache", "$modal", "KubernetesModel", "KubernetesState", "KubernetesApiURL",
|
||
|
($scope, $location, $http, $timeout, $routeParams, marked, $templateCache, $modal, KubernetesModel, KubernetesState, KubernetesApiURL) => {
|
||
|
var model = $scope.model = KubernetesModel;
|
||
|
$scope.filterText = $location.search()["q"];
|
||
|
|
||
|
$scope.targetNamespace = $routeParams.targetNamespace;
|
||
|
initShared($scope, $location, $http, $timeout, $routeParams, KubernetesModel, KubernetesState, KubernetesApiURL);
|
||
|
|
||
|
$scope.$watchCollection('model.namespaces', () => {
|
||
|
if (!$scope.targetNamespace) {
|
||
|
$scope.targetNamespace = model.currentNamespace();
|
||
|
}
|
||
|
});
|
||
|
|
||
|
var returnTo = new URI($location.search()['returnTo'] || '/kubernetes/apps');
|
||
|
|
||
|
function goBack() {
|
||
|
$location.path(returnTo.path()).search(returnTo.query(true));
|
||
|
}
|
||
|
|
||
|
function getAnnotations(obj) {
|
||
|
return Core.pathGet(obj, ['metadata', 'annotations']);
|
||
|
}
|
||
|
|
||
|
function getValueFor(obj, key) {
|
||
|
var annotations = getAnnotations(obj);
|
||
|
if (!annotations) {
|
||
|
return "";
|
||
|
}
|
||
|
var name = getName(obj);
|
||
|
if (name) {
|
||
|
var fullKey = "fabric8." + name + "/" + key;
|
||
|
var answer = annotations[fullKey];
|
||
|
if (answer) {
|
||
|
return answer;
|
||
|
}
|
||
|
}
|
||
|
var key: any = _.find(_.keys(annotations), (k: string) => _.endsWith(k, key));
|
||
|
if (key) {
|
||
|
return annotations[key];
|
||
|
} else {
|
||
|
return "";
|
||
|
}
|
||
|
}
|
||
|
|
||
|
$scope.cancel = () => {
|
||
|
if ($scope.formConfig) {
|
||
|
delete $scope.formConfig;
|
||
|
delete $scope.entity;
|
||
|
$scope.objects = undefined;
|
||
|
return;
|
||
|
}
|
||
|
goBack();
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
$scope.$watch('model.templates.length', (newValue) => {
|
||
|
if (newValue === 0) {
|
||
|
goBack();
|
||
|
}
|
||
|
});
|
||
|
*/
|
||
|
|
||
|
$scope.filterTemplates = (template) => {
|
||
|
if (Core.isBlank($scope.filterText)) {
|
||
|
return true;
|
||
|
}
|
||
|
return _.contains(angular.toJson(template), $scope.filterText.toLowerCase());
|
||
|
};
|
||
|
|
||
|
$scope.openFullDescription = (template) => {
|
||
|
var text = marked(getValueFor(template, 'description') || 'No description');
|
||
|
var modal = $modal.open({
|
||
|
templateUrl: UrlHelpers.join(templatePath, 'templateDescription.html'),
|
||
|
controller: ['$scope', '$modalInstance', ($scope, $modalInstance) => {
|
||
|
$scope.text = text,
|
||
|
$scope.ok = () => {
|
||
|
modal.close();
|
||
|
}
|
||
|
}]
|
||
|
});
|
||
|
};
|
||
|
|
||
|
$scope.getDescription = (template) => {
|
||
|
var answer:any = $(marked(getValueFor(template, 'description') || 'No description'));
|
||
|
var textDefault = answer.html();
|
||
|
var maxLength = 200;
|
||
|
if (textDefault.length > maxLength) {
|
||
|
var truncated = $.trim(textDefault).substring(0, maxLength).split(' ').slice(0, -1).join(' ');
|
||
|
answer.html(truncated + '...');
|
||
|
answer.append($templateCache.get('truncatedDescriptionTag.html'));
|
||
|
}
|
||
|
return answer.html();
|
||
|
};
|
||
|
|
||
|
$scope.getIconUrl = (template) => {
|
||
|
return getValueFor(template, 'iconUrl') || defaultIconUrl;
|
||
|
};
|
||
|
|
||
|
$scope.deployTemplate = (template) => {
|
||
|
log.debug("Template parameters: ", template.parameters);
|
||
|
log.debug("Template objects: ", template.objects);
|
||
|
log.debug("Template annotations: ", template.metadata.annotations);
|
||
|
var templateAnnotations = template.metadata.annotations;
|
||
|
if (templateAnnotations) {
|
||
|
_.forEach(template.objects, (object:any) => {
|
||
|
var annotations = object.metadata.annotations || {};
|
||
|
var name = getName(object);
|
||
|
var matches = _.filter(_.keys(templateAnnotations), (key) => key.match('.' + name + '/'));
|
||
|
matches.forEach((match) => {
|
||
|
if (!(match in annotations)) {
|
||
|
annotations[match] = templateAnnotations[match];
|
||
|
}
|
||
|
});
|
||
|
object.metadata.annotations = annotations;
|
||
|
});
|
||
|
}
|
||
|
var routeServiceName = <string> undefined;
|
||
|
var service = _.find(template.objects, (obj) => {
|
||
|
if (getKind(obj) === "Service") {
|
||
|
var ports = getPorts(obj);
|
||
|
if (ports && ports.length === 1) {
|
||
|
return true;
|
||
|
}
|
||
|
} else {
|
||
|
return false;
|
||
|
}
|
||
|
});
|
||
|
if (service) {
|
||
|
routeServiceName = getName(service);
|
||
|
}
|
||
|
log.debug("Service: ", service);
|
||
|
if ((!routeServiceName || !isOpenShift) && (!template.parameters || template.parameters.length === 0)) {
|
||
|
log.debug("No parameters required, deploying objects");
|
||
|
applyObjects(template.objects);
|
||
|
return;
|
||
|
}
|
||
|
var formConfig = {
|
||
|
style: HawtioForms.FormStyle.STANDARD,
|
||
|
hideLegend: true,
|
||
|
properties: <any> {}
|
||
|
};
|
||
|
var params = template.parameters;
|
||
|
_.forEach(params, (param:any) => {
|
||
|
var property = <any> {};
|
||
|
property.label = _.startCase(param.name);
|
||
|
property.description = param.description;
|
||
|
property.default = param.value;
|
||
|
// TODO, do parameters support types?
|
||
|
property.type = 'string';
|
||
|
formConfig.properties[param.name] = property;
|
||
|
});
|
||
|
if (routeServiceName && isOpenShift) {
|
||
|
formConfig.properties.createRoute = {
|
||
|
type: 'boolean',
|
||
|
default: true,
|
||
|
label: "Create Route"
|
||
|
};
|
||
|
/*
|
||
|
formConfig.properties.routeName = {
|
||
|
type: 'string',
|
||
|
label: 'Route Name',
|
||
|
default: routeServiceName,
|
||
|
'control-group-attributes': {
|
||
|
'ng-show': 'entity.createRoute'
|
||
|
}
|
||
|
};
|
||
|
*/
|
||
|
formConfig.properties.routeServiceName = {
|
||
|
type: 'hidden',
|
||
|
default: routeServiceName
|
||
|
}
|
||
|
|
||
|
var namespace = currentKubernetesNamespace();
|
||
|
// TODO store this in localStorage!
|
||
|
var domain = "vagrant.f8";
|
||
|
var defaultRouteHostSuffix = '.' + (namespace === "default" ? "" : namespace + ".") + domain;
|
||
|
formConfig.properties.routeHostname = {
|
||
|
type: 'string',
|
||
|
default: defaultRouteHostSuffix,
|
||
|
label: "Route host name suffix",
|
||
|
'control-group-attributes': {
|
||
|
'ng-show': 'entity.createRoute'
|
||
|
}
|
||
|
};
|
||
|
}
|
||
|
$scope.entity = <any> {};
|
||
|
$scope.formConfig = formConfig;
|
||
|
$scope.objects = template.objects;
|
||
|
log.debug("Form config: ", formConfig);
|
||
|
};
|
||
|
|
||
|
function substitute(str, data) {
|
||
|
return str.replace(/\${\w*}/g, (match) => {
|
||
|
var key = match.replace(/\${/, '').replace(/}/, '').trim();
|
||
|
return data[key] || match;
|
||
|
});
|
||
|
};
|
||
|
|
||
|
$scope.substituteAndDeployTemplate = () => {
|
||
|
var objects = $scope.objects;
|
||
|
var objectsText = angular.toJson(objects, true);
|
||
|
// pull these out of the entity object so they're not used in substitutions
|
||
|
var createRoute = $scope.entity.createRoute;
|
||
|
var routeHostnameSuffix = $scope.entity.routeHostname || "";
|
||
|
var routeName = $scope.entity.routeName;
|
||
|
var routeServiceName = $scope.entity.routeServiceName;
|
||
|
delete $scope.entity.createRoute;
|
||
|
delete $scope.entity.routeHostname;
|
||
|
delete $scope.entity.routeName;
|
||
|
delete $scope.entity.routeServiceName;
|
||
|
objectsText = substitute(objectsText, $scope.entity);
|
||
|
objects = angular.fromJson(objectsText);
|
||
|
if (createRoute) {
|
||
|
var routes = [];
|
||
|
angular.forEach(objects, (object) => {
|
||
|
var kind = object.kind;
|
||
|
var name = getName(object);
|
||
|
if (name && "Service" === kind) {
|
||
|
var routeHostname = name + routeHostnameSuffix;
|
||
|
var route = {
|
||
|
kind: "Route",
|
||
|
apiVersion: defaultOSApiVersion,
|
||
|
metadata: {
|
||
|
name: name,
|
||
|
},
|
||
|
spec: {
|
||
|
host: routeHostname,
|
||
|
to: {
|
||
|
kind: "Service",
|
||
|
name: name
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
routes.push(route);
|
||
|
}
|
||
|
});
|
||
|
objects = objects.concat(routes);
|
||
|
}
|
||
|
if ($scope.targetNamespace !== model.currentNamespace()) {
|
||
|
$scope.$on('WatcherNamespaceChanged', () => {
|
||
|
log.debug("Namespace changed");
|
||
|
setTimeout(() => {
|
||
|
applyObjects(objects);
|
||
|
Core.$apply($scope);
|
||
|
}, 500);
|
||
|
});
|
||
|
Core.notification('info', "Switching to namespace " + $scope.targetNamespace + " and deploying template");
|
||
|
model.kubernetes.selectedNamespace = $scope.targetNamespace;
|
||
|
} else {
|
||
|
applyObjects(objects);
|
||
|
}
|
||
|
};
|
||
|
|
||
|
function applyObjects(objects) {
|
||
|
var projectClient = Kubernetes.createKubernetesClient("projects");
|
||
|
|
||
|
_.forEach(objects, (object:any) => {
|
||
|
log.debug("Object: ", object);
|
||
|
|
||
|
var kind = getKind(object);
|
||
|
var name = getName(object);
|
||
|
var ns = getNamespace(object);
|
||
|
|
||
|
if (kind && name) {
|
||
|
if (ns && ns !== currentKubernetesNamespace()) {
|
||
|
var project = {
|
||
|
apiVersion: Kubernetes.defaultApiVersion,
|
||
|
kind: "Project",
|
||
|
metadata: {
|
||
|
name: ns,
|
||
|
labels: {
|
||
|
}
|
||
|
}
|
||
|
};
|
||
|
projectClient.put(project,
|
||
|
(data) => {
|
||
|
log.info("Created namespace: " + ns)
|
||
|
},
|
||
|
(err) => {
|
||
|
log.warn("Failed to create namespace: " + ns + ": " + angular.toJson(err));
|
||
|
});
|
||
|
}
|
||
|
|
||
|
var pluralKind = kind.toLowerCase() + "s";
|
||
|
var kubeClient = Kubernetes.createKubernetesClient(pluralKind, ns);
|
||
|
kubeClient.put(object,
|
||
|
(data) => {
|
||
|
log.info("updated " + kind + " name: " + name + (ns ? " ns: " + ns: ""));
|
||
|
},
|
||
|
(err) => {
|
||
|
log.warn("Failed to update " + kind + " name: " + name + (ns ? " ns: " + ns: "") + " error: " + angular.toJson(err));
|
||
|
});
|
||
|
}
|
||
|
});
|
||
|
goBack();
|
||
|
}
|
||
|
|
||
|
$scope.deleteTemplate = (template) => {
|
||
|
UI.multiItemConfirmActionDialog(<UI.MultiItemConfirmActionOptions>{
|
||
|
collection: [template],
|
||
|
index: 'metadata.name',
|
||
|
onClose: (result:boolean) => {
|
||
|
if (result) {
|
||
|
KubernetesModel['templatesResource'].delete({
|
||
|
id: template.metadata.name
|
||
|
}, undefined, () => {
|
||
|
KubernetesModel['templatesResource'].query((data) => {
|
||
|
KubernetesModel.templates = data.items;
|
||
|
});
|
||
|
}, (error) => {
|
||
|
log.debug("Error deleting template: ", error);
|
||
|
});
|
||
|
}
|
||
|
},
|
||
|
title: 'Delete Template?',
|
||
|
action: 'The following template will be deleted:',
|
||
|
okText: 'Delete',
|
||
|
okClass: 'btn-danger',
|
||
|
custom: "This operation is permanent once completed!",
|
||
|
customClass: "alert alert-warning"
|
||
|
}).open();
|
||
|
};
|
||
|
}]);
|
||
|
}
|
||
|
|