mirror of https://github.com/portainer/portainer
271 lines
8.4 KiB
JavaScript
271 lines
8.4 KiB
JavaScript
angular.module('portainer.app')
|
|
.controller('StackController', ['$q', '$scope', '$state', '$transition$', 'StackService', 'NodeService', 'ServiceService', 'TaskService', 'ContainerService', 'ServiceHelper', 'TaskHelper', 'Notifications', 'FormHelper', 'EndpointProvider', 'EndpointService', 'GroupService', 'ModalService',
|
|
function ($q, $scope, $state, $transition$, StackService, NodeService, ServiceService, TaskService, ContainerService, ServiceHelper, TaskHelper, Notifications, FormHelper, EndpointProvider, EndpointService, GroupService, ModalService) {
|
|
|
|
$scope.state = {
|
|
actionInProgress: false,
|
|
migrationInProgress: false,
|
|
externalStack: false,
|
|
showEditorTab: false
|
|
};
|
|
|
|
$scope.formValues = {
|
|
Prune: false,
|
|
Endpoint: null
|
|
};
|
|
|
|
$scope.showEditor = function() {
|
|
$scope.state.showEditorTab = true;
|
|
};
|
|
|
|
$scope.migrateStack = function() {
|
|
ModalService.confirm({
|
|
title: 'Are you sure?',
|
|
message: 'This action will deploy a new instance of this stack on the target endpoint, please note that this does NOT relocate the content of any persistent volumes that may be attached to this stack.',
|
|
buttons: {
|
|
confirm: {
|
|
label: 'Migrate',
|
|
className: 'btn-danger'
|
|
}
|
|
},
|
|
callback: function onConfirm(confirmed) {
|
|
if(!confirmed) { return; }
|
|
migrateStack();
|
|
}
|
|
});
|
|
};
|
|
|
|
$scope.removeStack = function() {
|
|
ModalService.confirmDeletion(
|
|
'Do you want to remove the stack? Associated services will be removed as well.',
|
|
function onConfirm(confirmed) {
|
|
if(!confirmed) { return; }
|
|
deleteStack();
|
|
}
|
|
);
|
|
};
|
|
|
|
function migrateStack() {
|
|
var stack = $scope.stack;
|
|
var targetEndpointId = $scope.formValues.Endpoint.Id;
|
|
|
|
var migrateRequest = StackService.migrateSwarmStack;
|
|
if (stack.Type === 2) {
|
|
migrateRequest = StackService.migrateComposeStack;
|
|
}
|
|
|
|
// TODO: this is a work-around for stacks created with Portainer version >= 1.17.1
|
|
// The EndpointID property is not available for these stacks, we can pass
|
|
// the current endpoint identifier as a part of the migrate request. It will be used if
|
|
// the EndpointID property is not defined on the stack.
|
|
var endpointId = EndpointProvider.endpointID();
|
|
if (stack.EndpointId === 0) {
|
|
stack.EndpointId = endpointId;
|
|
}
|
|
|
|
$scope.state.migrationInProgress = true;
|
|
migrateRequest(stack, targetEndpointId)
|
|
.then(function success() {
|
|
Notifications.success('Stack successfully migrated', stack.Name);
|
|
$state.go('portainer.stacks', {}, {reload: true});
|
|
})
|
|
.catch(function error(err) {
|
|
Notifications.error('Failure', err, 'Unable to migrate stack');
|
|
})
|
|
.finally(function final() {
|
|
$scope.state.migrationInProgress = false;
|
|
});
|
|
}
|
|
|
|
function deleteStack() {
|
|
var endpointId = EndpointProvider.endpointID();
|
|
var stack = $scope.stack;
|
|
|
|
StackService.remove(stack, $transition$.params().external, endpointId)
|
|
.then(function success() {
|
|
Notifications.success('Stack successfully removed', stack.Name);
|
|
$state.go('portainer.stacks');
|
|
})
|
|
.catch(function error(err) {
|
|
Notifications.error('Failure', err, 'Unable to remove stack ' + stack.Name);
|
|
});
|
|
}
|
|
|
|
$scope.deployStack = function () {
|
|
var stackFile = $scope.stackFileContent;
|
|
var env = FormHelper.removeInvalidEnvVars($scope.stack.Env);
|
|
var prune = $scope.formValues.Prune;
|
|
var stack = $scope.stack;
|
|
|
|
// TODO: this is a work-around for stacks created with Portainer version >= 1.17.1
|
|
// The EndpointID property is not available for these stacks, we can pass
|
|
// the current endpoint identifier as a part of the update request. It will be used if
|
|
// the EndpointID property is not defined on the stack.
|
|
var endpointId = EndpointProvider.endpointID();
|
|
if (stack.EndpointId === 0) {
|
|
stack.EndpointId = endpointId;
|
|
}
|
|
|
|
$scope.state.actionInProgress = true;
|
|
StackService.updateStack(stack, stackFile, env, prune)
|
|
.then(function success() {
|
|
Notifications.success('Stack successfully deployed');
|
|
$state.reload();
|
|
})
|
|
.catch(function error(err) {
|
|
Notifications.error('Failure', err, 'Unable to create stack');
|
|
})
|
|
.finally(function final() {
|
|
$scope.state.actionInProgress = false;
|
|
});
|
|
};
|
|
|
|
$scope.addEnvironmentVariable = function() {
|
|
$scope.stack.Env.push({ name: '', value: ''});
|
|
};
|
|
|
|
$scope.removeEnvironmentVariable = function(index) {
|
|
$scope.stack.Env.splice(index, 1);
|
|
};
|
|
|
|
$scope.editorUpdate = function(cm) {
|
|
$scope.stackFileContent = cm.getValue();
|
|
};
|
|
|
|
function loadStack(id) {
|
|
var agentProxy = $scope.applicationState.endpoint.mode.agentProxy;
|
|
var endpointId = EndpointProvider.endpointID();
|
|
|
|
$q.all({
|
|
stack: StackService.stack(id),
|
|
endpoints: EndpointService.endpoints(),
|
|
groups: GroupService.groups()
|
|
})
|
|
.then(function success(data) {
|
|
var stack = data.stack;
|
|
$scope.endpoints = data.endpoints.filter(function(endpoint) {
|
|
return endpoint.Id !== endpointId;
|
|
});
|
|
$scope.groups = data.groups;
|
|
$scope.stack = stack;
|
|
|
|
return $q.all({
|
|
stackFile: StackService.getStackFile(id),
|
|
resources: stack.Type === 1 ? retrieveSwarmStackResources(stack.Name, agentProxy) : retrieveComposeStackResources(stack.Name)
|
|
});
|
|
})
|
|
.then(function success(data) {
|
|
$scope.stackFileContent = data.stackFile;
|
|
if ($scope.stack.Type === 1) {
|
|
assignSwarmStackResources(data.resources, agentProxy);
|
|
} else {
|
|
assignComposeStackResources(data.resources);
|
|
}
|
|
})
|
|
.catch(function error(err) {
|
|
Notifications.error('Failure', err, 'Unable to retrieve stack details');
|
|
});
|
|
}
|
|
|
|
function retrieveSwarmStackResources(stackName, agentProxy) {
|
|
var stackFilter = {
|
|
label: ['com.docker.stack.namespace=' + stackName]
|
|
};
|
|
|
|
return $q.all({
|
|
services: ServiceService.services(stackFilter),
|
|
tasks: TaskService.tasks(stackFilter),
|
|
containers: agentProxy ? ContainerService.containers(1) : [],
|
|
nodes: NodeService.nodes()
|
|
});
|
|
}
|
|
|
|
function assignSwarmStackResources(resources, agentProxy) {
|
|
var services = resources.services;
|
|
var tasks = resources.tasks;
|
|
|
|
if (agentProxy) {
|
|
var containers = resources.containers;
|
|
for (var j = 0; j < tasks.length; j++) {
|
|
var task = tasks[j];
|
|
TaskHelper.associateContainerToTask(task, containers);
|
|
}
|
|
}
|
|
|
|
for (var i = 0; i < services.length; i++) {
|
|
var service = services[i];
|
|
ServiceHelper.associateTasksToService(service, tasks);
|
|
}
|
|
|
|
$scope.nodes = resources.nodes;
|
|
$scope.tasks = tasks;
|
|
$scope.services = services;
|
|
}
|
|
|
|
function retrieveComposeStackResources(stackName) {
|
|
var stackFilter = {
|
|
label: ['com.docker.compose.project=' + stackName]
|
|
};
|
|
|
|
return $q.all({
|
|
containers: ContainerService.containers(1, stackFilter)
|
|
});
|
|
}
|
|
|
|
function assignComposeStackResources(resources) {
|
|
$scope.containers = resources.containers;
|
|
}
|
|
|
|
function loadExternalStack(name) {
|
|
var stackType = $transition$.params().type;
|
|
if (!stackType || (stackType !== '1' && stackType !== '2')) {
|
|
Notifications.error('Failure', err, 'Invalid type URL parameter.');
|
|
return;
|
|
}
|
|
|
|
if (stackType === '1') {
|
|
loadExternalSwarmStack(name);
|
|
} else {
|
|
loadExternalComposeStack(name);
|
|
}
|
|
}
|
|
|
|
function loadExternalSwarmStack(name) {
|
|
var agentProxy = $scope.applicationState.endpoint.mode.agentProxy;
|
|
|
|
retrieveSwarmStackResources(name, agentProxy)
|
|
.then(function success(data) {
|
|
assignSwarmStackResources(data, agentProxy);
|
|
})
|
|
.catch(function error(err) {
|
|
Notifications.error('Failure', err, 'Unable to retrieve stack details');
|
|
});
|
|
}
|
|
|
|
function loadExternalComposeStack(name) {
|
|
retrieveComposeStackResources(name)
|
|
.then(function success(data) {
|
|
assignComposeStackResources(data);
|
|
})
|
|
.catch(function error(err) {
|
|
Notifications.error('Failure', err, 'Unable to retrieve stack details');
|
|
});
|
|
}
|
|
|
|
function initView() {
|
|
var stackName = $transition$.params().name;
|
|
$scope.stackName = stackName;
|
|
var external = $transition$.params().external;
|
|
|
|
if (external === 'true') {
|
|
$scope.state.externalStack = true;
|
|
loadExternalStack(stackName);
|
|
} else {
|
|
var stackId = $transition$.params().id;
|
|
loadStack(stackId);
|
|
}
|
|
}
|
|
|
|
initView();
|
|
}]);
|