diff --git a/api/http/proxy/factory/docker/transport.go b/api/http/proxy/factory/docker/transport.go index 961a98223..219c7f38f 100644 --- a/api/http/proxy/factory/docker/transport.go +++ b/api/http/proxy/factory/docker/transport.go @@ -273,7 +273,7 @@ func (transport *Transport) proxyServiceRequest(request *http.Request) (*http.Re func (transport *Transport) proxyVolumeRequest(request *http.Request) (*http.Response, error) { switch requestPath := request.URL.Path; requestPath { case "/volumes/create": - return transport.decorateGenericResourceCreationOperation(request, volumeObjectIdentifier, portainer.VolumeResourceControl) + return transport.decorateVolumeResourceCreationOperation(request, volumeObjectIdentifier, portainer.VolumeResourceControl) case "/volumes/prune": return transport.administratorOperation(request) diff --git a/api/http/proxy/factory/docker/volumes.go b/api/http/proxy/factory/docker/volumes.go index 49512852e..8923c996a 100644 --- a/api/http/proxy/factory/docker/volumes.go +++ b/api/http/proxy/factory/docker/volumes.go @@ -2,12 +2,14 @@ package docker import ( "context" + "errors" "net/http" "github.com/docker/docker/client" - "github.com/portainer/portainer/api" + portainer "github.com/portainer/portainer/api" "github.com/portainer/portainer/api/http/proxy/factory/responseutils" + "github.com/portainer/portainer/api/http/security" ) const ( @@ -87,3 +89,40 @@ func (transport *Transport) volumeInspectOperation(response *http.Response, exec func selectorVolumeLabels(responseObject map[string]interface{}) map[string]interface{} { return responseutils.GetJSONObject(responseObject, "Labels") } + +func (transport *Transport) decorateVolumeResourceCreationOperation(request *http.Request, resourceIdentifierAttribute string, resourceType portainer.ResourceControlType) (*http.Response, error) { + tokenData, err := security.RetrieveTokenData(request) + if err != nil { + return nil, err + } + + volumeID := request.Header.Get("X-Portainer-VolumeName") + + if volumeID != "" { + cli := transport.dockerClient + agentTargetHeader := request.Header.Get(portainer.PortainerAgentTargetHeader) + if agentTargetHeader != "" { + dockerClient, err := transport.dockerClientFactory.CreateClient(transport.endpoint, agentTargetHeader) + if err != nil { + return nil, err + } + defer dockerClient.Close() + cli = dockerClient + } + + _, err = cli.VolumeInspect(context.Background(), volumeID) + if err == nil { + return nil, errors.New("a volume with the same name already exists") + } + } + + response, err := transport.executeDockerRequest(request) + if err != nil { + return response, err + } + + if response.StatusCode == http.StatusCreated && volumeID != "" { + err = transport.decorateGenericResourceCreationResponse(response, resourceIdentifierAttribute, resourceType, tokenData.ID) + } + return response, err +} diff --git a/app/docker/rest/volume.js b/app/docker/rest/volume.js index 2a6f44165..e37f1b6c0 100644 --- a/app/docker/rest/volume.js +++ b/app/docker/rest/volume.js @@ -7,6 +7,11 @@ angular.module('portainer.docker').factory('Volume', [ 'VolumesInterceptor', function VolumeFactory($resource, API_ENDPOINT_ENDPOINTS, EndpointProvider, VolumesInterceptor) { 'use strict'; + + function addVolumeNameToHeader(config) { + return config.data.Name; + } + return $resource( API_ENDPOINT_ENDPOINTS + '/:endpointId/docker/volumes/:id/:action', { @@ -15,7 +20,13 @@ angular.module('portainer.docker').factory('Volume', [ { query: { method: 'GET', interceptor: VolumesInterceptor, timeout: 15000 }, get: { method: 'GET', params: { id: '@id' } }, - create: { method: 'POST', params: { action: 'create' }, transformResponse: genericHandler, ignoreLoadingBar: true }, + create: { + method: 'POST', + params: { action: 'create' }, + transformResponse: genericHandler, + ignoreLoadingBar: true, + headers: { 'X-Portainer-VolumeName': addVolumeNameToHeader }, + }, remove: { method: 'DELETE', transformResponse: genericHandler,