From 9813099aa416311e7c6bdd7a037375e953c39ff5 Mon Sep 17 00:00:00 2001 From: Chaim Lev-Ari Date: Fri, 26 Oct 2018 06:16:29 +0300 Subject: [PATCH] feat(app): toggle features based on agent API version (#2378) * feat(agent): get agent's version from ping * feat(agent): add version to api url * feat(agent): query agent with api version * feat(agent): rename agent api version name on state * feat(agent): disable feature based on agent's api version * style(agent): rename ping rest service + remove whitespaces * style(state): remove whitespace * style(agent): add whitespace * fix(agent): remove check for error status 403 * refactor(agent): rename ping file name * refactor(agent): move old services to v1 folder * refactor(agent): turn ping service to usual pattern * refactor(agent): change version to a global variable * refactor(agent): move ping to version2 * refactor(agent): restore ping to use root ping * fix(volumes): add volumeID to browse api path * feat(volume): add upload button to volume browser --- .../volume-browser/volume-browser.js | 3 +- .../volume-browser/volumeBrowser.html | 3 ++ .../volume-browser/volumeBrowserController.js | 20 ++++++++ app/agent/rest/agent.js | 8 ++-- app/agent/rest/browse.js | 8 ++-- app/agent/rest/host.js | 9 ++-- app/agent/rest/ping.js | 33 +++++++++++++ app/agent/rest/v1/agent.js | 10 ++++ app/agent/rest/v1/browse.js | 22 +++++++++ app/agent/services/agentService.js | 14 ++++-- app/agent/services/hostBrowserService.js | 16 +++++-- app/agent/services/pingService.js | 14 ++++++ app/agent/services/volumeBrowserService.js | 47 ++++++++++++++++--- .../host-overview/host-overview.html | 6 +-- .../components/host-overview/host-overview.js | 1 + .../host-details-panel.html | 2 +- .../host-details-panel/host-details-panel.js | 2 +- app/docker/views/host/host-view-controller.js | 6 ++- app/docker/views/host/host-view.html | 1 + .../node-details-view-controller.js | 16 ++++--- .../nodes/node-details/node-details-view.html | 1 + .../volumes/browse/browseVolumeController.js | 5 +- .../views/volumes/browse/browsevolume.html | 2 + app/portainer/services/stateManager.js | 16 ++++++- 24 files changed, 224 insertions(+), 41 deletions(-) create mode 100644 app/agent/rest/ping.js create mode 100644 app/agent/rest/v1/agent.js create mode 100644 app/agent/rest/v1/browse.js create mode 100644 app/agent/services/pingService.js diff --git a/app/agent/components/volume-browser/volume-browser.js b/app/agent/components/volume-browser/volume-browser.js index 964803da0..78e963604 100644 --- a/app/agent/components/volume-browser/volume-browser.js +++ b/app/agent/components/volume-browser/volume-browser.js @@ -3,6 +3,7 @@ angular.module('portainer.agent').component('volumeBrowser', { controller: 'VolumeBrowserController', bindings: { volumeId: '<', - nodeName: '<' + nodeName: '<', + isUploadEnabled: '<' } }); diff --git a/app/agent/components/volume-browser/volumeBrowser.html b/app/agent/components/volume-browser/volumeBrowser.html index 3759f22d2..97c8a4da6 100644 --- a/app/agent/components/volume-browser/volumeBrowser.html +++ b/app/agent/components/volume-browser/volumeBrowser.html @@ -8,4 +8,7 @@ rename="$ctrl.rename(name, newName)" download="$ctrl.download(name)" delete="$ctrl.delete(name)" + + is-upload-allowed="$ctrl.isUploadEnabled" + on-file-selected-for-upload="$ctrl.onFileSelectedForUpload" > diff --git a/app/agent/components/volume-browser/volumeBrowserController.js b/app/agent/components/volume-browser/volumeBrowserController.js index 2fa4426b9..8db735a3b 100644 --- a/app/agent/components/volume-browser/volumeBrowserController.js +++ b/app/agent/components/volume-browser/volumeBrowserController.js @@ -84,6 +84,16 @@ function (HttpRequestHelper, VolumeBrowserService, FileSaver, Blob, ModalService }); } + this.onFileSelectedForUpload = function onFileSelectedForUpload(file) { + VolumeBrowserService.upload(ctrl.state.path, file, ctrl.volumeId) + .then(function onFileUpload() { + onFileUploaded(); + }) + .catch(function onFileUpload(err) { + Notifications.error('Failure', err, 'Unable to upload file'); + }); + }; + function parentPath(path) { if (path.lastIndexOf('/') === 0) { return '/'; @@ -112,4 +122,14 @@ function (HttpRequestHelper, VolumeBrowserService, FileSaver, Blob, ModalService }); }; + function onFileUploaded() { + refreshList(); + } + + function refreshList() { + browse(ctrl.state.path); + } + + + }]); diff --git a/app/agent/rest/agent.js b/app/agent/rest/agent.js index a7800717c..522296c3a 100644 --- a/app/agent/rest/agent.js +++ b/app/agent/rest/agent.js @@ -1,8 +1,10 @@ angular.module('portainer.agent') -.factory('Agent', ['$resource', 'API_ENDPOINT_ENDPOINTS', 'EndpointProvider', function AgentFactory($resource, API_ENDPOINT_ENDPOINTS, EndpointProvider) { +.factory('Agent', ['$resource', 'API_ENDPOINT_ENDPOINTS', 'EndpointProvider', 'StateManager', + function AgentFactory($resource, API_ENDPOINT_ENDPOINTS, EndpointProvider, StateManager) { 'use strict'; - return $resource(API_ENDPOINT_ENDPOINTS + '/:endpointId/docker/agents', { - endpointId: EndpointProvider.endpointID + return $resource(API_ENDPOINT_ENDPOINTS + '/:endpointId/docker/v:version/agents', { + endpointId: EndpointProvider.endpointID, + version: StateManager.getAgentApiVersion }, { query: { method: 'GET', isArray: true } diff --git a/app/agent/rest/browse.js b/app/agent/rest/browse.js index 9496768eb..e78cd3206 100644 --- a/app/agent/rest/browse.js +++ b/app/agent/rest/browse.js @@ -1,8 +1,10 @@ angular.module('portainer.agent') -.factory('Browse', ['$resource', 'API_ENDPOINT_ENDPOINTS', 'EndpointProvider', function BrowseFactory($resource, API_ENDPOINT_ENDPOINTS, EndpointProvider) { +.factory('Browse', ['$resource', 'API_ENDPOINT_ENDPOINTS', 'EndpointProvider', 'StateManager', + function BrowseFactory($resource, API_ENDPOINT_ENDPOINTS, EndpointProvider, StateManager) { 'use strict'; - return $resource(API_ENDPOINT_ENDPOINTS + '/:endpointId/docker/browse/:action', { - endpointId: EndpointProvider.endpointID + return $resource(API_ENDPOINT_ENDPOINTS + '/:endpointId/docker/v:version/browse/:action', { + endpointId: EndpointProvider.endpointID, + version: StateManager.getAgentApiVersion }, { ls: { diff --git a/app/agent/rest/host.js b/app/agent/rest/host.js index a717def30..f184d2544 100644 --- a/app/agent/rest/host.js +++ b/app/agent/rest/host.js @@ -1,11 +1,12 @@ angular.module('portainer.agent').factory('Host', [ - '$resource', 'API_ENDPOINT_ENDPOINTS', 'EndpointProvider', - function AgentFactory($resource, API_ENDPOINT_ENDPOINTS, EndpointProvider) { + '$resource', 'API_ENDPOINT_ENDPOINTS', 'EndpointProvider', 'StateManager', + function AgentFactory($resource, API_ENDPOINT_ENDPOINTS, EndpointProvider, StateManager) { 'use strict'; return $resource( - API_ENDPOINT_ENDPOINTS + '/:endpointId/docker/host/:action', + API_ENDPOINT_ENDPOINTS + '/:endpointId/docker/v:version/host/:action', { - endpointId: EndpointProvider.endpointID + endpointId: EndpointProvider.endpointID, + version: StateManager.getAgentApiVersion }, { info: { method: 'GET', params: { action: 'info' } } diff --git a/app/agent/rest/ping.js b/app/agent/rest/ping.js new file mode 100644 index 000000000..7eeb93f2e --- /dev/null +++ b/app/agent/rest/ping.js @@ -0,0 +1,33 @@ +angular.module('portainer.agent').factory('AgentPing', [ + '$resource', 'API_ENDPOINT_ENDPOINTS', 'EndpointProvider', '$q', + function AgentPingFactory($resource, API_ENDPOINT_ENDPOINTS, EndpointProvider, $q) { + 'use strict'; + return $resource( + API_ENDPOINT_ENDPOINTS + '/:endpointId/docker/ping', + { + endpointId: EndpointProvider.endpointID + }, + { + ping: { + method: 'GET', + interceptor: { + response: function versionInterceptor(response) { + var instance = response.resource; + var version = + response.headers('Portainer-Agent-Api-Version') || 1; + instance.version = Number(version); + return instance; + }, + responseError: function versionResponseError(error) { + // 404 - agent is up - set version to 1 + if (error.status === 404) { + return { version: 1 }; + } + return $q.reject(error); + } + } + } + } + ); + } +]); diff --git a/app/agent/rest/v1/agent.js b/app/agent/rest/v1/agent.js new file mode 100644 index 000000000..a78755b35 --- /dev/null +++ b/app/agent/rest/v1/agent.js @@ -0,0 +1,10 @@ +angular.module('portainer.agent') +.factory('AgentVersion1', ['$resource', 'API_ENDPOINT_ENDPOINTS', 'EndpointProvider', function AgentFactory($resource, API_ENDPOINT_ENDPOINTS, EndpointProvider) { + 'use strict'; + return $resource(API_ENDPOINT_ENDPOINTS + '/:endpointId/docker/agents', { + endpointId: EndpointProvider.endpointID + }, + { + query: { method: 'GET', isArray: true } + }); +}]); diff --git a/app/agent/rest/v1/browse.js b/app/agent/rest/v1/browse.js new file mode 100644 index 000000000..d576433fa --- /dev/null +++ b/app/agent/rest/v1/browse.js @@ -0,0 +1,22 @@ +angular.module('portainer.agent') +.factory('BrowseVersion1', ['$resource', 'API_ENDPOINT_ENDPOINTS', 'EndpointProvider', function BrowseFactory($resource, API_ENDPOINT_ENDPOINTS, EndpointProvider) { + 'use strict'; + return $resource(API_ENDPOINT_ENDPOINTS + '/:endpointId/docker/browse/:volumeID/:action', { + endpointId: EndpointProvider.endpointID + }, + { + ls: { + method: 'GET', isArray: true, params: { action: 'ls' } + }, + get: { + method: 'GET', params: { action: 'get' }, + transformResponse: browseGetResponse + }, + delete: { + method: 'DELETE', params: { action: 'delete' } + }, + rename: { + method: 'PUT', params: { action: 'rename' } + } + }); +}]); diff --git a/app/agent/services/agentService.js b/app/agent/services/agentService.js index 7143e1e0f..17a2c3166 100644 --- a/app/agent/services/agentService.js +++ b/app/agent/services/agentService.js @@ -1,12 +1,17 @@ angular.module('portainer.agent').factory('AgentService', [ - '$q', 'Agent','HttpRequestHelper', 'Host', - function AgentServiceFactory($q, Agent, HttpRequestHelper, Host) { + '$q', 'Agent', 'AgentVersion1', 'HttpRequestHelper', 'Host', 'StateManager', + function AgentServiceFactory($q, Agent, AgentVersion1, HttpRequestHelper, Host, StateManager) { 'use strict'; var service = {}; service.agents = agents; service.hostInfo = hostInfo; + function getAgentApiVersion() { + var state = StateManager.getState(); + return state.endpoint.agentApiVersion; + } + function hostInfo(nodeName) { HttpRequestHelper.setPortainerAgentTargetHeader(nodeName); return Host.info().$promise; @@ -15,7 +20,10 @@ angular.module('portainer.agent').factory('AgentService', [ function agents() { var deferred = $q.defer(); - Agent.query({}) + var agentVersion = getAgentApiVersion(); + var service = agentVersion > 1 ? Agent : AgentVersion1; + + service.query({ version: agentVersion }) .$promise.then(function success(data) { var agents = data.map(function(item) { return new AgentViewModel(item); diff --git a/app/agent/services/hostBrowserService.js b/app/agent/services/hostBrowserService.js index 935d34be4..6f292c36a 100644 --- a/app/agent/services/hostBrowserService.js +++ b/app/agent/services/hostBrowserService.js @@ -1,6 +1,6 @@ angular.module('portainer.agent').factory('HostBrowserService', [ - 'Browse', 'Upload', 'API_ENDPOINT_ENDPOINTS', 'EndpointProvider', '$q', - function HostBrowserServiceFactory(Browse, Upload, API_ENDPOINT_ENDPOINTS, EndpointProvider, $q) { + 'Browse', 'Upload', 'API_ENDPOINT_ENDPOINTS', 'EndpointProvider', '$q', 'StateManager', + function HostBrowserServiceFactory(Browse, Upload, API_ENDPOINT_ENDPOINTS, EndpointProvider, $q, StateManager) { var service = {}; service.ls = ls; @@ -31,9 +31,17 @@ angular.module('portainer.agent').factory('HostBrowserService', [ function upload(path, file, onProgress) { var deferred = $q.defer(); - var url = API_ENDPOINT_ENDPOINTS + '/' + EndpointProvider.endpointID() + '/docker/browse/put'; + var agentVersion = StateManager.getAgentApiVersion(); + var url = + API_ENDPOINT_ENDPOINTS + + '/' + + EndpointProvider.endpointID() + + '/docker' + + (agentVersion > 1 ? '/v' + agentVersion : '') + + '/browse/put'; + Upload.upload({ - url: url, + url: url, data: { file: file, Path: path } }).then(deferred.resolve, deferred.reject, onProgress); return deferred.promise; diff --git a/app/agent/services/pingService.js b/app/agent/services/pingService.js new file mode 100644 index 000000000..765d47a5f --- /dev/null +++ b/app/agent/services/pingService.js @@ -0,0 +1,14 @@ +angular.module('portainer.agent').service('AgentPingService', [ + 'AgentPing', + function AgentPingService(AgentPing) { + var service = {}; + + service.ping = ping; + + function ping() { + return AgentPing.ping().$promise; + } + + return service; + } +]); diff --git a/app/agent/services/volumeBrowserService.js b/app/agent/services/volumeBrowserService.js index 1da020c38..a4ffdbf9d 100644 --- a/app/agent/services/volumeBrowserService.js +++ b/app/agent/services/volumeBrowserService.js @@ -1,27 +1,60 @@ angular.module('portainer.agent').factory('VolumeBrowserService', [ - '$q', 'Browse', - function VolumeBrowserServiceFactory($q, Browse) { + 'StateManager', 'Browse', 'BrowseVersion1', '$q', 'API_ENDPOINT_ENDPOINTS', 'EndpointProvider', 'Upload', + function VolumeBrowserServiceFactory(StateManager, Browse, BrowseVersion1, $q, API_ENDPOINT_ENDPOINTS, EndpointProvider, Upload) { 'use strict'; var service = {}; + function getAgentApiVersion() { + var state = StateManager.getState(); + return state.endpoint.agentApiVersion; + } + + function getBrowseService() { + var agentVersion = getAgentApiVersion(); + return agentVersion > 1 ? Browse : BrowseVersion1; + } + service.ls = function(volumeId, path) { - return Browse.ls({ volumeID: volumeId, path: path }).$promise; + return getBrowseService().ls({ volumeID: volumeId, path: path, version: getAgentApiVersion() }).$promise; }; service.get = function(volumeId, path) { - return Browse.get({ volumeID: volumeId, path: path }).$promise; + return getBrowseService().get({ volumeID: volumeId, path: path, version: getAgentApiVersion() }).$promise; }; service.delete = function(volumeId, path) { - return Browse.delete({ volumeID: volumeId, path: path }).$promise; + return getBrowseService().delete({ volumeID: volumeId, path: path, version: getAgentApiVersion() }).$promise; }; service.rename = function(volumeId, path, newPath) { var payload = { - CurrentFilePath: path, + CurrentFilePath: path, NewFilePath: newPath }; - return Browse.rename({ volumeID: volumeId }, payload).$promise; + return getBrowseService().rename({ volumeID: volumeId, version: getAgentApiVersion() }, payload).$promise; + }; + + service.upload = function upload(path, file, volumeId, onProgress) { + var deferred = $q.defer(); + var agentVersion = StateManager.getAgentApiVersion(); + if (agentVersion <2) { + deferred.reject('upload is not supported on this agent version'); + return; + } + var url = + API_ENDPOINT_ENDPOINTS + + '/' + + EndpointProvider.endpointID() + + '/docker' + + '/v' + agentVersion + + '/browse/put?volumeID=' + + volumeId; + + Upload.upload({ + url: url, + data: { file: file, Path: path } + }).then(deferred.resolve, deferred.reject, onProgress); + return deferred.promise; }; return service; diff --git a/app/docker/components/host-overview/host-overview.html b/app/docker/components/host-overview/host-overview.html index 81ac13c62..e38ccabb0 100644 --- a/app/docker/components/host-overview/host-overview.html +++ b/app/docker/components/host-overview/host-overview.html @@ -10,12 +10,12 @@ - - + + \ 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 index 78883e4e8..36ab4087a 100644 --- a/app/docker/components/host-overview/host-overview.js +++ b/app/docker/components/host-overview/host-overview.js @@ -6,6 +6,7 @@ angular.module('portainer.docker').component('hostOverview', { devices: '<', disks: '<', isAgent: '<', + agentApiVersion: '<', refreshUrl: '@', browseUrl: '@' }, 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 index 9bb9b7f85..8b46f466f 100644 --- 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 @@ -26,7 +26,7 @@ Total memory {{ $ctrl.host.totalMemory | humansize }} - +