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

feat(agent): add agent support (#1828)

This commit is contained in:
Anthony Lapenna 2018-05-06 09:15:57 +02:00 committed by GitHub
parent 77a85bd385
commit 2327d696e0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
116 changed files with 1900 additions and 689 deletions

View file

@ -57,7 +57,7 @@ angular.module('portainer.docker', ['portainer.app'])
var container = {
name: 'docker.containers.container',
url: '/:id',
url: '/:id?nodeName',
views: {
'content@': {
templateUrl: 'app/docker/views/containers/edit/container.html',
@ -79,15 +79,12 @@ angular.module('portainer.docker', ['portainer.app'])
var containerCreation = {
name: 'docker.containers.new',
url: '/new',
url: '/new?nodeName&from',
views: {
'content@': {
templateUrl: 'app/docker/views/containers/create/createcontainer.html',
controller: 'CreateContainerController'
}
},
params: {
from: ''
}
};
@ -170,7 +167,7 @@ angular.module('portainer.docker', ['portainer.app'])
var image = {
name: 'docker.images.image',
url: '/:id',
url: '/:id?nodeName',
views: {
'content@': {
templateUrl: 'app/docker/views/images/edit/image.html',
@ -203,7 +200,7 @@ angular.module('portainer.docker', ['portainer.app'])
var network = {
name: 'docker.networks.network',
url: '/:id',
url: '/:id?nodeName',
views: {
'content@': {
templateUrl: 'app/docker/views/networks/edit/network.html',
@ -443,7 +440,7 @@ angular.module('portainer.docker', ['portainer.app'])
var volume = {
name: 'docker.volumes.volume',
url: '/:id',
url: '/:id?nodeName',
views: {
'content@': {
templateUrl: 'app/docker/views/volumes/edit/volume.html',

View file

@ -0,0 +1,4 @@
angular.module('portainer.docker').component('dashboardClusterAgentInfo', {
templateUrl: 'app/docker/components/dashboard-cluster-agent-info/dashboardClusterAgentInfo.html',
controller: 'DashboardClusterAgentInfoController'
});

View file

@ -0,0 +1,20 @@
<rd-widget>
<rd-widget-header icon="fa-tachometer-alt" title="Cluster information"></rd-widget-header>
<rd-widget-body classes="no-padding">
<table class="table">
<tbody>
<tr>
<td>Nodes in the cluster</td>
<td>{{ $ctrl.agentCount }}</td>
</tr>
<tr>
<td colspan="2">
<div class="btn-group" role="group" aria-label="...">
<a ui-sref="docker.swarm.visualizer"><i class="fa fa-object-group space-right" aria-hidden="true"></i>Go to cluster visualizer</a>
</div>
</td>
</tr>
</tbody>
</table>
</rd-widget-body>
</rd-widget>

View file

@ -0,0 +1,16 @@
angular.module('portainer.docker')
.controller('DashboardClusterAgentInfoController', ['AgentService', 'Notifications',
function (AgentService, Notifications) {
var ctrl = this;
this.$onInit = function() {
AgentService.agents()
.then(function success(data) {
ctrl.agentCount = data.length;
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to retrieve agent information');
});
};
}]);

View file

@ -38,7 +38,7 @@
</thead>
<tbody>
<tr dir-paginate="(key, value) in $ctrl.dataset | itemsPerPage: $ctrl.state.paginatedItemLimit" ng-class="{active: item.Checked}">
<td><a ui-sref="docker.networks.network({id: value.NetworkID})">{{ key }}</a></td>
<td><a ui-sref="docker.networks.network({ id: value.NetworkID, nodeName: $ctrl.nodeName })">{{ key }}</a></td>
<td>{{ value.IPAddress || '-' }}</td>
<td>{{ value.Gateway || '-' }}</td>
<td>{{ value.MacAddress || '-' }}</td>

View file

@ -11,6 +11,7 @@ angular.module('portainer.docker').component('containerNetworksDatatable', {
joinNetworkAction: '<',
joinNetworkActionInProgress: '<',
leaveNetworkActionInProgress: '<',
leaveNetworkAction: '<'
leaveNetworkAction: '<',
nodeName: '<'
}
});

View file

@ -156,6 +156,13 @@
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'IP' && $ctrl.state.reverseOrder"></i>
</a>
</th>
<th ng-if="$ctrl.showHostColumn">
<a ng-click="$ctrl.changeOrderBy('NodeName')">
Host
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'NodeName' && !$ctrl.state.reverseOrder"></i>
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'NodeName' && $ctrl.state.reverseOrder"></i>
</a>
</th>
<th>
<a ng-click="$ctrl.changeOrderBy('Ports')">
Published Ports
@ -179,7 +186,7 @@
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-change="$ctrl.selectItem(item)"/>
<label for="select_{{ $index }}"></label>
</span>
<a ui-sref="docker.containers.container({ id: item.Id })">{{ item | containername | truncate: $ctrl.settings.containerNameTruncateSize }}</a>
<a ui-sref="docker.containers.container({ id: item.Id, nodeName: item.NodeName })">{{ item | containername | truncate: $ctrl.settings.containerNameTruncateSize }}</a>
</td>
<td>
<span ng-if="['starting','healthy','unhealthy'].indexOf(item.Status) !== -1" class="label label-{{ item.Status|containerstatusbadge }} interactive" uib-tooltip="This container has a health check">{{ item.Status }}</span>
@ -187,15 +194,16 @@
</td>
<td ng-if="$ctrl.settings.showQuickActionStats || $ctrl.settings.showQuickActionLogs || $ctrl.settings.showQuickActionConsole || $ctrl.settings.showQuickActionInspect">
<div class="btn-group btn-group-xs" role="group" aria-label="..." style="display:inline-flex;">
<a ng-if="$ctrl.settings.showQuickActionStats" style="margin: 0 2.5px;" ui-sref="docker.containers.container.stats({id: item.Id})" title="Stats"><i class="fa fa-chart-area space-right" aria-hidden="true"></i></a>
<a ng-if="$ctrl.settings.showQuickActionLogs" style="margin: 0 2.5px;" ui-sref="docker.containers.container.logs({id: item.Id})" title="Logs"><i class="fa fa-file-alt space-right" aria-hidden="true"></i></a>
<a ng-if="$ctrl.settings.showQuickActionConsole" style="margin: 0 2.5px;" ui-sref="docker.containers.container.console({id: item.Id})" title="Console"><i class="fa fa-terminal space-right" aria-hidden="true"></i></a>
<a ng-if="$ctrl.settings.showQuickActionInspect" style="margin: 0 2.5px;" ui-sref="docker.containers.container.inspect({id: item.Id})" title="Inspect"><i class="fa fa-info-circle space-right" aria-hidden="true"></i></a>
<a ng-if="$ctrl.settings.showQuickActionStats" style="margin: 0 2.5px;" ui-sref="docker.containers.container.stats({id: item.Id, nodeName: item.NodeName})" title="Stats"><i class="fa fa-chart-area space-right" aria-hidden="true"></i></a>
<a ng-if="$ctrl.settings.showQuickActionLogs" style="margin: 0 2.5px;" ui-sref="docker.containers.container.logs({id: item.Id, nodeName: item.NodeName})" title="Logs"><i class="fa fa-file-alt space-right" aria-hidden="true"></i></a>
<a ng-if="$ctrl.settings.showQuickActionConsole" style="margin: 0 2.5px;" ui-sref="docker.containers.container.console({id: item.Id, nodeName: item.NodeName})" title="Console"><i class="fa fa-terminal space-right" aria-hidden="true"></i></a>
<a ng-if="$ctrl.settings.showQuickActionInspect" style="margin: 0 2.5px;" ui-sref="docker.containers.container.inspect({id: item.Id, nodeName: item.NodeName})" title="Inspect"><i class="fa fa-info-circle space-right" aria-hidden="true"></i></a>
</div>
</td>
<td>{{ item.StackName ? item.StackName : '-' }}</td>
<td><a ui-sref="docker.images.image({ id: item.Image })">{{ item.Image | trimshasum }}</a></td>
<td>{{ item.IP ? item.IP : '-' }}</td>
<td ng-if="$ctrl.showHostColumn">{{ item.NodeName ? item.NodeName : '-' }}</td>
<td>
<a ng-if="item.Ports.length > 0" ng-repeat="p in item.Ports" class="image-tag" ng-href="http://{{ $ctrl.publicUrl || p.host }}:{{p.public}}" target="_blank">
<i class="fa fa-external-link-alt" aria-hidden="true"></i> {{ p.public }}:{{ p.private }}
@ -210,10 +218,10 @@
</td>
</tr>
<tr ng-if="!$ctrl.dataset">
<td colspan="8" class="text-center text-muted">Loading...</td>
<td colspan="9" class="text-center text-muted">Loading...</td>
</tr>
<tr ng-if="$ctrl.state.filteredDataSet.length === 0">
<td colspan="8" class="text-center text-muted">No container available.</td>
<td colspan="9" class="text-center text-muted">No container available.</td>
</tr>
</tbody>
</table>

View file

@ -10,6 +10,7 @@ angular.module('portainer.docker').component('containersDatatable', {
reverseOrder: '<',
showTextFilter: '<',
showOwnershipColumn: '<',
showHostColumn: '<',
publicUrl: '<',
containerNameTruncateSize: '<',
startAction: '<',

View file

@ -93,6 +93,13 @@
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Created' && $ctrl.state.reverseOrder"></i>
</a>
</th>
<th ng-if="$ctrl.showHostColumn">
<a ng-click="$ctrl.changeOrderBy('NodeName')">
Host
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'NodeName' && !$ctrl.state.reverseOrder"></i>
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'NodeName' && $ctrl.state.reverseOrder"></i>
</a>
</th>
</tr>
</thead>
<tbody>
@ -102,7 +109,7 @@
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-change="$ctrl.selectItem(item)"/>
<label for="select_{{ $index }}"></label>
</span>
<a ui-sref="docker.images.image({id: item.Id})" class="monospaced">{{ item.Id | truncate:20 }}</a>
<a ui-sref="docker.images.image({ id: item.Id, nodeName: item.NodeName })" class="monospaced">{{ item.Id | truncate:20 }}</a>
<span style="margin-left: 10px;" class="label label-warning image-tag" ng-if="::item.ContainerCount === 0">Unused</span>
</td>
<td>
@ -110,12 +117,13 @@
</td>
<td>{{ item.VirtualSize | humansize }}</td>
<td>{{ item.Created | getisodatefromtimestamp }}</td>
<td ng-if="$ctrl.showHostColumn">{{ item.NodeName ? item.NodeName : '-' }}</td>
</tr>
<tr ng-if="!$ctrl.dataset">
<td colspan="4" class="text-center text-muted">Loading...</td>
<td colspan="5" class="text-center text-muted">Loading...</td>
</tr>
<tr ng-if="$ctrl.state.filteredDataSet.length === 0">
<td colspan="4" class="text-center text-muted">No image available.</td>
<td colspan="5" class="text-center text-muted">No image available.</td>
</tr>
</tbody>
</table>

View file

@ -9,6 +9,7 @@ angular.module('portainer.docker').component('imagesDatatable', {
orderBy: '@',
reverseOrder: '<',
showTextFilter: '<',
showHostColumn: '<',
removeAction: '<',
forceRemoveAction: '<'
}

View file

@ -81,6 +81,13 @@
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'IPAM.Config[0].Gateway' && $ctrl.state.reverseOrder"></i>
</a>
</th>
<th ng-if="$ctrl.showHostColumn">
<a ng-click="$ctrl.changeOrderBy('NodeName')">
Host
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'NodeName' && !$ctrl.state.reverseOrder"></i>
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'NodeName' && $ctrl.state.reverseOrder"></i>
</a>
</th>
<th ng-if="$ctrl.showOwnershipColumn">
<a ng-click="$ctrl.changeOrderBy('ResourceControl.Ownership')">
Ownership
@ -97,7 +104,7 @@
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-change="$ctrl.selectItem(item)"/>
<label for="select_{{ $index }}"></label>
</span>
<a ui-sref="docker.networks.network({id: item.Id})" title="{{ item.Name }}">{{ item.Name | truncate:40 }}</a>
<a ui-sref="docker.networks.network({ id: item.Id, nodeName: item.NodeName })" title="{{ item.Name }}">{{ item.Name | truncate:40 }}</a>
</td>
<td>{{ item.StackName ? item.StackName : '-' }}</td>
<td>{{ item.Scope }}</td>
@ -105,6 +112,7 @@
<td>{{ item.IPAM.Driver }}</td>
<td>{{ item.IPAM.Config[0].Subnet ? item.IPAM.Config[0].Subnet : '-' }}</td>
<td>{{ item.IPAM.Config[0].Gateway ? item.IPAM.Config[0].Gateway : '-' }}</td>
<td ng-if="$ctrl.showHostColumn">{{ item.NodeName ? item.NodeName : '-' }}</td>
<td ng-if="$ctrl.showOwnershipColumn">
<span>
<i ng-class="item.ResourceControl.Ownership | ownershipicon" aria-hidden="true"></i>
@ -113,10 +121,10 @@
</td>
</tr>
<tr ng-if="!$ctrl.dataset">
<td colspan="8" class="text-center text-muted">Loading...</td>
<td colspan="9" class="text-center text-muted">Loading...</td>
</tr>
<tr ng-if="$ctrl.state.filteredDataSet.length === 0">
<td colspan="8" class="text-center text-muted">No network available.</td>
<td colspan="9" class="text-center text-muted">No network available.</td>
</tr>
</tbody>
</table>

View file

@ -10,6 +10,7 @@ angular.module('portainer.docker').component('networksDatatable', {
reverseOrder: '<',
showTextFilter: '<',
showOwnershipColumn: '<',
showHostColumn: '<',
removeAction: '<'
}
});

View file

@ -54,20 +54,26 @@
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Updated' && $ctrl.state.reverseOrder"></i>
</a>
</th>
<th ng-if="$ctrl.showLogsButton">Actions</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr dir-paginate="item in ($ctrl.state.filteredDataSet = ($ctrl.dataset | filter:$ctrl.state.textFilter | orderBy:$ctrl.state.orderBy:$ctrl.state.reverseOrder | itemsPerPage: $ctrl.state.paginatedItemLimit))" ng-class="{active: item.Checked}">
<td><a ui-sref="docker.tasks.task({id: item.Id})" class="monospaced">{{ item.Id }}</a></td>
<td>
<a ng-if="!$ctrl.agentProxy || !item.Container" ui-sref="docker.tasks.task({id: item.Id})" class="monospaced">{{ item.Id }}</a>
<a ng-if="$ctrl.agentProxy && item.Container" ui-sref="docker.containers.container({ id: item.Container.Id, nodeName: item.Container.NodeName })" class="monospaced">{{ item.Id }}</a>
</td>
<td><span class="label label-{{ item.Status.State | taskstatusbadge }}">{{ item.Status.State }}</span></td>
<td ng-if="$ctrl.showSlotColumn">{{ item.Slot ? item.Slot : '-' }}</td>
<td>{{ item.NodeId | tasknodename: $ctrl.nodes }}</td>
<td>{{ item.Updated | getisodate }}</td>
<td ng-if="$ctrl.showLogsButton">
<a ui-sref="docker.tasks.task.logs({id: item.Id})">
<td>
<a ui-sref="docker.tasks.task.logs({id: item.Id})" ng-if="$ctrl.showLogsButton" class="space-right">
<i class="fa fa-file-alt" aria-hidden="true"></i> View logs
</a>
<a ui-sref="docker.containers.container.console({ id: item.Container.Id, nodeName: item.Container.NodeName })" ng-if="$ctrl.agentProxy">
<i class="fa fa-terminal" aria-hidden="true"></i> Console
</a>
</td>
</tr>
<tr ng-if="!$ctrl.dataset">

View file

@ -11,6 +11,7 @@ angular.module('portainer.docker').component('tasksDatatable', {
nodes: '<',
showTextFilter: '<',
showSlotColumn: '<',
showLogsButton: '<'
showLogsButton: '<',
agentProxy: '<'
}
});

View file

@ -84,6 +84,13 @@
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'Mountpoint' && $ctrl.state.reverseOrder"></i>
</a>
</th>
<th ng-if="$ctrl.showHostColumn">
<a ng-click="$ctrl.changeOrderBy('NodeName')">
Host
<i class="fa fa-sort-alpha-down" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'NodeName' && !$ctrl.state.reverseOrder"></i>
<i class="fa fa-sort-alpha-up" aria-hidden="true" ng-if="$ctrl.state.orderBy === 'NodeName' && $ctrl.state.reverseOrder"></i>
</a>
</th>
<th ng-if="$ctrl.showOwnershipColumn">
<a ng-click="$ctrl.changeOrderBy('ResourceControl.Ownership')">
Ownership
@ -100,12 +107,13 @@
<input id="select_{{ $index }}" type="checkbox" ng-model="item.Checked" ng-change="$ctrl.selectItem(item)"/>
<label for="select_{{ $index }}"></label>
</span>
<a ui-sref="docker.volumes.volume({id: item.Id})" class="monospaced">{{ item.Id | truncate:25 }}</a>
<a ui-sref="docker.volumes.volume({ id: item.Id, nodeName: item.NodeName })" class="monospaced">{{ item.Id | truncate:25 }}</a>
<span style="margin-left: 10px;" class="label label-warning image-tag" ng-if="item.dangling">Unused</span>
</td>
<td>{{ item.StackName ? item.StackName : '-' }}</td>
<td>{{ item.Driver }}</td>
<td>{{ item.Mountpoint | truncatelr }}</td>
<td ng-if="$ctrl.showHostColumn">{{ item.NodeName ? item.NodeName : '-' }}</td>
<td ng-if="$ctrl.showOwnershipColumn">
<span>
<i ng-class="item.ResourceControl.Ownership | ownershipicon" aria-hidden="true"></i>
@ -114,10 +122,10 @@
</td>
</tr>
<tr ng-if="!$ctrl.dataset">
<td colspan="5" class="text-center text-muted">Loading...</td>
<td colspan="6" class="text-center text-muted">Loading...</td>
</tr>
<tr ng-if="$ctrl.state.filteredDataSet.length === 0">
<td colspan="5" class="text-center text-muted">No volume available.</td>
<td colspan="6" class="text-center text-muted">No volume available.</td>
</tr>
</tbody>
</table>

View file

@ -10,6 +10,7 @@ angular.module('portainer.docker').component('volumesDatatable', {
reverseOrder: '<',
showTextFilter: '<',
showOwnershipColumn: '<',
showHostColumn: '<',
removeAction: '<'
}
});

View file

@ -4,12 +4,20 @@ angular.module('portainer.docker')
var helper = {};
helper.determineEndpointMode = function(info) {
helper.determineEndpointMode = function(info, type) {
var mode = {
provider: '',
role: ''
role: '',
agentProxy: false
};
if (type === 2) {
mode.provider = 'DOCKER_SWARM_MODE';
mode.role = 'MANAGER';
mode.agentProxy = true;
return mode;
}
if (!info.Swarm || _.isEmpty(info.Swarm.NodeID)) {
if (info.ID === 'vSphere Integrated Containers') {
mode.provider = 'VMWARE_VIC';

View file

@ -0,0 +1,18 @@
angular.module('portainer.docker')
.factory('TaskHelper', [function TaskHelperFactory() {
'use strict';
var helper = {};
helper.associateContainerToTask = function(task, containers) {
for (var i = 0; i < containers.length; i++) {
var container = containers[i];
if (task.ContainerId === container.Id) {
task.Container = container;
break;
}
}
};
return helper;
}]);

View file

@ -34,6 +34,9 @@ function ContainerViewModel(data) {
if (data.Portainer.ResourceControl) {
this.ResourceControl = new ResourceControlViewModel(data.Portainer.ResourceControl);
}
if (data.Portainer.Agent && data.Portainer.Agent.NodeName) {
this.NodeName = data.Portainer.Agent.NodeName;
}
}
}

View file

@ -7,6 +7,10 @@ function ImageViewModel(data) {
this.RepoTags = data.RepoTags;
this.VirtualSize = data.VirtualSize;
this.ContainerCount = data.ContainerCount;
if (data.Portainer && data.Portainer.Agent && data.Portainer.Agent.NodeName) {
this.NodeName = data.Portainer.Agent.NodeName;
}
}
function ImageBuildModel(data) {

View file

@ -19,5 +19,8 @@ function NetworkViewModel(data) {
if (data.Portainer.ResourceControl) {
this.ResourceControl = new ResourceControlViewModel(data.Portainer.ResourceControl);
}
if (data.Portainer.Agent && data.Portainer.Agent.NodeName) {
this.NodeName = data.Portainer.Agent.NodeName;
}
}
}

View file

@ -7,4 +7,7 @@ function TaskViewModel(data) {
this.Status = data.Status;
this.ServiceId = data.ServiceID;
this.NodeId = data.NodeID;
if (data.Status && data.Status.ContainerStatus && data.Status.ContainerStatus.ContainerID) {
this.ContainerId = data.Status.ContainerStatus.ContainerID;
}
}

View file

@ -14,5 +14,8 @@ function VolumeViewModel(data) {
if (data.Portainer.ResourceControl) {
this.ResourceControl = new ResourceControlViewModel(data.Portainer.ResourceControl);
}
if (data.Portainer.Agent && data.Portainer.Agent.NodeName) {
this.NodeName = data.Portainer.Agent.NodeName;
}
}
}

View file

@ -1,18 +1,34 @@
angular.module('portainer.docker')
.factory('Container', ['$resource', 'API_ENDPOINT_ENDPOINTS', 'EndpointProvider', function ContainerFactory($resource, API_ENDPOINT_ENDPOINTS, EndpointProvider) {
.factory('Container', ['$resource', 'API_ENDPOINT_ENDPOINTS', 'EndpointProvider',
function ContainerFactory($resource, API_ENDPOINT_ENDPOINTS, EndpointProvider) {
'use strict';
return $resource(API_ENDPOINT_ENDPOINTS + '/:endpointId/docker/containers/:id/:action', {
name: '@name',
endpointId: EndpointProvider.endpointID
},
{
query: {method: 'GET', params: {all: 0, action: 'json', filters: '@filters' }, isArray: true},
get: {method: 'GET', params: {action: 'json'}},
stop: {method: 'POST', params: {id: '@id', t: 5, action: 'stop'}},
restart: {method: 'POST', params: {id: '@id', t: 5, action: 'restart'}},
kill: {method: 'POST', params: {id: '@id', action: 'kill'}},
pause: {method: 'POST', params: {id: '@id', action: 'pause'}},
unpause: {method: 'POST', params: {id: '@id', action: 'unpause'}},
query: {
method: 'GET', params: { all: 0, action: 'json', filters: '@filters' },
isArray: true
},
get: {
method: 'GET', params: { action: 'json' }
},
stop: {
method: 'POST', params: { id: '@id', t: 5, action: 'stop' }
},
restart: {
method: 'POST', params: { id: '@id', t: 5, action: 'restart' }
},
kill: {
method: 'POST', params: { id: '@id', action: 'kill' }
},
pause: {
method: 'POST', params: { id: '@id', action: 'pause' }
},
unpause: {
method: 'POST', params: { id: '@id', action: 'unpause' }
},
logs: {
method: 'GET', params: { id: '@id', action: 'logs' },
timeout: 4500, ignoreLoadingBar: true,
@ -40,7 +56,7 @@ angular.module('portainer.docker')
transformResponse: genericHandler
},
rename: {
method: 'POST', params: {id: '@id', action: 'rename', name: '@name'},
method: 'POST', params: { id: '@id', action: 'rename', name: '@name' },
transformResponse: genericHandler
},
exec: {

View file

@ -1,5 +1,6 @@
angular.module('portainer.docker')
.factory('Exec', ['$resource', 'API_ENDPOINT_ENDPOINTS', 'EndpointProvider', function ExecFactory($resource, API_ENDPOINT_ENDPOINTS, EndpointProvider) {
.factory('Exec', ['$resource', 'API_ENDPOINT_ENDPOINTS', 'EndpointProvider',
function ExecFactory($resource, API_ENDPOINT_ENDPOINTS, EndpointProvider) {
'use strict';
return $resource(API_ENDPOINT_ENDPOINTS + '/:endpointId/docker/exec/:id/:action', {
endpointId: EndpointProvider.endpointID

View file

@ -1,5 +1,6 @@
angular.module('portainer.docker')
.factory('Image', ['$resource', 'API_ENDPOINT_ENDPOINTS', 'EndpointProvider', 'HttpRequestHelper', function ImageFactory($resource, API_ENDPOINT_ENDPOINTS, EndpointProvider, HttpRequestHelper) {
.factory('Image', ['$resource', 'API_ENDPOINT_ENDPOINTS', 'EndpointProvider', 'HttpRequestHelper',
function ImageFactory($resource, API_ENDPOINT_ENDPOINTS, EndpointProvider, HttpRequestHelper) {
'use strict';
return $resource(API_ENDPOINT_ENDPOINTS + '/:endpointId/docker/images/:id/:action', {

View file

@ -1,16 +1,30 @@
angular.module('portainer.docker')
.factory('Network', ['$resource', 'API_ENDPOINT_ENDPOINTS', 'EndpointProvider', function NetworkFactory($resource, API_ENDPOINT_ENDPOINTS, EndpointProvider) {
.factory('Network', ['$resource', 'API_ENDPOINT_ENDPOINTS', 'EndpointProvider',
function NetworkFactory($resource, API_ENDPOINT_ENDPOINTS, EndpointProvider) {
'use strict';
return $resource(API_ENDPOINT_ENDPOINTS + '/:endpointId/docker/networks/:id/:action', {
id: '@id',
endpointId: EndpointProvider.endpointID
},
{
query: {method: 'GET', isArray: true},
get: {method: 'GET'},
create: {method: 'POST', params: {action: 'create'}, transformResponse: genericHandler, ignoreLoadingBar: true},
remove: { method: 'DELETE', transformResponse: genericHandler },
connect: {method: 'POST', params: {action: 'connect'}},
disconnect: {method: 'POST', params: {action: 'disconnect'}}
query: {
method: 'GET', isArray: true
},
get: {
method: 'GET'
},
create: {
method: 'POST', params: {action: 'create'},
transformResponse: genericHandler, ignoreLoadingBar: true
},
remove: {
method: 'DELETE', transformResponse: genericHandler
},
connect: {
method: 'POST', params: { action: 'connect' }
},
disconnect: {
method: 'POST', params: { action: 'disconnect' }
}
});
}]);

View file

@ -1,5 +1,6 @@
angular.module('portainer.docker')
.factory('Service', ['$resource', 'API_ENDPOINT_ENDPOINTS', 'EndpointProvider', 'HttpRequestHelper' ,function ServiceFactory($resource, API_ENDPOINT_ENDPOINTS, EndpointProvider, HttpRequestHelper) {
.factory('Service', ['$resource', 'API_ENDPOINT_ENDPOINTS', 'EndpointProvider', 'HttpRequestHelper',
function ServiceFactory($resource, API_ENDPOINT_ENDPOINTS, EndpointProvider, HttpRequestHelper) {
'use strict';
return $resource(API_ENDPOINT_ENDPOINTS + '/:endpointId/docker/services/:id/:action', {
endpointId: EndpointProvider.endpointID

View file

@ -6,7 +6,10 @@ angular.module('portainer.docker')
endpointId: EndpointProvider.endpointID
},
{
info: { method: 'GET', params: { action: 'info' }, ignoreLoadingBar: true },
info: {
method: 'GET', params: { action: 'info' },
ignoreLoadingBar: true
},
version: { method: 'GET', params: { action: 'version' }, ignoreLoadingBar: true, timeout: 4500 },
events: {
method: 'GET', params: { action: 'events', since: '@since', until: '@until' },

View file

@ -35,6 +35,34 @@ function ContainerServiceFactory($q, Container, ResourceControlService, LogHelpe
return deferred.promise;
};
service.startContainer = function(id) {
return Container.start({ id: id }, {}).$promise;
};
service.stopContainer = function(id) {
return Container.stop({ id: id }, {}).$promise;
};
service.restartContainer = function(id) {
return Container.restart({ id: id }, {}).$promise;
};
service.killContainer = function(id) {
return Container.kill({ id: id }, {}).$promise;
};
service.pauseContainer = function(id) {
return Container.pause({ id: id }, {}).$promise;
};
service.resumeContainer = function(id) {
return Container.unpause({ id: id }, {}).$promise;
};
service.renameContainer = function(id, newContainerName) {
return Container.rename({id: id, name: newContainerName }, {}).$promise;
};
service.createContainer = function(configuration) {
var deferred = $q.defer();
Container.create(configuration).$promise
@ -51,30 +79,6 @@ function ContainerServiceFactory($q, Container, ResourceControlService, LogHelpe
return deferred.promise;
};
service.startContainer = function(containerID) {
return Container.start({ id: containerID }, {}).$promise;
};
service.stopContainer = function(containerID) {
return Container.stop({ id: containerID }, {}).$promise;
};
service.restartContainer = function(containerID) {
return Container.restart({ id: containerID }, {}).$promise;
};
service.killContainer = function(containerID) {
return Container.kill({ id: containerID }, {}).$promise;
};
service.pauseContainer = function(containerID) {
return Container.pause({ id: containerID }, {}).$promise;
};
service.resumeContainer = function(containerID) {
return Container.unpause({ id: containerID }, {}).$promise;
};
service.createAndStartContainer = function(configuration) {
var deferred = $q.defer();
var containerID;
@ -95,7 +99,7 @@ function ContainerServiceFactory($q, Container, ResourceControlService, LogHelpe
service.remove = function(container, removeVolumes) {
var deferred = $q.defer();
Container.remove({id: container.Id, v: (removeVolumes) ? 1 : 0, force: true}).$promise
Container.remove({ id: container.Id, v: (removeVolumes) ? 1 : 0, force: true }).$promise
.then(function success(data) {
if (data.message) {
deferred.reject({ msg: data.message, err: data.message });
@ -117,7 +121,7 @@ function ContainerServiceFactory($q, Container, ResourceControlService, LogHelpe
service.createExec = function(execConfig) {
var deferred = $q.defer();
Container.exec(execConfig).$promise
Container.exec({}, execConfig).$promise
.then(function success(data) {
if (data.message) {
deferred.reject({ msg: data.message, err: data.message });
@ -158,7 +162,7 @@ function ContainerServiceFactory($q, Container, ResourceControlService, LogHelpe
service.containerStats = function(id) {
var deferred = $q.defer();
Container.stats({id: id}).$promise
Container.stats({ id: id }).$promise
.then(function success(data) {
var containerStats = new ContainerStatsViewModel(data);
deferred.resolve(containerStats);
@ -171,11 +175,11 @@ function ContainerServiceFactory($q, Container, ResourceControlService, LogHelpe
};
service.containerTop = function(id) {
return Container.top({id: id}).$promise;
return Container.top({ id: id }).$promise;
};
service.inspect = function(id) {
return Container.inspect({id: id}).$promise;
return Container.inspect({ id: id }).$promise;
};
return service;

View file

@ -7,7 +7,7 @@ angular.module('portainer.docker')
var deferred = $q.defer();
$timeout(function() {
Exec.resize({id: execId, height: height, width: width}).$promise
Exec.resize({}, { id: execId, height: height, width: width }).$promise
.then(function success(data) {
if (data.message) {
deferred.reject({ msg: 'Unable to exec into container', err: data.message });

View file

@ -19,7 +19,7 @@ angular.module('portainer.docker')
service.network = function(id) {
var deferred = $q.defer();
Network.get({id: id}).$promise
Network.get({ id: id }).$promise
.then(function success(data) {
var network = new NetworkViewModel(data);
deferred.resolve(network);
@ -65,5 +65,13 @@ angular.module('portainer.docker')
return Network.remove({ id: id }).$promise;
};
service.disconnectContainer = function(networkId, containerId, force) {
return Network.disconnect({ id: networkId }, { Container: containerId, Force: force }).$promise;
};
service.connectContainer = function(networkId, containerId) {
return Network.connect({ id: networkId }, { Container: containerId }).$promise;
};
return service;
}]);

View file

@ -1,6 +1,8 @@
angular.module('portainer.docker')
.controller('ContainerConsoleController', ['$scope', '$transition$', 'Container', 'Image', 'EndpointProvider', 'Notifications', 'ContainerHelper', 'ContainerService', 'ExecService',
function ($scope, $transition$, Container, Image, EndpointProvider, Notifications, ContainerHelper, ContainerService, ExecService) {
.controller('ContainerConsoleController', ['$scope', '$transition$', 'ContainerService', 'ImageService', 'EndpointProvider', 'Notifications', 'ContainerHelper', 'ExecService', 'HttpRequestHelper',
function ($scope, $transition$, ContainerService, ImageService, EndpointProvider, Notifications, ContainerHelper, ExecService, HttpRequestHelper) {
var socket, term;
$scope.state = {
loaded: false,
connected: false
@ -8,8 +10,6 @@ function ($scope, $transition$, Container, Image, EndpointProvider, Notification
$scope.formValues = {};
var socket, term;
// Ensure the socket is closed before leaving the view
$scope.$on('$stateChangeStart', function (event, next, current) {
if (socket && socket !== null) {
@ -17,23 +17,6 @@ function ($scope, $transition$, Container, Image, EndpointProvider, Notification
}
});
Container.get({id: $transition$.params().id}, function(d) {
$scope.container = d;
if (d.message) {
Notifications.error('Error', d, 'Unable to retrieve container details');
} else {
Image.get({id: d.Image}, function(imgData) {
$scope.imageOS = imgData.Os;
$scope.formValues.command = imgData.Os === 'windows' ? 'powershell' : 'bash';
$scope.state.loaded = true;
}, function (e) {
Notifications.error('Failure', e, 'Unable to retrieve image details');
});
}
}, function (e) {
Notifications.error('Failure', e, 'Unable to retrieve container details');
});
$scope.connect = function() {
var termWidth = Math.floor(($('#terminal-container').width() - 20) / 8.39);
var termHeight = 30;
@ -54,6 +37,9 @@ function ($scope, $transition$, Container, Image, EndpointProvider, Notification
.then(function success(data) {
execId = data.Id;
var url = window.location.href.split('#')[0] + 'api/websocket/exec?id=' + execId + '&endpointId=' + EndpointProvider.endpointID();
if ($transition$.params().nodeName) {
url += '&nodeName=' + $transition$.params().nodeName;
}
if (url.indexOf('https') > -1) {
url = url.replace('https://', 'wss://');
} else {
@ -108,4 +94,25 @@ function ($scope, $transition$, Container, Image, EndpointProvider, Notification
};
};
}
function initView() {
HttpRequestHelper.setPortainerAgentTargetHeader($transition$.params().nodeName);
ContainerService.container($transition$.params().id)
.then(function success(data) {
var container = data;
$scope.container = container;
return ImageService.image(container.Image);
})
.then(function success(data) {
var image = data;
$scope.imageOS = image.Os;
$scope.formValues.command = image.Os === 'windows' ? 'powershell' : 'bash';
$scope.state.loaded = true;
})
.catch(function error(err) {
Notifications.error('Error', err, 'Unable to retrieve container details');
});
}
initView();
}]);

View file

@ -14,6 +14,7 @@
dataset="containers" table-key="containers"
order-by="Status" show-text-filter="true"
show-ownership-column="applicationState.application.authentication"
show-host-column="applicationState.endpoint.mode.agentProxy"
public-url="state.publicURL"
container-name-truncate-size="truncate_size"
start-action="startAction"

View file

@ -1,6 +1,6 @@
angular.module('portainer.docker')
.controller('ContainersController', ['$q', '$scope', '$state', '$filter', '$transition$', 'ContainerService', 'SystemService', 'Notifications', 'ModalService', 'EndpointProvider',
function ($q, $scope, $state, $filter, $transition$, ContainerService, SystemService, Notifications, ModalService, EndpointProvider) {
.controller('ContainersController', ['$q', '$scope', '$state', '$filter', '$transition$', 'ContainerService', 'SystemService', 'Notifications', 'ModalService', 'EndpointProvider', 'HttpRequestHelper',
function ($q, $scope, $state, $filter, $transition$, ContainerService, SystemService, Notifications, ModalService, EndpointProvider, HttpRequestHelper) {
$scope.state = {
publicURL: EndpointProvider.endpointPublicURL()
};
@ -70,6 +70,7 @@ angular.module('portainer.docker')
function executeActionOnContainerList(containers, action, successMessage, errorMessage) {
var actionCount = containers.length;
angular.forEach(containers, function (container) {
HttpRequestHelper.setPortainerAgentTargetHeader(container.NodeName);
action(container.Id)
.then(function success() {
Notifications.success(successMessage, container.Names[0]);
@ -89,6 +90,7 @@ angular.module('portainer.docker')
function removeAction(containers, cleanVolumes) {
var actionCount = containers.length;
angular.forEach(containers, function (container) {
HttpRequestHelper.setPortainerAgentTargetHeader(container.NodeName);
ContainerService.remove(container, cleanVolumes)
.then(function success() {
Notifications.success('Container successfully removed', container.Names[0]);

View file

@ -1,6 +1,6 @@
angular.module('portainer.docker')
.controller('CreateContainerController', ['$q', '$scope', '$state', '$timeout', '$transition$', '$filter', 'Container', 'ContainerHelper', 'Image', 'ImageHelper', 'Volume', 'NetworkService', 'ResourceControlService', 'Authentication', 'Notifications', 'ContainerService', 'ImageService', 'FormValidator', 'ModalService', 'RegistryService', 'SystemService', 'SettingsService',
function ($q, $scope, $state, $timeout, $transition$, $filter, Container, ContainerHelper, Image, ImageHelper, Volume, NetworkService, ResourceControlService, Authentication, Notifications, ContainerService, ImageService, FormValidator, ModalService, RegistryService, SystemService, SettingsService) {
.controller('CreateContainerController', ['$q', '$scope', '$state', '$timeout', '$transition$', '$filter', 'Container', 'ContainerHelper', 'Image', 'ImageHelper', 'Volume', 'NetworkService', 'ResourceControlService', 'Authentication', 'Notifications', 'ContainerService', 'ImageService', 'FormValidator', 'ModalService', 'RegistryService', 'SystemService', 'SettingsService', 'HttpRequestHelper',
function ($q, $scope, $state, $timeout, $transition$, $filter, Container, ContainerHelper, Image, ImageHelper, Volume, NetworkService, ResourceControlService, Authentication, Notifications, ContainerService, ImageService, FormValidator, ModalService, RegistryService, SystemService, SettingsService, HttpRequestHelper) {
$scope.formValues = {
alwaysPull: true,
@ -15,7 +15,8 @@ function ($q, $scope, $state, $timeout, $transition$, $filter, Container, Contai
AccessControlData: new AccessControlFormData(),
CpuLimit: 0,
MemoryLimit: 0,
MemoryReservation: 0
MemoryReservation: 0,
NodeName: null
};
$scope.state = {
@ -388,12 +389,14 @@ function ($q, $scope, $state, $timeout, $transition$, $filter, Container, Contai
// Mac Address
$scope.formValues.MacAddress = d.NetworkSettings.Networks[$scope.config.HostConfig.NetworkMode].MacAddress;
// ExtraHosts
var extraHosts = $scope.config.HostConfig.ExtraHosts;
for (var i = 0; i < extraHosts.length; i++) {
var host = extraHosts[i];
$scope.formValues.ExtraHosts.push({ 'value': host });
if ($scope.config.HostConfig.ExtraHosts) {
var extraHosts = $scope.config.HostConfig.ExtraHosts;
for (var i = 0; i < extraHosts.length; i++) {
var host = extraHosts[i];
$scope.formValues.ExtraHosts.push({ 'value': host });
}
$scope.config.HostConfig.ExtraHosts = [];
}
$scope.config.HostConfig.ExtraHosts = [];
}
function loadFromContainerEnvironmentVariables(d) {
@ -491,6 +494,10 @@ function ($q, $scope, $state, $timeout, $transition$, $filter, Container, Contai
}
function initView() {
var nodeName = $transition$.params().nodeName;
$scope.formValues.NodeName = nodeName;
HttpRequestHelper.setPortainerAgentTargetHeader(nodeName);
Volume.query({}, function (d) {
$scope.availableVolumes = d.Volumes;
}, function (e) {
@ -520,7 +527,7 @@ function ($q, $scope, $state, $timeout, $transition$, $filter, Container, Contai
Container.query({}, function (d) {
var containers = d;
$scope.runningContainers = containers;
if ($transition$.params().from !== '') {
if ($transition$.params().from) {
loadFromContainerSpec();
} else {
$scope.fromContainer = {};
@ -587,6 +594,8 @@ function ($q, $scope, $state, $timeout, $transition$, $filter, Container, Contai
$scope.state.actionInProgress = true;
var config = prepareConfiguration();
var nodeName = $scope.formValues.NodeName;
HttpRequestHelper.setPortainerAgentTargetHeader(nodeName);
createContainer(config, accessControlData);
})
.catch(function error(err) {

View file

@ -103,6 +103,16 @@
<!-- !port-mapping-input-list -->
</div>
<!-- !port-mapping -->
<div ng-if="applicationState.endpoint.mode.agentProxy">
<div class="col-sm-12 form-section-title">
Deployment
</div>
<!-- node-selection -->
<node-selector
model="formValues.NodeName">
</node-selector>
<!-- !node-selection -->
</div>
<!-- access-control -->
<por-access-control-form form-data="formValues.AccessControlData" resource-control="fromContainer.ResourceControl" ng-if="applicationState.application.authentication && fromContainer"></por-access-control-form>
<!-- !access-control -->

View file

@ -20,12 +20,12 @@
<button class="btn btn-primary btn-sm" ng-click="unpause()" ng-disabled="!container.State.Paused"><i class="fa fa-play space-right" aria-hidden="true"></i>Resume</button>
<button class="btn btn-danger btn-sm" ng-click="confirmRemove()"><i class="fa fa-trash-alt space-right" aria-hidden="true"></i>Remove</button>
</div>
<div class="btn-group" role="group" aria-label="...">
<button type="button" class="btn btn-danger btn-sm" ng-disabled="state.recreateContainerInProgress" ng-click="recreate()" button-spinner="state.recreateContainerInProgress" ng-if="!container.Config.Labels['com.docker.swarm.service.id']">
<div class="btn-group" role="group" aria-label="..." ng-if="!container.Config.Labels['com.docker.swarm.service.id']">
<button type="button" class="btn btn-danger btn-sm" ng-disabled="state.recreateContainerInProgress" ng-click="recreate()" button-spinner="state.recreateContainerInProgress">
<span ng-hide="state.recreateContainerInProgress"><i class="fa fa-sync space-right" aria-hidden="true"></i>Recreate</span>
<span ng-show="state.recreateContainerInProgress">Recreation in progress...</span>
</button>
<button class="btn btn-primary btn-sm" ng-click="duplicate()" ng-if="!container.Config.Labels['com.docker.swarm.service.id']"><i class="fa fa-copy space-right" aria-hidden="true"></i>Duplicate/Edit</button>
<a class="btn btn-primary btn-sm" type="button" ui-sref="docker.containers.new({ from: container.Id, nodeName: nodeName })"><i class="fa fa-copy space-right" aria-hidden="true"></i>Duplicate/Edit</a>
</div>
</rd-widget-body>
</rd-widget>
@ -84,10 +84,10 @@
<tr>
<td colspan="2">
<div class="btn-group" role="group" aria-label="...">
<a class="btn" type="button" ui-sref="docker.containers.container.stats({id: container.Id})"><i class="fa fa-chart-area space-right" aria-hidden="true"></i>Stats</a>
<a class="btn" type="button" ui-sref="docker.containers.container.logs({id: container.Id})"><i class="fa fa-file-alt space-right" aria-hidden="true"></i>Logs</a>
<a class="btn" type="button" ui-sref="docker.containers.container.console({id: container.Id})"><i class="fa fa-terminal space-right" aria-hidden="true"></i>Console</a>
<a class="btn" type="button" ui-sref="docker.containers.container.inspect({id: container.Id})"><i class="fa fa-info-circle space-right" aria-hidden="true"></i>Inspect</a>
<a class="btn" type="button" ui-sref="docker.containers.container.stats({ id: container.Id })"><i class="fa fa-chart-area space-right" aria-hidden="true"></i>Stats</a>
<a class="btn" type="button" ui-sref="docker.containers.container.logs({ id: container.Id })"><i class="fa fa-file-alt space-right" aria-hidden="true"></i>Logs</a>
<a class="btn" type="button" ui-sref="docker.containers.container.console({ id: container.Id })"><i class="fa fa-terminal space-right" aria-hidden="true"></i>Console</a>
<a class="btn" type="button" ui-sref="docker.containers.container.inspect({ id: container.Id })"><i class="fa fa-info-circle space-right" aria-hidden="true"></i>Inspect</a>
</div>
</td>
</tr>
@ -184,7 +184,7 @@
<tbody>
<tr>
<td>Image</td>
<td><a ui-sref="docker.images.image({id: container.Image})">{{ container.Image }}</a></td>
<td><a ui-sref="docker.images.image({ id: container.Image, nodeName: nodeName })">{{ container.Image }}</a></td>
</tr>
<tr ng-if="portBindings.length > 0">
<td>Port configuration</td>
@ -259,7 +259,7 @@
<tbody>
<tr ng-repeat="vol in container.Mounts">
<td ng-if="vol.Type === 'bind'">{{ vol.Source }}</td>
<td ng-if="vol.Type === 'volume'"><a ui-sref="docker.volumes.volume({id: vol.Name})">{{ vol.Name }}</a></td>
<td ng-if="vol.Type === 'volume'"><a ui-sref="docker.volumes.volume({ id: vol.Name, nodeName: nodeName })">{{ vol.Name }}</a></td>
<td>{{ vol.Destination }}</td>
</tr>
</tbody>
@ -280,6 +280,7 @@
join-network-action-in-progress="state.joinNetworkInProgress"
leave-network-action="containerLeaveNetwork"
leave-network-action-in-progress="state.leaveNetworkInProgress"
node-name="nodeName"
></container-networks-datatable>
</div>
</div>

View file

@ -1,6 +1,6 @@
angular.module('portainer.docker')
.controller('ContainerController', ['$q', '$scope', '$state','$transition$', '$filter', 'Container', 'Commit', 'ContainerHelper', 'ContainerService', 'ImageHelper', 'Network', 'NetworkService', 'Notifications', 'ModalService', 'ResourceControlService', 'RegistryService', 'ImageService',
function ($q, $scope, $state, $transition$, $filter, Container, Commit, ContainerHelper, ContainerService, ImageHelper, Network, NetworkService, Notifications, ModalService, ResourceControlService, RegistryService, ImageService) {
.controller('ContainerController', ['$q', '$scope', '$state','$transition$', '$filter', 'Commit', 'ContainerHelper', 'ContainerService', 'ImageHelper', 'NetworkService', 'Notifications', 'ModalService', 'ResourceControlService', 'RegistryService', 'ImageService', 'HttpRequestHelper',
function ($q, $scope, $state, $transition$, $filter, Commit, ContainerHelper, ContainerService, ImageHelper, NetworkService, Notifications, ModalService, ResourceControlService, RegistryService, ImageService, HttpRequestHelper) {
$scope.activityTime = 0;
$scope.portBindings = [];
@ -16,8 +16,13 @@ function ($q, $scope, $state, $transition$, $filter, Container, Commit, Containe
};
var update = function () {
Container.get({id: $transition$.params().id}, function (d) {
var container = new ContainerDetailsViewModel(d);
var nodeName = $transition$.params().nodeName;
HttpRequestHelper.setPortainerAgentTargetHeader(nodeName);
$scope.nodeName = nodeName;
ContainerService.container($transition$.params().id)
.then(function success(data) {
var container = data;
$scope.container = container;
$scope.container.edit = false;
$scope.container.newContainerName = $filter('trimcontainername')(container.Name);
@ -41,41 +46,105 @@ function ($q, $scope, $state, $transition$, $filter, Container, Commit, Containe
}
});
}
}, function (e) {
Notifications.error('Failure', e, 'Unable to retrieve container info');
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to retrieve container info');
});
};
$scope.start = function () {
Container.start({id: $scope.container.Id}, {}, function (d) {
function executeContainerAction(id, action, successMessage, errorMessage) {
action(id)
.then(function success(data) {
Notifications.success(successMessage, id);
update();
Notifications.success('Container started', $transition$.params().id);
}, function (e) {
update();
Notifications.error('Failure', e, 'Unable to start container');
})
.catch(function error(err) {
Notifications.error('Failure', err, errorMessage);
});
}
$scope.start = function () {
var successMessage = 'Container successfully started';
var errorMessage = 'Unable to start container';
executeContainerAction($transition$.params().id, ContainerService.startContainer, successMessage, errorMessage);
};
$scope.stop = function () {
Container.stop({id: $transition$.params().id}, function (d) {
update();
Notifications.success('Container stopped', $transition$.params().id);
}, function (e) {
update();
Notifications.error('Failure', e, 'Unable to stop container');
});
var successMessage = 'Container successfully stopped';
var errorMessage = 'Unable to stop container';
executeContainerAction($transition$.params().id, ContainerService.stopContainer, successMessage, errorMessage);
};
$scope.kill = function () {
Container.kill({id: $transition$.params().id}, function (d) {
update();
Notifications.success('Container killed', $transition$.params().id);
}, function (e) {
update();
Notifications.error('Failure', e, 'Unable to kill container');
var successMessage = 'Container successfully killed';
var errorMessage = 'Unable to kill container';
executeContainerAction($transition$.params().id, ContainerService.killContainer, successMessage, errorMessage);
};
$scope.pause = function() {
var successMessage = 'Container successfully paused';
var errorMessage = 'Unable to pause container';
executeContainerAction($transition$.params().id, ContainerService.pauseContainer, successMessage, errorMessage);
};
$scope.unpause = function() {
var successMessage = 'Container successfully resumed';
var errorMessage = 'Unable to resume container';
executeContainerAction($transition$.params().id, ContainerService.resumeContainer, successMessage, errorMessage);
};
$scope.restart = function () {
var successMessage = 'Container successfully restarted';
var errorMessage = 'Unable to restart container';
executeContainerAction($transition$.params().id, ContainerService.restartContainer, successMessage, errorMessage);
};
$scope.renameContainer = function () {
var container = $scope.container;
ContainerService.renameContainer($transition$.params().id, container.newContainerName)
.then(function success(data) {
container.Name = container.newContainerName;
Notifications.success('Container successfully renamed', container.Name);
})
.catch(function error(err) {
container.newContainerName = container.Name;
Notifications.error('Failure', err, 'Unable to rename container');
})
.finally(function final() {
$scope.container.edit = false;
});
};
$scope.containerLeaveNetwork = function containerLeaveNetwork(container, networkId) {
$scope.state.leaveNetworkInProgress = true;
NetworkService.disconnectContainer(networkId, container.Id, false)
.then(function success(data) {
Notifications.success('Container left network', container.Id);
$state.reload();
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to disconnect container from network');
})
.finally(function final() {
$scope.state.leaveNetworkInProgress = false;
});
};
$scope.containerJoinNetwork = function containerJoinNetwork(container, networkId) {
$scope.state.joinNetworkInProgress = true;
NetworkService.connectContainer(networkId, container.Id)
.then(function success(data) {
Notifications.success('Container joined network', container.Id);
$state.reload();
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to connect container to network');
})
.finally(function final() {
$scope.state.joinNetworkInProgress = false;
});
};
$scope.commit = function () {
var image = $scope.config.Image;
var registry = $scope.config.Registry;
@ -89,25 +158,6 @@ function ($q, $scope, $state, $transition$, $filter, Container, Commit, Containe
});
};
$scope.pause = function () {
Container.pause({id: $transition$.params().id}, function (d) {
update();
Notifications.success('Container paused', $transition$.params().id);
}, function (e) {
update();
Notifications.error('Failure', e, 'Unable to pause container');
});
};
$scope.unpause = function () {
Container.unpause({id: $transition$.params().id}, function (d) {
update();
Notifications.success('Container unpaused', $transition$.params().id);
}, function (e) {
update();
Notifications.error('Failure', e, 'Unable to unpause container');
});
};
$scope.confirmRemove = function () {
var title = 'You are about to remove a container.';
@ -122,12 +172,12 @@ function ($q, $scope, $state, $transition$, $filter, Container, Commit, Containe
if (result[0]) {
cleanAssociatedVolumes = true;
}
$scope.remove(cleanAssociatedVolumes);
removeContainer(cleanAssociatedVolumes);
}
);
};
$scope.remove = function(cleanAssociatedVolumes) {
function removeContainer(cleanAssociatedVolumes) {
ContainerService.remove($scope.container, cleanAssociatedVolumes)
.then(function success() {
Notifications.success('Container successfully removed');
@ -136,71 +186,7 @@ function ($q, $scope, $state, $transition$, $filter, Container, Commit, Containe
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to remove container');
});
};
$scope.restart = function () {
Container.restart({id: $transition$.params().id}, function (d) {
update();
Notifications.success('Container restarted', $transition$.params().id);
}, function (e) {
update();
Notifications.error('Failure', e, 'Unable to restart container');
});
};
$scope.renameContainer = function () {
var container = $scope.container;
Container.rename({id: $transition$.params().id, 'name': container.newContainerName}, function (d) {
if (d.message) {
container.newContainerName = container.Name;
Notifications.error('Unable to rename container', {}, d.message);
} else {
container.Name = container.newContainerName;
Notifications.success('Container successfully renamed', container.Name);
}
}, function (e) {
Notifications.error('Failure', e, 'Unable to rename container');
});
$scope.container.edit = false;
};
$scope.containerLeaveNetwork = function containerLeaveNetwork(container, networkId) {
$scope.state.leaveNetworkInProgress = true;
Network.disconnect({id: networkId}, { Container: $transition$.params().id, Force: false }, function (d) {
if (container.message) {
Notifications.error('Error', d, 'Unable to disconnect container from network');
} else {
Notifications.success('Container left network', $transition$.params().id);
$state.go('docker.containers.container', {id: $transition$.params().id}, {reload: true});
}
$scope.state.leaveNetworkInProgress = false;
}, function (e) {
Notifications.error('Failure', e, 'Unable to disconnect container from network');
$scope.state.leaveNetworkInProgress = false;
});
};
$scope.duplicate = function() {
$state.go('docker.containers.new', {from: $transition$.params().id}, {reload: true});
};
$scope.confirmRemove = function () {
var title = 'You are about to remove a container.';
if ($scope.container.State.Running) {
title = 'You are about to remove a running container.';
}
ModalService.confirmContainerDeletion(
title,
function (result) {
if(!result) { return; }
var cleanAssociatedVolumes = false;
if (result[0]) {
cleanAssociatedVolumes = true;
}
$scope.remove(cleanAssociatedVolumes);
}
);
};
}
function recreateContainer(pullImage) {
var container = $scope.container;
@ -253,22 +239,6 @@ function ($q, $scope, $state, $transition$, $filter, Container, Commit, Containe
});
};
$scope.containerJoinNetwork = function containerJoinNetwork(container, networkId) {
$scope.state.joinNetworkInProgress = true;
Network.connect({id: networkId}, { Container: $transition$.params().id }, function (d) {
if (container.message) {
Notifications.error('Error', d, 'Unable to connect container to network');
} else {
Notifications.success('Container joined network', $transition$.params().id);
$state.go('docker.containers.container', {id: $transition$.params().id}, {reload: true});
}
$scope.state.joinNetworkInProgress = false;
}, function (e) {
Notifications.error('Failure', e, 'Unable to connect container to network');
$scope.state.joinNetworkInProgress = false;
});
};
var provider = $scope.applicationState.endpoint.mode.provider;
var apiVersion = $scope.applicationState.endpoint.apiVersion;
NetworkService.networks(

View file

@ -1,6 +1,6 @@
angular.module('portainer.docker')
.controller('ContainerInspectController', ['$scope', '$transition$', 'Notifications', 'ContainerService',
function ($scope, $transition$, Notifications, ContainerService) {
.controller('ContainerInspectController', ['$scope', '$transition$', 'Notifications', 'ContainerService', 'HttpRequestHelper',
function ($scope, $transition$, Notifications, ContainerService, HttpRequestHelper) {
$scope.state = {
DisplayTextView: false
@ -8,6 +8,7 @@ function ($scope, $transition$, Notifications, ContainerService) {
$scope.containerInfo = {};
function initView() {
HttpRequestHelper.setPortainerAgentTargetHeader($transition$.params().nodeName);
ContainerService.inspect($transition$.params().id)
.then(function success(d) {
$scope.containerInfo = d;

View file

@ -1,6 +1,6 @@
angular.module('portainer.docker')
.controller('ContainerLogsController', ['$scope', '$transition$', '$interval', 'ContainerService', 'Notifications',
function ($scope, $transition$, $interval, ContainerService, Notifications) {
.controller('ContainerLogsController', ['$scope', '$transition$', '$interval', 'ContainerService', 'Notifications', 'HttpRequestHelper',
function ($scope, $transition$, $interval, ContainerService, Notifications, HttpRequestHelper) {
$scope.state = {
refreshRate: 3,
lineCount: 2000,
@ -58,6 +58,7 @@ function ($scope, $transition$, $interval, ContainerService, Notifications) {
}
function initView() {
HttpRequestHelper.setPortainerAgentTargetHeader($transition$.params().nodeName);
ContainerService.container($transition$.params().id)
.then(function success(data) {
var container = data;

View file

@ -1,6 +1,6 @@
angular.module('portainer.docker')
.controller('ContainerStatsController', ['$q', '$scope', '$transition$', '$document', '$interval', 'ContainerService', 'ChartService', 'Notifications',
function ($q, $scope, $transition$, $document, $interval, ContainerService, ChartService, Notifications) {
.controller('ContainerStatsController', ['$q', '$scope', '$transition$', '$document', '$interval', 'ContainerService', 'ChartService', 'Notifications', 'HttpRequestHelper',
function ($q, $scope, $transition$, $document, $interval, ContainerService, ChartService, Notifications, HttpRequestHelper) {
$scope.state = {
refreshRate: '5',
@ -126,6 +126,7 @@ function ($q, $scope, $transition$, $document, $interval, ContainerService, Char
}
function initView() {
HttpRequestHelper.setPortainerAgentTargetHeader($transition$.params().nodeName);
ContainerService.container($transition$.params().id)
.then(function success(data) {
$scope.container = data;

View file

@ -3,7 +3,13 @@
<rd-header-content>Dashboard</rd-header-content>
</rd-header>
<div class="row">
<div class="row" ng-if="applicationState.endpoint.mode.agentProxy">
<div class="col-sm-12">
<dashboard-cluster-agent-info></dashboard-cluster-agent-info>
</div>
</div>
<div class="row" ng-if="!applicationState.endpoint.mode.agentProxy">
<div class="col-sm-12">
<rd-widget>
<rd-widget-header icon="fa-tachometer-alt" title="Node info"></rd-widget-header>

View file

@ -1,6 +1,6 @@
angular.module('portainer.docker')
.controller('BuildImageController', ['$scope', '$state', 'BuildService', 'Notifications',
function ($scope, $state, BuildService, Notifications) {
.controller('BuildImageController', ['$scope', '$state', 'BuildService', 'Notifications', 'HttpRequestHelper',
function ($scope, $state, BuildService, Notifications, HttpRequestHelper) {
$scope.state = {
BuildType: 'editor',
@ -13,7 +13,8 @@ function ($scope, $state, BuildService, Notifications) {
UploadFile: null,
DockerFileContent: '',
URL: '',
Path: 'Dockerfile'
Path: 'Dockerfile',
NodeName: null
};
$scope.addImageName = function() {
@ -56,6 +57,9 @@ function ($scope, $state, BuildService, Notifications) {
return x.Name;
});
var nodeName = $scope.formValues.NodeName;
HttpRequestHelper.setPortainerAgentTargetHeader(nodeName);
buildImageBasedOnBuildType(buildType, imageNames)
.then(function success(data) {
$scope.buildLogs = data.buildLogs;

View file

@ -194,6 +194,16 @@
</div>
</div>
<!-- !url -->
<div ng-if="applicationState.endpoint.mode.agentProxy">
<div class="col-sm-12 form-section-title">
Deployment
</div>
<!-- node-selection -->
<node-selector
model="formValues.NodeName">
</node-selector>
<!-- !node-selection -->
</div>
<!-- actions -->
<div class="col-sm-12 form-section-title">
Actions

View file

@ -1,6 +1,6 @@
angular.module('portainer.docker')
.controller('ImageController', ['$q', '$scope', '$transition$', '$state', '$timeout', 'ImageService', 'RegistryService', 'Notifications',
function ($q, $scope, $transition$, $state, $timeout, ImageService, RegistryService, Notifications) {
.controller('ImageController', ['$q', '$scope', '$transition$', '$state', '$timeout', 'ImageService', 'RegistryService', 'Notifications', 'HttpRequestHelper',
function ($q, $scope, $transition$, $state, $timeout, ImageService, RegistryService, Notifications, HttpRequestHelper) {
$scope.formValues = {
Image: '',
Registry: ''
@ -98,6 +98,7 @@ function ($q, $scope, $transition$, $state, $timeout, ImageService, RegistryServ
};
function initView() {
HttpRequestHelper.setPortainerAgentTargetHeader($transition$.params().nodeName);
var endpointProvider = $scope.applicationState.endpoint.mode.provider;
$q.all({
image: ImageService.image($transition$.params().id),

View file

@ -26,6 +26,16 @@
</div>
</div>
<!-- !tag-note -->
<div ng-if="applicationState.endpoint.mode.agentProxy">
<div class="col-sm-12 form-section-title">
Deployment
</div>
<!-- node-selection -->
<node-selector
model="formValues.NodeName">
</node-selector>
<!-- !node-selection -->
</div>
<div class="form-group">
<div class="col-sm-12">
<button type="button" class="btn btn-primary btn-sm" ng-disabled="state.actionInProgress || !formValues.Image" ng-click="pullImage()" button-spinner="state.actionInProgress">
@ -46,6 +56,7 @@
title="Images" title-icon="fa-clone"
dataset="images" table-key="images"
order-by="RepoTags" show-text-filter="true"
show-host-column="applicationState.endpoint.mode.agentProxy"
remove-action="removeAction"
force-remove-action="confirmRemovalAction"
></images-datatable>

View file

@ -1,19 +1,23 @@
angular.module('portainer.docker')
.controller('ImagesController', ['$scope', '$state', 'ImageService', 'Notifications', 'ModalService',
function ($scope, $state, ImageService, Notifications, ModalService) {
.controller('ImagesController', ['$scope', '$state', 'ImageService', 'Notifications', 'ModalService', 'HttpRequestHelper',
function ($scope, $state, ImageService, Notifications, ModalService, HttpRequestHelper) {
$scope.state = {
actionInProgress: false
};
$scope.formValues = {
Image: '',
Registry: ''
Registry: '',
NodeName: null
};
$scope.pullImage = function() {
var image = $scope.formValues.Image;
var registry = $scope.formValues.Registry;
var nodeName = $scope.formValues.NodeName;
HttpRequestHelper.setPortainerAgentTargetHeader(nodeName);
$scope.state.actionInProgress = true;
ImageService.pullImage(image, registry, false)
.then(function success(data) {
@ -38,6 +42,7 @@ function ($scope, $state, ImageService, Notifications, ModalService) {
$scope.removeAction = function (selectedItems, force) {
var actionCount = selectedItems.length;
angular.forEach(selectedItems, function (image) {
HttpRequestHelper.setPortainerAgentTargetHeader(image.NodeName);
ImageService.deleteImage(image.Id, force)
.then(function success() {
Notifications.success('Image successfully removed', image.Id);

View file

@ -1,13 +1,14 @@
angular.module('portainer.docker')
.controller('CreateNetworkController', ['$q', '$scope', '$state', 'PluginService', 'Notifications', 'NetworkService', 'LabelHelper', 'Authentication', 'ResourceControlService', 'FormValidator',
function ($q, $scope, $state, PluginService, Notifications, NetworkService, LabelHelper, Authentication, ResourceControlService, FormValidator) {
.controller('CreateNetworkController', ['$q', '$scope', '$state', 'PluginService', 'Notifications', 'NetworkService', 'LabelHelper', 'Authentication', 'ResourceControlService', 'FormValidator', 'HttpRequestHelper',
function ($q, $scope, $state, PluginService, Notifications, NetworkService, LabelHelper, Authentication, ResourceControlService, FormValidator, HttpRequestHelper) {
$scope.formValues = {
DriverOptions: [],
Subnet: '',
Gateway: '',
Labels: [],
AccessControlData: new AccessControlFormData()
AccessControlData: new AccessControlFormData(),
NodeName: null
};
$scope.state = {
@ -99,6 +100,9 @@ function ($q, $scope, $state, PluginService, Notifications, NetworkService, Labe
return;
}
var nodeName = $scope.formValues.NodeName;
HttpRequestHelper.setPortainerAgentTargetHeader(nodeName);
$scope.state.actionInProgress = true;
NetworkService.create(networkConfiguration)
.then(function success(data) {

View file

@ -119,6 +119,16 @@
</div>
</div>
<!-- !internal -->
<div ng-if="applicationState.endpoint.mode.agentProxy && config.Driver !== 'overlay'">
<div class="col-sm-12 form-section-title">
Deployment
</div>
<!-- node-selection -->
<node-selector
model="formValues.NodeName">
</node-selector>
<!-- !node-selection -->
</div>
<!-- access-control -->
<por-access-control-form form-data="formValues.AccessControlData" ng-if="applicationState.application.authentication"></por-access-control-form>
<!-- !access-control -->

View file

@ -89,12 +89,12 @@
</thead>
<tbody>
<tr ng-repeat="container in containersInNetwork">
<td><a ui-sref="docker.containers.container({id: container.Id})">{{ container.Name }}</a></td>
<td><a ui-sref="docker.containers.container({ id: container.Id, nodeName: nodeName })">{{ container.Name }}</a></td>
<td>{{ container.IPv4Address || '-' }}</td>
<td>{{ container.IPv6Address || '-' }}</td>
<td>{{ container.MacAddress || '-' }}</td>
<td>
<button type="button" class="btn btn-xs btn-danger" ng-click="containerLeaveNetwork(network, container.Id)"><i class="fa fa-trash-alt space-right" aria-hidden="true"></i>Leave Network</button>
<button type="button" class="btn btn-xs btn-danger" ng-click="containerLeaveNetwork(network, container)"><i class="fa fa-trash-alt space-right" aria-hidden="true"></i>Leave Network</button>
</td>
</tr>
</tbody>

View file

@ -1,30 +1,27 @@
angular.module('portainer.docker')
.controller('NetworkController', ['$scope', '$state', '$transition$', '$filter', 'Network', 'NetworkService', 'Container', 'ContainerHelper', 'Notifications',
function ($scope, $state, $transition$, $filter, Network, NetworkService, Container, ContainerHelper, Notifications) {
.controller('NetworkController', ['$scope', '$state', '$transition$', '$filter', 'NetworkService', 'Container', 'ContainerHelper', 'Notifications', 'HttpRequestHelper',
function ($scope, $state, $transition$, $filter, NetworkService, Container, ContainerHelper, Notifications, HttpRequestHelper) {
$scope.removeNetwork = function removeNetwork(networkId) {
Network.remove({id: $transition$.params().id}, function (d) {
if (d.message) {
Notifications.error('Error', d, 'Unable to remove network');
} else {
Notifications.success('Network removed', $transition$.params().id);
$state.go('docker.networks', {});
}
}, function (e) {
Notifications.error('Failure', e, 'Unable to remove network');
NetworkService.remove($transition$.params().id, $transition$.params().id)
.then(function success(data) {
Notifications.success('Network removed', $transition$.params().id);
$state.go('docker.networks', {});
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to remove network');
});
};
$scope.containerLeaveNetwork = function containerLeaveNetwork(network, containerId) {
Network.disconnect({id: $transition$.params().id}, { Container: containerId, Force: false }, function (d) {
if (d.message) {
Notifications.error('Error', d, 'Unable to disconnect container from network');
} else {
Notifications.success('Container left network', $transition$.params().id);
$state.go('docker.networks.network', {id: network.Id}, {reload: true});
}
}, function (e) {
Notifications.error('Failure', e, 'Unable to disconnect container from network');
$scope.containerLeaveNetwork = function containerLeaveNetwork(network, container) {
HttpRequestHelper.setPortainerAgentTargetHeader(container.NodeName);
NetworkService.disconnectContainer($transition$.params().id, container.Id, false)
.then(function success(data) {
Notifications.success('Container left network', $transition$.params().id);
$state.go('docker.networks.network', { id: network.Id }, { reload: true });
})
.catch(function error(err) {
Notifications.error('Failure', err, 'Unable to disconnect container from network');
});
};
@ -71,6 +68,9 @@ function ($scope, $state, $transition$, $filter, Network, NetworkService, Contai
}
function initView() {
var nodeName = $transition$.params().nodeName;
HttpRequestHelper.setPortainerAgentTargetHeader(nodeName);
$scope.nodeName = nodeName;
NetworkService.network($transition$.params().id)
.then(function success(data) {
$scope.network = data;

View file

@ -15,6 +15,7 @@
order-by="Name" show-text-filter="true"
remove-action="removeAction"
show-ownership-column="applicationState.application.authentication"
show-host-column="applicationState.endpoint.mode.agentProxy"
></networks-datatable>
</div>
</div>

View file

@ -1,10 +1,11 @@
angular.module('portainer.docker')
.controller('NetworksController', ['$scope', '$state', 'NetworkService', 'Notifications',
function ($scope, $state, NetworkService, Notifications) {
.controller('NetworksController', ['$scope', '$state', 'NetworkService', 'Notifications', 'HttpRequestHelper',
function ($scope, $state, NetworkService, Notifications, HttpRequestHelper) {
$scope.removeAction = function (selectedItems) {
var actionCount = selectedItems.length;
angular.forEach(selectedItems, function (network) {
HttpRequestHelper.setPortainerAgentTargetHeader(network.NodeName);
NetworkService.remove(network.Id)
.then(function success() {
Notifications.success('Network successfully removed', network.Name);

View file

@ -7,5 +7,6 @@
show-text-filter="true"
show-slot-column="service.Mode !== 'global'"
show-logs-button="applicationState.endpoint.apiVersion >= 1.30"
agent-powered="applicationState.endpoint.mode.agentProxy"
></tasks-datatable>
</div>

View file

@ -1,6 +1,6 @@
angular.module('portainer.docker')
.controller('ServiceController', ['$q', '$scope', '$transition$', '$state', '$location', '$timeout', '$anchorScroll', 'ServiceService', 'ConfigService', 'ConfigHelper', 'SecretService', 'ImageService', 'SecretHelper', 'Service', 'ServiceHelper', 'LabelHelper', 'TaskService', 'NodeService', 'Notifications', 'ModalService', 'PluginService', 'Authentication', 'SettingsService', 'VolumeService',
function ($q, $scope, $transition$, $state, $location, $timeout, $anchorScroll, ServiceService, ConfigService, ConfigHelper, SecretService, ImageService, SecretHelper, Service, ServiceHelper, LabelHelper, TaskService, NodeService, Notifications, ModalService, PluginService, Authentication, SettingsService, VolumeService) {
.controller('ServiceController', ['$q', '$scope', '$transition$', '$state', '$location', '$timeout', '$anchorScroll', 'ServiceService', 'ConfigService', 'ConfigHelper', 'SecretService', 'ImageService', 'SecretHelper', 'Service', 'ServiceHelper', 'LabelHelper', 'TaskService', 'NodeService', 'ContainerService', 'TaskHelper', 'Notifications', 'ModalService', 'PluginService', 'Authentication', 'SettingsService', 'VolumeService',
function ($q, $scope, $transition$, $state, $location, $timeout, $anchorScroll, ServiceService, ConfigService, ConfigHelper, SecretService, ImageService, SecretHelper, Service, ServiceHelper, LabelHelper, TaskService, NodeService, ContainerService, TaskHelper, Notifications, ModalService, PluginService, Authentication, SettingsService, VolumeService) {
$scope.state = {
updateInProgress: false,
@ -407,6 +407,7 @@ function ($q, $scope, $transition$, $state, $location, $timeout, $anchorScroll,
function initView() {
var apiVersion = $scope.applicationState.endpoint.apiVersion;
var agentProxy = $scope.applicationState.endpoint.mode.agentProxy;
ServiceService.service($transition$.params().id)
.then(function success(data) {
@ -425,6 +426,7 @@ function ($q, $scope, $transition$, $state, $location, $timeout, $anchorScroll,
return $q.all({
volumes: VolumeService.volumes(),
tasks: TaskService.tasks({ service: [service.Name] }),
containers: agentProxy ? ContainerService.containers() : [],
nodes: NodeService.nodes(),
secrets: apiVersion >= 1.25 ? SecretService.secrets() : [],
configs: apiVersion >= 1.30 ? ConfigService.configs() : [],
@ -434,7 +436,6 @@ function ($q, $scope, $transition$, $state, $location, $timeout, $anchorScroll,
});
})
.then(function success(data) {
$scope.tasks = data.tasks;
$scope.nodes = data.nodes;
$scope.configs = data.configs;
$scope.secrets = data.secrets;
@ -445,6 +446,19 @@ function ($q, $scope, $transition$, $state, $location, $timeout, $anchorScroll,
var userDetails = Authentication.getUserDetails();
$scope.isAdmin = userDetails.role === 1;
var tasks = data.tasks;
if (agentProxy) {
var containers = data.containers;
for (var i = 0; i < tasks.length; i++) {
var task = tasks[i];
TaskHelper.associateContainerToTask(task, containers);
}
}
$scope.tasks = data.tasks;
// Set max cpu value
var maxCpus = 0;
for (var n in data.nodes) {

View file

@ -42,6 +42,7 @@
show-text-filter="true"
show-slot-column="true"
show-logs-button="applicationState.endpoint.apiVersion >= 1.30"
agent-powered="applicationState.endpoint.mode.agentProxy"
></tasks-datatable>
</div>
</div>

View file

@ -1,6 +1,6 @@
angular.module('portainer.docker')
.controller('StackController', ['$q', '$scope', '$state', '$transition$', 'StackService', 'NodeService', 'ServiceService', 'TaskService', 'ServiceHelper', 'Notifications', 'FormHelper', 'EndpointProvider',
function ($q, $scope, $state, $transition$, StackService, NodeService, ServiceService, TaskService, ServiceHelper, Notifications, FormHelper, EndpointProvider) {
.controller('StackController', ['$q', '$scope', '$state', '$transition$', 'StackService', 'NodeService', 'ServiceService', 'TaskService', 'ContainerService', 'ServiceHelper', 'TaskHelper', 'Notifications', 'FormHelper', 'EndpointProvider',
function ($q, $scope, $state, $transition$, StackService, NodeService, ServiceService, TaskService, ContainerService, ServiceHelper, TaskHelper, Notifications, FormHelper, EndpointProvider) {
$scope.state = {
actionInProgress: false,
@ -41,6 +41,7 @@ function ($q, $scope, $state, $transition$, StackService, NodeService, ServiceSe
function initView() {
var stackId = $transition$.params().id;
var apiVersion = $scope.applicationState.endpoint.apiVersion;
var agentProxy = $scope.applicationState.endpoint.mode.agentProxy;
StackService.stack(stackId)
.then(function success(data) {
@ -55,24 +56,31 @@ function ($q, $scope, $state, $transition$, StackService, NodeService, ServiceSe
stackFile: StackService.getStackFile(stackId),
services: ServiceService.services(serviceFilters),
tasks: TaskService.tasks(serviceFilters),
containers: agentProxy ? ContainerService.containers() : [],
nodes: NodeService.nodes()
});
})
.then(function success(data) {
$scope.stackFileContent = data.stackFile;
$scope.nodes = data.nodes;
var services = data.services;
var tasks = data.tasks;
$scope.tasks = tasks;
for (var i = 0; i < services.length; i++) {
var service = services[i];
ServiceHelper.associateTasksToService(service, tasks);
}
if (agentProxy) {
var containers = data.containers;
for (var j = 0; j < tasks.length; j++) {
var task = tasks[j];
TaskHelper.associateContainerToTask(task, containers);
}
}
$scope.tasks = tasks;
$scope.services = services;
})
.catch(function error(err) {

View file

@ -1,11 +1,12 @@
angular.module('portainer.docker')
.controller('CreateVolumeController', ['$q', '$scope', '$state', 'VolumeService', 'PluginService', 'ResourceControlService', 'Authentication', 'Notifications', 'FormValidator',
function ($q, $scope, $state, VolumeService, PluginService, ResourceControlService, Authentication, Notifications, FormValidator) {
.controller('CreateVolumeController', ['$q', '$scope', '$state', 'VolumeService', 'PluginService', 'ResourceControlService', 'Authentication', 'Notifications', 'FormValidator', 'HttpRequestHelper',
function ($q, $scope, $state, VolumeService, PluginService, ResourceControlService, Authentication, Notifications, FormValidator, HttpRequestHelper) {
$scope.formValues = {
Driver: 'local',
DriverOptions: [],
AccessControlData: new AccessControlFormData()
AccessControlData: new AccessControlFormData(),
NodeName: null
};
$scope.state = {
@ -55,6 +56,9 @@ function ($q, $scope, $state, VolumeService, PluginService, ResourceControlServi
return;
}
var nodeName = $scope.formValues.NodeName;
HttpRequestHelper.setPortainerAgentTargetHeader(nodeName);
$scope.state.actionInProgress = true;
VolumeService.createVolume(volumeConfiguration)
.then(function success(data) {
@ -76,7 +80,7 @@ function ($q, $scope, $state, VolumeService, PluginService, ResourceControlServi
function initView() {
var apiVersion = $scope.applicationState.endpoint.apiVersion;
var endpointProvider = $scope.applicationState.endpoint.provider;
var endpointProvider = $scope.applicationState.endpoint.mode.provider;
PluginService.volumePlugins(apiVersion < 1.25 || endpointProvider === 'VMWARE_VIC')
.then(function success(data) {

View file

@ -6,7 +6,7 @@
</rd-header>
<div class="row">
<div class="col-lg-12 col-md-12 col-xs-12">
<div class="col-sm-12">
<rd-widget>
<rd-widget-body>
<form class="form-horizontal">
@ -70,6 +70,16 @@
<storidge-profile-selector storidge-profile="formValues.StoridgeProfile"></storidge-profile-selector>
</div>
<!-- storidge -->
<div ng-if="applicationState.endpoint.mode.agentProxy && formValues.Driver === 'local'">
<div class="col-sm-12 form-section-title">
Deployment
</div>
<!-- node-selection -->
<node-selector
model="formValues.NodeName">
</node-selector>
<!-- !node-selection -->
</div>
<!-- access-control -->
<por-access-control-form form-data="formValues.AccessControlData" ng-if="applicationState.application.authentication"></por-access-control-form>
<!-- !access-control -->

View file

@ -84,7 +84,7 @@
</thead>
<tbody>
<tr ng-repeat="container in containersUsingVolume">
<td><a ui-sref="docker.containers.container({id: container.Id})">{{ container | containername }}</a></td>
<td><a ui-sref="docker.containers.container({ id: container.Id, nodeName: container.NodeName })">{{ container | containername }}</a></td>
<td>{{ container.volumeData.Destination }}</td>
<td>{{ !container.volumeData.RW }}</td>
</tr>

View file

@ -1,6 +1,6 @@
angular.module('portainer.docker')
.controller('VolumeController', ['$scope', '$state', '$transition$', 'VolumeService', 'ContainerService', 'Notifications',
function ($scope, $state, $transition$, VolumeService, ContainerService, Notifications) {
.controller('VolumeController', ['$scope', '$state', '$transition$', 'VolumeService', 'ContainerService', 'Notifications', 'HttpRequestHelper',
function ($scope, $state, $transition$, VolumeService, ContainerService, Notifications, HttpRequestHelper) {
$scope.removeVolume = function removeVolume() {
VolumeService.remove($scope.volume)
@ -20,6 +20,7 @@ function ($scope, $state, $transition$, VolumeService, ContainerService, Notific
}
function initView() {
HttpRequestHelper.setPortainerAgentTargetHeader($transition$.params().nodeName);
VolumeService.volume($transition$.params().id)
.then(function success(data) {
var volume = data;

View file

@ -15,6 +15,7 @@
order-by="Id" show-text-filter="true"
remove-action="removeAction"
show-ownership-column="applicationState.application.authentication"
show-host-column="applicationState.endpoint.mode.agentProxy"
></volumes-datatable>
</div>
</div>

View file

@ -1,10 +1,11 @@
angular.module('portainer.docker')
.controller('VolumesController', ['$q', '$scope', '$state', 'VolumeService', 'ServiceService', 'VolumeHelper', 'Notifications',
function ($q, $scope, $state, VolumeService, ServiceService, VolumeHelper, Notifications) {
.controller('VolumesController', ['$q', '$scope', '$state', 'VolumeService', 'ServiceService', 'VolumeHelper', 'Notifications', 'HttpRequestHelper',
function ($q, $scope, $state, VolumeService, ServiceService, VolumeHelper, Notifications, HttpRequestHelper) {
$scope.removeAction = function (selectedItems) {
var actionCount = selectedItems.length;
angular.forEach(selectedItems, function (volume) {
HttpRequestHelper.setPortainerAgentTargetHeader(volume.NodeName);
VolumeService.remove(volume)
.then(function success() {
Notifications.success('Volume successfully removed', volume.Id);