From 275fcf558741aadcd056b54eb4639ffb8b9cf9db Mon Sep 17 00:00:00 2001 From: Chaim Lev-Ari Date: Mon, 8 Oct 2018 01:34:47 +0300 Subject: [PATCH 1/2] fix(volume-browser): move volume id to query params (#2338) --- app/agent/rest/browse.js | 10 +++++----- app/agent/services/volumeBrowserService.js | 8 ++++---- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/app/agent/rest/browse.js b/app/agent/rest/browse.js index b4e8d53e0..9496768eb 100644 --- a/app/agent/rest/browse.js +++ b/app/agent/rest/browse.js @@ -1,22 +1,22 @@ angular.module('portainer.agent') .factory('Browse', ['$resource', 'API_ENDPOINT_ENDPOINTS', 'EndpointProvider', function BrowseFactory($resource, API_ENDPOINT_ENDPOINTS, EndpointProvider) { 'use strict'; - return $resource(API_ENDPOINT_ENDPOINTS + '/:endpointId/docker/browse/:id/:action', { + return $resource(API_ENDPOINT_ENDPOINTS + '/:endpointId/docker/browse/:action', { endpointId: EndpointProvider.endpointID }, { ls: { - method: 'GET', isArray: true, params: { id: '@id', action: 'ls' } + method: 'GET', isArray: true, params: { action: 'ls' } }, get: { - method: 'GET', params: { id: '@id', action: 'get' }, + method: 'GET', params: { action: 'get' }, transformResponse: browseGetResponse }, delete: { - method: 'DELETE', params: { id: '@id', action: 'delete' } + method: 'DELETE', params: { action: 'delete' } }, rename: { - method: 'PUT', params: { id: '@id', action: 'rename' } + method: 'PUT', params: { action: 'rename' } } }); }]); diff --git a/app/agent/services/volumeBrowserService.js b/app/agent/services/volumeBrowserService.js index 22b020494..2a5c92f56 100644 --- a/app/agent/services/volumeBrowserService.js +++ b/app/agent/services/volumeBrowserService.js @@ -4,15 +4,15 @@ angular.module('portainer.agent') var service = {}; service.ls = function(volumeId, path) { - return Browse.ls({ 'id': volumeId, 'path': path }).$promise; + return Browse.ls({ volumeID: volumeId, path: path }).$promise; }; service.get = function(volumeId, path) { - return Browse.get({ 'id': volumeId, 'path': path }).$promise; + return Browse.get({ volumeID: volumeId, path: path }).$promise; }; service.delete = function(volumeId, path) { - return Browse.delete({ 'id': volumeId, 'path': path }).$promise; + return Browse.delete({ volumeID: volumeId, path: path }).$promise; }; service.rename = function(volumeId, path, newPath) { @@ -20,7 +20,7 @@ angular.module('portainer.agent') CurrentFilePath: path, NewFilePath: newPath }; - return Browse.rename({ 'id': volumeId }, payload).$promise; + return Browse.rename({ volumeID: volumeId }, payload).$promise; }; return service; From ca08b2fa2a14bcece593fe587535a80a724cb6a4 Mon Sep 17 00:00:00 2001 From: Chaim Lev-Ari Date: Mon, 8 Oct 2018 01:44:08 +0300 Subject: [PATCH 2/2] feat(host): replace engine view with host view (#2255) * feat(engine-details): remove old panels * feat(engine-details): add basic engine-details-panel component * feat(engine-details): pass details to the different components * feat(engine-details): replace host-view with host-overview * feat(engine-details): add commaseperated filter * feat(engine-details): add host-view container component * feat(engine-details): add host-details component * feat(engine-details): build host details object * feat(engine-details): format engine version * feat(engine-details): get details for one node * feat(engine-details): pass is-agent from view * feat(engine-details): replace old node view with a new component * feat(engine-details): add swarm-node-details component * feat(engine-details): remove isSwarm binding * feat(engine-details): remove node-details and include in parent * feat(engine-details): add labels-table component * feat(engine-details): add update node service * feat(engine-details): add update label functionality * style(engine-details): remove whitespaces * feat(engine-details): remove old node page * feat(engine-details): pass is agent to host details * feat(host-details): hide missing info * feat(host-details): update node availability * style(host-details): remove obsolete event object * feat(host-details): fix labels not sending * feat(host-details): remove flags for hiding data * feat(host-details): create mock call to server for agent host info * style(host-details): fix spelling mistake in filter's name * feat(host-details): get info from agent * feat(host-details): hide engine labels when empty * feat(node-details): move labels table and save button * feat(host-info): add different urls for refresh * feat(host-details): show disk/devices info for agent * feat(host-view): add loading indicator to devices-panel * feat(host-details): add loading indicator to disks panel * feat(host-details): show devices/disks on standalone agent * refactor(host-details): remove default value * refactor(host-details): remove redundant commaSeperated filter * refactor(host-details): remove unused functions * style(host-details): remove whitespace --- app/agent/rest/host.js | 15 ++ app/agent/services/agentService.js | 50 ++-- app/docker/__module.js | 14 +- .../dockerSidebarContent.html | 2 +- .../host-overview/host-overview.html | 17 ++ .../components/host-overview/host-overview.js | 12 + .../devices-panel/devices-panel.html | 31 +++ .../devices-panel/devices-panel.js | 7 + .../disks-panel/disks-panel.html | 31 +++ .../disks-panel/disks-panel.js | 7 + .../engine-details-panel.html | 37 +++ .../engine-details-panel.js | 7 + .../host-details-panel.html | 34 +++ .../host-details-panel/host-details-panel.js | 8 + .../node-availability-select-controller.js | 11 + .../node-availability-select.html | 8 + .../node-availability-select.js | 10 + .../node-labels-table-controller.js | 23 ++ .../node-labels-table/node-labels-table.html | 35 +++ .../node-labels-table/node-labels-table.js | 9 + .../swarm-node-details-panel-controller.js | 96 +++++++ .../swarm-node-details-panel.html | 75 ++++++ .../swarm-node-details-panel.js | 9 + app/docker/services/nodeService.js | 64 +++-- app/docker/views/engine/engine.html | 118 --------- app/docker/views/engine/engineController.js | 22 -- app/docker/views/host/host-view-controller.js | 70 +++++ app/docker/views/host/host-view.html | 8 + app/docker/views/host/host-view.js | 4 + app/docker/views/nodes/edit/node.html | 243 ------------------ app/docker/views/nodes/edit/nodeController.js | 96 ------- .../node-details-view-controller.js | 75 ++++++ .../nodes/node-details/node-details-view.html | 13 + .../nodes/node-details/node-details-view.js | 4 + 34 files changed, 737 insertions(+), 528 deletions(-) create mode 100644 app/agent/rest/host.js create mode 100644 app/docker/components/host-overview/host-overview.html create mode 100644 app/docker/components/host-overview/host-overview.js create mode 100644 app/docker/components/host-view-panels/devices-panel/devices-panel.html create mode 100644 app/docker/components/host-view-panels/devices-panel/devices-panel.js create mode 100644 app/docker/components/host-view-panels/disks-panel/disks-panel.html create mode 100644 app/docker/components/host-view-panels/disks-panel/disks-panel.js create mode 100644 app/docker/components/host-view-panels/engine-details-panel/engine-details-panel.html create mode 100644 app/docker/components/host-view-panels/engine-details-panel/engine-details-panel.js create mode 100644 app/docker/components/host-view-panels/host-details-panel/host-details-panel.html create mode 100644 app/docker/components/host-view-panels/host-details-panel/host-details-panel.js create mode 100644 app/docker/components/host-view-panels/node-availability-select/node-availability-select-controller.js create mode 100644 app/docker/components/host-view-panels/node-availability-select/node-availability-select.html create mode 100644 app/docker/components/host-view-panels/node-availability-select/node-availability-select.js create mode 100644 app/docker/components/host-view-panels/node-labels-table/node-labels-table-controller.js create mode 100644 app/docker/components/host-view-panels/node-labels-table/node-labels-table.html create mode 100644 app/docker/components/host-view-panels/node-labels-table/node-labels-table.js create mode 100644 app/docker/components/host-view-panels/swarm-node-details-panel/swarm-node-details-panel-controller.js create mode 100644 app/docker/components/host-view-panels/swarm-node-details-panel/swarm-node-details-panel.html create mode 100644 app/docker/components/host-view-panels/swarm-node-details-panel/swarm-node-details-panel.js delete mode 100644 app/docker/views/engine/engine.html delete mode 100644 app/docker/views/engine/engineController.js create mode 100644 app/docker/views/host/host-view-controller.js create mode 100644 app/docker/views/host/host-view.html create mode 100644 app/docker/views/host/host-view.js delete mode 100644 app/docker/views/nodes/edit/node.html delete mode 100644 app/docker/views/nodes/edit/nodeController.js create mode 100644 app/docker/views/nodes/node-details/node-details-view-controller.js create mode 100644 app/docker/views/nodes/node-details/node-details-view.html create mode 100644 app/docker/views/nodes/node-details/node-details-view.js diff --git a/app/agent/rest/host.js b/app/agent/rest/host.js new file mode 100644 index 000000000..a717def30 --- /dev/null +++ b/app/agent/rest/host.js @@ -0,0 +1,15 @@ +angular.module('portainer.agent').factory('Host', [ + '$resource', 'API_ENDPOINT_ENDPOINTS', 'EndpointProvider', + function AgentFactory($resource, API_ENDPOINT_ENDPOINTS, EndpointProvider) { + 'use strict'; + return $resource( + API_ENDPOINT_ENDPOINTS + '/:endpointId/docker/host/:action', + { + endpointId: EndpointProvider.endpointID + }, + { + info: { method: 'GET', params: { action: 'info' } } + } + ); + } +]); diff --git a/app/agent/services/agentService.js b/app/agent/services/agentService.js index 5f011d01c..7143e1e0f 100644 --- a/app/agent/services/agentService.js +++ b/app/agent/services/agentService.js @@ -1,24 +1,34 @@ -angular.module('portainer.agent') -.factory('AgentService', ['$q', 'Agent', function AgentServiceFactory($q, Agent) { - 'use strict'; - var service = {}; +angular.module('portainer.agent').factory('AgentService', [ + '$q', 'Agent','HttpRequestHelper', 'Host', + function AgentServiceFactory($q, Agent, HttpRequestHelper, Host) { + 'use strict'; + var service = {}; - service.agents = function() { - var deferred = $q.defer(); + service.agents = agents; + service.hostInfo = hostInfo; - Agent.query({}).$promise - .then(function success(data) { - var agents = data.map(function (item) { - return new AgentViewModel(item); - }); - deferred.resolve(agents); - }) - .catch(function error(err) { - deferred.reject({ msg: 'Unable to retrieve agents', err: err }); - }); + function hostInfo(nodeName) { + HttpRequestHelper.setPortainerAgentTargetHeader(nodeName); + return Host.info().$promise; + } - return deferred.promise; - }; + function agents() { + var deferred = $q.defer(); - return service; -}]); + Agent.query({}) + .$promise.then(function success(data) { + var agents = data.map(function(item) { + return new AgentViewModel(item); + }); + deferred.resolve(agents); + }) + .catch(function error(err) { + deferred.reject({ msg: 'Unable to retrieve agents', err: err }); + }); + + return deferred.promise; + } + + return service; + } +]); diff --git a/app/docker/__module.js b/app/docker/__module.js index ffd451fbd..23ce18433 100644 --- a/app/docker/__module.js +++ b/app/docker/__module.js @@ -129,13 +129,12 @@ angular.module('portainer.docker', ['portainer.app']) } }; - var engine = { - name: 'docker.engine', - url: '/engine', + var host = { + name: 'docker.host', + url: '/host', views: { 'content@': { - templateUrl: 'app/docker/views/engine/engine.html', - controller: 'EngineController' + component: 'hostView' } } }; @@ -239,8 +238,7 @@ angular.module('portainer.docker', ['portainer.app']) url: '/:id', views: { 'content@': { - templateUrl: 'app/docker/views/nodes/edit/node.html', - controller: 'NodeController' + component: 'nodeDetailsView' } } }; @@ -428,7 +426,7 @@ angular.module('portainer.docker', ['portainer.app']) $stateRegistryProvider.register(containerStats); $stateRegistryProvider.register(docker); $stateRegistryProvider.register(dashboard); - $stateRegistryProvider.register(engine); + $stateRegistryProvider.register(host); $stateRegistryProvider.register(events); $stateRegistryProvider.register(images); $stateRegistryProvider.register(image); diff --git a/app/docker/components/dockerSidebarContent/dockerSidebarContent.html b/app/docker/components/dockerSidebarContent/dockerSidebarContent.html index 870c959ed..2e0eeb497 100644 --- a/app/docker/components/dockerSidebarContent/dockerSidebarContent.html +++ b/app/docker/components/dockerSidebarContent/dockerSidebarContent.html @@ -35,5 +35,5 @@ Swarm diff --git a/app/docker/components/host-overview/host-overview.html b/app/docker/components/host-overview/host-overview.html new file mode 100644 index 000000000..baa43628e --- /dev/null +++ b/app/docker/components/host-overview/host-overview.html @@ -0,0 +1,17 @@ + + + + + + + Docker + + + + + + + + + + \ No newline at end of file diff --git a/app/docker/components/host-overview/host-overview.js b/app/docker/components/host-overview/host-overview.js new file mode 100644 index 000000000..add9515b4 --- /dev/null +++ b/app/docker/components/host-overview/host-overview.js @@ -0,0 +1,12 @@ +angular.module('portainer.docker').component('hostOverview', { + templateUrl: 'app/docker/components/host-overview/host-overview.html', + bindings: { + hostDetails: '<', + engineDetails: '<', + devices: '<', + disks: '<', + isAgent: '<', + refreshUrl: '@' + }, + transclude: true +}); diff --git a/app/docker/components/host-view-panels/devices-panel/devices-panel.html b/app/docker/components/host-view-panels/devices-panel/devices-panel.html new file mode 100644 index 000000000..5279f4ac1 --- /dev/null +++ b/app/docker/components/host-view-panels/devices-panel/devices-panel.html @@ -0,0 +1,31 @@ +
+
+ + + + + + + + + + + + + + + + + + + + + + +
NameVendor
{{device.Name}}{{device.Vendor}}
Loading...
+ No device available. +
+
+
+
+
\ No newline at end of file diff --git a/app/docker/components/host-view-panels/devices-panel/devices-panel.js b/app/docker/components/host-view-panels/devices-panel/devices-panel.js new file mode 100644 index 000000000..1bd32e1f6 --- /dev/null +++ b/app/docker/components/host-view-panels/devices-panel/devices-panel.js @@ -0,0 +1,7 @@ +angular.module('portainer.docker').component('devicesPanel', { + templateUrl: + 'app/docker/components/host-view-panels/devices-panel/devices-panel.html', + bindings: { + devices: '<' + } +}); diff --git a/app/docker/components/host-view-panels/disks-panel/disks-panel.html b/app/docker/components/host-view-panels/disks-panel/disks-panel.html new file mode 100644 index 000000000..632f07151 --- /dev/null +++ b/app/docker/components/host-view-panels/disks-panel/disks-panel.html @@ -0,0 +1,31 @@ +
+
+ + + + + + + + + + + + + + + + + + + + + + +
VendorSize
{{disk.Vendor}}{{disk.Size | humansize}}
Loading...
+ No disks available. +
+
+
+
+
\ No newline at end of file diff --git a/app/docker/components/host-view-panels/disks-panel/disks-panel.js b/app/docker/components/host-view-panels/disks-panel/disks-panel.js new file mode 100644 index 000000000..ae96224ee --- /dev/null +++ b/app/docker/components/host-view-panels/disks-panel/disks-panel.js @@ -0,0 +1,7 @@ +angular.module('portainer.docker').component('disksPanel', { + templateUrl: + 'app/docker/components/host-view-panels/disks-panel/disks-panel.html', + bindings: { + disks: '<' + } +}); diff --git a/app/docker/components/host-view-panels/engine-details-panel/engine-details-panel.html b/app/docker/components/host-view-panels/engine-details-panel/engine-details-panel.html new file mode 100644 index 000000000..4eebf6bcd --- /dev/null +++ b/app/docker/components/host-view-panels/engine-details-panel/engine-details-panel.html @@ -0,0 +1,37 @@ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Version{{ $ctrl.engine.releaseVersion }} (API: {{ $ctrl.engine.apiVersion }})
Root directory{{ $ctrl.engine.rootDirectory }}
Storage Driver{{ $ctrl.engine.storageDriver }}
Logging Driver{{ $ctrl.engine.loggingDriver }}
Volume Plugins{{ $ctrl.engine.volumePlugins | arraytostr: ', ' }}
Network Plugins{{ $ctrl.engine.networkPlugins | arraytostr: ', ' }}
+
+
+
+
\ No newline at end of file diff --git a/app/docker/components/host-view-panels/engine-details-panel/engine-details-panel.js b/app/docker/components/host-view-panels/engine-details-panel/engine-details-panel.js new file mode 100644 index 000000000..666dd5254 --- /dev/null +++ b/app/docker/components/host-view-panels/engine-details-panel/engine-details-panel.js @@ -0,0 +1,7 @@ +angular.module('portainer.docker').component('engineDetailsPanel', { + templateUrl: + 'app/docker/components/host-view-panels/engine-details-panel/engine-details-panel.html', + bindings: { + engine: '<' + } +}); diff --git a/app/docker/components/host-view-panels/host-details-panel/host-details-panel.html b/app/docker/components/host-view-panels/host-details-panel/host-details-panel.html new file mode 100644 index 000000000..2ea05f88f --- /dev/null +++ b/app/docker/components/host-view-panels/host-details-panel/host-details-panel.html @@ -0,0 +1,34 @@ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + +
Hostname{{ $ctrl.host.name }}
OS Information{{ $ctrl.host.os.type }} {{$ctrl.host.os.arch}} + {{$ctrl.host.os.name}}
Kernel Version{{ $ctrl.host.kernelVersion }}
Total CPU{{ $ctrl.host.totalCPU }}
Total memory{{ $ctrl.host.totalMemory | humansize }}
+
+
+
+
\ No newline at end of file diff --git a/app/docker/components/host-view-panels/host-details-panel/host-details-panel.js b/app/docker/components/host-view-panels/host-details-panel/host-details-panel.js new file mode 100644 index 000000000..6865b5872 --- /dev/null +++ b/app/docker/components/host-view-panels/host-details-panel/host-details-panel.js @@ -0,0 +1,8 @@ +angular.module('portainer.docker').component('hostDetailsPanel', { + templateUrl: + 'app/docker/components/host-view-panels/host-details-panel/host-details-panel.html', + bindings: { + host: '<', + isAgent: '<' + } +}); diff --git a/app/docker/components/host-view-panels/node-availability-select/node-availability-select-controller.js b/app/docker/components/host-view-panels/node-availability-select/node-availability-select-controller.js new file mode 100644 index 000000000..52df40cd6 --- /dev/null +++ b/app/docker/components/host-view-panels/node-availability-select/node-availability-select-controller.js @@ -0,0 +1,11 @@ +angular + .module('portainer.docker') + .controller('NodeAvailabilitySelectController', [ + function NodeAvailabilitySelectController() { + this.onChange = onChange; + + function onChange() { + this.onSave({ availability: this.availability }); + } + } + ]); diff --git a/app/docker/components/host-view-panels/node-availability-select/node-availability-select.html b/app/docker/components/host-view-panels/node-availability-select/node-availability-select.html new file mode 100644 index 000000000..94e086127 --- /dev/null +++ b/app/docker/components/host-view-panels/node-availability-select/node-availability-select.html @@ -0,0 +1,8 @@ +
+ +
\ No newline at end of file diff --git a/app/docker/components/host-view-panels/node-availability-select/node-availability-select.js b/app/docker/components/host-view-panels/node-availability-select/node-availability-select.js new file mode 100644 index 000000000..b09730f37 --- /dev/null +++ b/app/docker/components/host-view-panels/node-availability-select/node-availability-select.js @@ -0,0 +1,10 @@ +angular.module('portainer.docker').component('nodeAvailabilitySelect', { + templateUrl: + 'app/docker/components/host-view-panels/node-availability-select/node-availability-select.html', + controller: 'NodeAvailabilitySelectController', + bindings: { + availability: '<', + originalValue: '<', + onSave: '&' + } +}); diff --git a/app/docker/components/host-view-panels/node-labels-table/node-labels-table-controller.js b/app/docker/components/host-view-panels/node-labels-table/node-labels-table-controller.js new file mode 100644 index 000000000..a9ad8ab58 --- /dev/null +++ b/app/docker/components/host-view-panels/node-labels-table/node-labels-table-controller.js @@ -0,0 +1,23 @@ +angular.module('portainer.docker').controller('NodeLabelsTableController', [ + function NodeLabelsTableController() { + var ctrl = this; + ctrl.removeLabel = removeLabel; + ctrl.updateLabel = updateLabel; + + function removeLabel(index) { + var label = ctrl.labels.splice(index, 1); + if (label !== null) { + ctrl.onChangedLabels({ labels: ctrl.labels }); + } + } + + function updateLabel(label) { + if ( + label.value !== label.originalValue || + label.key !== label.originalKey + ) { + ctrl.onChangedLabels({ labels: ctrl.labels }); + } + } + } +]); diff --git a/app/docker/components/host-view-panels/node-labels-table/node-labels-table.html b/app/docker/components/host-view-panels/node-labels-table/node-labels-table.html new file mode 100644 index 000000000..86eee9356 --- /dev/null +++ b/app/docker/components/host-view-panels/node-labels-table/node-labels-table.html @@ -0,0 +1,35 @@ +
+ There are no labels for this node. +
+ + + + + + + + + + + + + + +
LabelValue
+
+ Name + +
+
+
+ Value + + + + +
+
\ No newline at end of file diff --git a/app/docker/components/host-view-panels/node-labels-table/node-labels-table.js b/app/docker/components/host-view-panels/node-labels-table/node-labels-table.js new file mode 100644 index 000000000..5d6d6c320 --- /dev/null +++ b/app/docker/components/host-view-panels/node-labels-table/node-labels-table.js @@ -0,0 +1,9 @@ +angular.module('portainer.docker').component('nodeLabelsTable', { + templateUrl: + 'app/docker/components/host-view-panels/node-labels-table/node-labels-table.html', + controller: 'NodeLabelsTableController', + bindings: { + labels: '<', + onChangedLabels: '&' + } +}); diff --git a/app/docker/components/host-view-panels/swarm-node-details-panel/swarm-node-details-panel-controller.js b/app/docker/components/host-view-panels/swarm-node-details-panel/swarm-node-details-panel-controller.js new file mode 100644 index 000000000..65e376d99 --- /dev/null +++ b/app/docker/components/host-view-panels/swarm-node-details-panel/swarm-node-details-panel-controller.js @@ -0,0 +1,96 @@ +angular + .module('portainer.docker') + .controller('SwarmNodeDetailsPanelController', [ + 'NodeService', 'LabelHelper', 'Notifications', '$state', + function SwarmNodeDetailsPanelController(NodeService, LabelHelper, Notifications, $state) { + var ctrl = this; + ctrl.state = { + managerAddress: '', + hasChanges: false + }; + ctrl.$onChanges = $onChanges; + ctrl.addLabel = addLabel; + ctrl.updateNodeLabels = updateNodeLabels; + ctrl.updateNodeAvailability = updateNodeAvailability; + ctrl.saveChanges = saveChanges; + ctrl.cancelChanges = cancelChanges; + + var managerRole = 'manager'; + + function $onChanges() { + if (!ctrl.details) { + return; + } + if (ctrl.details.role === managerRole) { + ctrl.state.managerAddress = '(' + ctrl.details.managerAddress + ')'; + } + } + + function addLabel() { + ctrl.details.nodeLabels.push({ + key: '', + value: '', + originalValue: '', + originalKey: '' + }); + } + + function updateNodeLabels(labels) { + ctrl.details.nodeLabels = labels; + ctrl.state.hasChanges = true; + } + + function updateNodeAvailability(availability) { + ctrl.details.availability = availability; + ctrl.state.hasChanges = true; + } + + function saveChanges() { + var originalNode = ctrl.originalNode; + var config = { + Name: originalNode.Name, + Availability: ctrl.details.availability, + Role: originalNode.Role, + Labels: LabelHelper.fromKeyValueToLabelHash(ctrl.details.nodeLabels), + Id: originalNode.Id, + Version: originalNode.Version + }; + + NodeService.updateNode(config) + .then(onUpdateSuccess) + .catch(notifyOnError); + + function onUpdateSuccess() { + Notifications.success('Node successfully updated', 'Node updated'); + $state.go( + 'docker.nodes.node', + { id: originalNode.Id }, + { reload: true } + ); + } + + function notifyOnError(error) { + Notifications.error('Failure', error, 'Failed to update node'); + } + } + + function cancelChanges() { + cancelLabelChanges(); + ctrl.details.availability = ctrl.originalNode.Availability; + ctrl.state.hasChanges = false; + } + + function cancelLabelChanges() { + ctrl.details.nodeLabels = ctrl.details.nodeLabels + .filter(function(label) { + return label.originalValue || label.originalKey; + }) + .map(function(label) { + return Object.assign(label, { + value: label.originalValue, + key: label.originalKey + }); + }); + } + } + ]); diff --git a/app/docker/components/host-view-panels/swarm-node-details-panel/swarm-node-details-panel.html b/app/docker/components/host-view-panels/swarm-node-details-panel/swarm-node-details-panel.html new file mode 100644 index 000000000..291025e13 --- /dev/null +++ b/app/docker/components/host-view-panels/swarm-node-details-panel/swarm-node-details-panel.html @@ -0,0 +1,75 @@ +
+
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Node name{{ $ctrl.details.name }}
Role{{ $ctrl.details.role }} {{ $ctrl.state.managerAddress }}
Availability + + +
Status{{ + $ctrl.details.status }}
Engine Labels{{ $ctrl.details.engineLabels | arraytostr:', ' }}
+ + Node Labels +
+ +
+ +
+ +
+
+
+
\ No newline at end of file diff --git a/app/docker/components/host-view-panels/swarm-node-details-panel/swarm-node-details-panel.js b/app/docker/components/host-view-panels/swarm-node-details-panel/swarm-node-details-panel.js new file mode 100644 index 000000000..7eea3e708 --- /dev/null +++ b/app/docker/components/host-view-panels/swarm-node-details-panel/swarm-node-details-panel.js @@ -0,0 +1,9 @@ +angular.module('portainer.docker').component('swarmNodeDetailsPanel', { + templateUrl: + 'app/docker/components/host-view-panels/swarm-node-details-panel/swarm-node-details-panel.html', + controller: 'SwarmNodeDetailsPanelController', + bindings: { + details: '<', + originalNode: '<' + } +}); diff --git a/app/docker/services/nodeService.js b/app/docker/services/nodeService.js index 706f26647..5ebfeeee4 100644 --- a/app/docker/services/nodeService.js +++ b/app/docker/services/nodeService.js @@ -1,24 +1,48 @@ -angular.module('portainer.docker') -.factory('NodeService', ['$q', 'Node', function NodeServiceFactory($q, Node) { - 'use strict'; - var service = {}; +angular.module('portainer.docker').factory('NodeService', [ + '$q', 'Node', + function NodeServiceFactory($q, Node) { + 'use strict'; + var service = {}; - service.nodes = function() { - var deferred = $q.defer(); + service.nodes = nodes; + service.node = node; + service.updateNode = updateNode; - Node.query({}).$promise - .then(function success(data) { - var nodes = data.map(function (item) { - return new NodeViewModel(item); - }); - deferred.resolve(nodes); - }) - .catch(function error(err) { - deferred.reject({ msg: 'Unable to retrieve nodes', err: err }); - }); + function node(id) { + var deferred = $q.defer(); + Node.get({ id: id }) + .$promise.then(function onNodeLoaded(rawNode) { + var node = new NodeViewModel(rawNode); + return deferred.resolve(node); + }) + .catch(function onFailed(err) { + deferred.reject({ msg: 'Unable to retrieve node', err: err }); + }); - return deferred.promise; - }; + return deferred.promise; + } - return service; -}]); + function nodes() { + var deferred = $q.defer(); + + Node.query({}) + .$promise.then(function success(data) { + var nodes = data.map(function(item) { + return new NodeViewModel(item); + }); + deferred.resolve(nodes); + }) + .catch(function error(err) { + deferred.reject({ msg: 'Unable to retrieve nodes', err: err }); + }); + + return deferred.promise; + } + + function updateNode(node) { + return Node.update({ id: node.Id, version: node.Version }, node).$promise; + } + + return service; + } +]); diff --git a/app/docker/views/engine/engine.html b/app/docker/views/engine/engine.html deleted file mode 100644 index 73b1248b2..000000000 --- a/app/docker/views/engine/engine.html +++ /dev/null @@ -1,118 +0,0 @@ - - - - - - - Docker - - -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Version{{ version.Version }}
API version{{ version.ApiVersion }}
Go version{{ version.GoVersion }}
OS type{{ version.Os }}
OS{{ info.OperatingSystem }}
Architecture{{ version.Arch }}
Kernel version{{ version.KernelVersion }}
-
-
-
-
- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
Total CPU{{ info.NCPU }}
Total memory{{ info.MemTotal|humansize }}
Docker root directory{{ info.DockerRootDir }}
Storage driver{{ info.Driver }}
Logging driver{{ info.LoggingDriver }}
Cgroup driver{{ info.CgroupDriver }}
Execution driver{{ info.ExecutionDriver }}
-
-
-
-
- -
-
- - - - - - - - - - - - - - - - - - -
Volume{{ info.Plugins.Volume|arraytostr: ', '}}
Network{{ info.Plugins.Network|arraytostr: ', '}}
Authorization{{ info.Plugins.Authorization|arraytostr: ', '}}
-
-
-
-
diff --git a/app/docker/views/engine/engineController.js b/app/docker/views/engine/engineController.js deleted file mode 100644 index f106273a5..000000000 --- a/app/docker/views/engine/engineController.js +++ /dev/null @@ -1,22 +0,0 @@ -angular.module('portainer.docker') -.controller('EngineController', ['$q', '$scope', 'SystemService', 'Notifications', -function ($q, $scope, SystemService, Notifications) { - - function initView() { - $q.all({ - version: SystemService.version(), - info: SystemService.info() - }) - .then(function success(data) { - $scope.version = data.version; - $scope.info = data.info; - }) - .catch(function error(err) { - $scope.info = {}; - $scope.version = {}; - Notifications.error('Failure', err, 'Unable to retrieve engine details'); - }); - } - - initView(); -}]); diff --git a/app/docker/views/host/host-view-controller.js b/app/docker/views/host/host-view-controller.js new file mode 100644 index 000000000..43bc49391 --- /dev/null +++ b/app/docker/views/host/host-view-controller.js @@ -0,0 +1,70 @@ +angular.module('portainer.docker').controller('HostViewController', [ + '$q', 'SystemService', 'Notifications', 'StateManager', 'AgentService', + function HostViewController($q, SystemService, Notifications, StateManager, AgentService) { + var ctrl = this; + this.$onInit = initView; + + ctrl.state = { + isAgent: false + }; + + this.engineDetails = {}; + this.hostDetails = {}; + + function initView() { + var applicationState = StateManager.getState(); + ctrl.state.isAgent = applicationState.endpoint.mode.agentProxy; + + $q.all({ + version: SystemService.version(), + info: SystemService.info() + }) + .then(function success(data) { + ctrl.engineDetails = buildEngineDetails(data); + ctrl.hostDetails = buildHostDetails(data.info); + + if (ctrl.state.isAgent) { + return AgentService.hostInfo(data.info.Hostname).then(function onHostInfoLoad(agentHostInfo) { + ctrl.devices = agentHostInfo.PCIDevices; + ctrl.disks = agentHostInfo.PhysicalDisks; + }); + } + }) + .catch(function error(err) { + Notifications.error( + 'Failure', + err, + 'Unable to retrieve engine details' + ); + }); + } + + function buildEngineDetails(data) { + var versionDetails = data.version; + var info = data.info; + return { + releaseVersion: versionDetails.Version, + apiVersion: versionDetails.ApiVersion, + rootDirectory: info.DockerRootDir, + storageDriver: info.Driver, + loggingDriver: info.LoggingDriver, + volumePlugins: info.Plugins.Volume, + networkPlugins: info.Plugins.Network + }; + } + + function buildHostDetails(info) { + return { + os: { + arch: info.Architecture, + type: info.OSType, + name: info.OperatingSystem + }, + name: info.Name, + kernelVersion: info.KernelVersion, + totalCPU: info.NCPU, + totalMemory: info.MemTotal + }; + } + } +]); diff --git a/app/docker/views/host/host-view.html b/app/docker/views/host/host-view.html new file mode 100644 index 000000000..419e96995 --- /dev/null +++ b/app/docker/views/host/host-view.html @@ -0,0 +1,8 @@ + \ No newline at end of file diff --git a/app/docker/views/host/host-view.js b/app/docker/views/host/host-view.js new file mode 100644 index 000000000..e321dccb4 --- /dev/null +++ b/app/docker/views/host/host-view.js @@ -0,0 +1,4 @@ +angular.module('portainer.docker').component('hostView', { + templateUrl: 'app/docker/views/host/host-view.html', + controller: 'HostViewController' +}); diff --git a/app/docker/views/nodes/edit/node.html b/app/docker/views/nodes/edit/node.html deleted file mode 100644 index b7d733206..000000000 --- a/app/docker/views/nodes/edit/node.html +++ /dev/null @@ -1,243 +0,0 @@ - - - - - - - - Swarm nodes > {{ node.Hostname }} - - - -
-
-
- Loading... -
- - - - -

It looks like the node you wish to inspect does not exist.

-
-
-
-
- -
-
- - - - - - - - - - - - - - - - - - - - - - - - - - -
Name - -
Host name{{ node.Hostname }}
Role{{ node.Role }}
Availability -
- -
-
Status{{ node.Status }}
-
- -

- View the Docker Swarm mode Node documentation here. -

- -
-
-
-
- -
-
- - - - - - - - - - - - - - - - - - -
Leader - Yes - No -
Reachability{{ node.Reachability }}
Manager address{{ node.ManagerAddr }}
-
-
-
-
- -
-
- - - - - - - - - - - - - - - - - - - - - - -
CPU{{ node.CPUs / 1000000000 }}
Memory{{ node.Memory|humansize: 2 }}
Platform{{ node.PlatformOS }} {{ node.PlatformArchitecture }}
Docker Engine version{{ node.EngineVersion }}
-
-
-
-
- -
-
- - - -

There are no engine labels for this node.

-
- - - - - - - - - - - - - - -
LabelValue
{{ engineLabel.key }}{{ engineLabel.value }}
-
-
-
-
- -
-
- - - - - -

There are no labels for this node.

-
- - - - - - - - - - - - - - -
LabelValue
-
- name - -
-
-
- value - - - - -
-
-
- - - -
-
-
- -
-
- -
-
diff --git a/app/docker/views/nodes/edit/nodeController.js b/app/docker/views/nodes/edit/nodeController.js deleted file mode 100644 index 30f210b2a..000000000 --- a/app/docker/views/nodes/edit/nodeController.js +++ /dev/null @@ -1,96 +0,0 @@ -angular.module('portainer.docker') -.controller('NodeController', ['$scope', '$state', '$transition$', 'LabelHelper', 'Node', 'NodeHelper', 'Task', 'Notifications', -function ($scope, $state, $transition$, LabelHelper, Node, NodeHelper, Task, Notifications) { - - $scope.loading = true; - $scope.tasks = []; - - var originalNode = {}; - var editedKeys = []; - - $scope.updateNodeAttribute = function updateNodeAttribute(node, key) { - editedKeys.push(key); - }; - $scope.addLabel = function addLabel(node) { - node.Labels.push({ key: '', value: '', originalValue: '', originalKey: '' }); - $scope.updateNodeAttribute(node, 'Labels'); - }; - $scope.removeLabel = function removeLabel(node, index) { - var removedElement = node.Labels.splice(index, 1); - if (removedElement !== null) { - $scope.updateNodeAttribute(node, 'Labels'); - } - }; - $scope.updateLabel = function updateLabel(node, label) { - if (label.value !== label.originalValue || label.key !== label.originalKey) { - $scope.updateNodeAttribute(node, 'Labels'); - } - }; - - $scope.hasChanges = function(node, elements) { - if (!elements) { - elements = Object.keys(originalNode); - } - var hasChanges = false; - elements.forEach(function(key) { - hasChanges = hasChanges || ((editedKeys.indexOf(key) >= 0) && node[key] !== originalNode[key]); - }); - return hasChanges; - }; - - $scope.cancelChanges = function(node) { - editedKeys.forEach(function(key) { - node[key] = originalNode[key]; - }); - editedKeys = []; - }; - - $scope.updateNode = function updateNode(node) { - var config = NodeHelper.nodeToConfig(node.Model); - config.Name = node.Name; - config.Availability = node.Availability; - config.Role = node.Role; - config.Labels = LabelHelper.fromKeyValueToLabelHash(node.Labels); - - Node.update({ id: node.Id, version: node.Version }, config, function () { - Notifications.success('Node successfully updated', 'Node updated'); - $state.go('docker.nodes.node', {id: node.Id}, {reload: true}); - }, function (e) { - Notifications.error('Failure', e, 'Failed to update node'); - }); - }; - - function loadNodeAndTasks() { - $scope.loading = true; - if ($scope.applicationState.endpoint.mode.provider === 'DOCKER_SWARM_MODE') { - Node.get({ id: $transition$.params().id}, function(d) { - if (d.message) { - Notifications.error('Failure', e, 'Unable to inspect the node'); - } else { - var node = new NodeViewModel(d); - originalNode = angular.copy(node); - $scope.node = node; - getTasks(d); - } - $scope.loading = false; - }); - } else { - $scope.loading = false; - } - } - - function getTasks(node) { - if (node) { - Task.query({filters: {node: [node.ID]}}, function (tasks) { - $scope.tasks = tasks.map(function (task) { - return new TaskViewModel(task); - }); - }, function (e) { - Notifications.error('Failure', e, 'Unable to retrieve tasks associated to the node'); - }); - } - } - - loadNodeAndTasks(); - -}]); diff --git a/app/docker/views/nodes/node-details/node-details-view-controller.js b/app/docker/views/nodes/node-details/node-details-view-controller.js new file mode 100644 index 000000000..cde111986 --- /dev/null +++ b/app/docker/views/nodes/node-details/node-details-view-controller.js @@ -0,0 +1,75 @@ +angular.module('portainer.docker').controller('NodeDetailsViewController', [ + '$stateParams', 'NodeService', 'StateManager', 'AgentService', + function NodeDetailsViewController($stateParams, NodeService, StateManager, AgentService) { + var ctrl = this; + + ctrl.$onInit = initView; + + ctrl.state = { + isAgent: false + }; + + function initView() { + var applicationState = StateManager.getState(); + ctrl.state.isAgent = applicationState.endpoint.mode.agentProxy; + + var nodeId = $stateParams.id; + NodeService.node(nodeId).then(function(node) { + ctrl.originalNode = node; + ctrl.hostDetails = buildHostDetails(node); + ctrl.engineDetails = buildEngineDetails(node); + ctrl.nodeDetails = buildNodeDetails(node); + if (ctrl.state.isAgent) { + AgentService.hostInfo(node.Hostname).then(function onHostInfoLoad( + agentHostInfo + ) { + ctrl.devices = agentHostInfo.PCIDevices; + ctrl.disks = agentHostInfo.PhysicalDisks; + }); + } + }); + } + + function buildHostDetails(node) { + return { + os: { + arch: node.PlatformArchitecture, + type: node.PlatformOS + }, + name: node.Hostname, + totalCPU: node.CPUs / 1e9, + totalMemory: node.Memory + }; + } + + function buildEngineDetails(node) { + return { + releaseVersion: node.EngineVersion, + volumePlugins: transformPlugins(node.Plugins, 'Volume'), + networkPlugins: transformPlugins(node.Plugins, 'Network') + }; + } + + function buildNodeDetails(node) { + return { + name: node.Name, + role: node.Role, + managerAddress: node.ManagerAddr, + availability: node.Availability, + status: node.Status, + engineLabels: node.EngineLabels, + nodeLabels: node.Labels + }; + } + + function transformPlugins(pluginsList, type) { + return pluginsList + .filter(function(plugin) { + return plugin.Type === type; + }) + .map(function(plugin) { + return plugin.Name; + }); + } + } +]); diff --git a/app/docker/views/nodes/node-details/node-details-view.html b/app/docker/views/nodes/node-details/node-details-view.html new file mode 100644 index 000000000..13df99558 --- /dev/null +++ b/app/docker/views/nodes/node-details/node-details-view.html @@ -0,0 +1,13 @@ + + + \ No newline at end of file diff --git a/app/docker/views/nodes/node-details/node-details-view.js b/app/docker/views/nodes/node-details/node-details-view.js new file mode 100644 index 000000000..5b1c76e2b --- /dev/null +++ b/app/docker/views/nodes/node-details/node-details-view.js @@ -0,0 +1,4 @@ +angular.module('portainer.docker').component('nodeDetailsView', { + templateUrl: 'app/docker/views/nodes/node-details/node-details-view.html', + controller: 'NodeDetailsViewController' +});