From 91981c815cb5640bea434fb7e9ed343afb912841 Mon Sep 17 00:00:00 2001 From: Maxime Bajeux Date: Tue, 7 Jul 2020 02:01:18 +0200 Subject: [PATCH] feat(volumes): Ensure a unique identifier for volumes (#3879) * feat(volumes): Ensure a unique identifier for volumes * feat(volumes): change few things --- api/http/proxy/factory/docker/transport.go | 9 +--- api/http/proxy/factory/docker/volumes.go | 60 ++++++++++++++++++++-- 2 files changed, 58 insertions(+), 11 deletions(-) diff --git a/api/http/proxy/factory/docker/transport.go b/api/http/proxy/factory/docker/transport.go index 09027d0c4..cf9b64a86 100644 --- a/api/http/proxy/factory/docker/transport.go +++ b/api/http/proxy/factory/docker/transport.go @@ -264,14 +264,7 @@ func (transport *Transport) proxyVolumeRequest(request *http.Request) (*http.Res default: // assume /volumes/{name} - volumeID := path.Base(requestPath) - - if request.Method == http.MethodGet { - return transport.rewriteOperation(request, transport.volumeInspectOperation) - } else if request.Method == http.MethodDelete { - return transport.executeGenericResourceDeletionOperation(request, volumeID, portainer.VolumeResourceControl) - } - return transport.restrictedResourceOperation(request, volumeID, portainer.VolumeResourceControl, false) + return transport.restrictedVolumeOperation(requestPath, request) } } diff --git a/api/http/proxy/factory/docker/volumes.go b/api/http/proxy/factory/docker/volumes.go index a4b5002ef..61a38c72a 100644 --- a/api/http/proxy/factory/docker/volumes.go +++ b/api/http/proxy/factory/docker/volumes.go @@ -4,6 +4,7 @@ import ( "context" "errors" "net/http" + "path" "github.com/docker/docker/client" @@ -14,7 +15,7 @@ import ( ) const ( - volumeObjectIdentifier = "Name" + volumeObjectIdentifier = "ID" ) func getInheritedResourceControlFromVolumeLabels(dockerClient *client.Client, volumeID string, resourceControls []portainer.ResourceControl) (*portainer.ResourceControl, error) { @@ -45,6 +46,14 @@ func (transport *Transport) volumeListOperation(response *http.Response, executo if responseObject["Volumes"] != nil { volumeData := responseObject["Volumes"].([]interface{}) + for _, volumeObject := range volumeData { + volume := volumeObject.(map[string]interface{}) + if volume["Name"] == nil || volume["CreatedAt"] == nil { + return errors.New("missing identifier in Docker resource list response") + } + volume[volumeObjectIdentifier] = volume["Name"].(string) + volume["CreatedAt"].(string) + } + resourceOperationParameters := &resourceOperationParameters{ resourceIdentifierAttribute: volumeObjectIdentifier, resourceType: portainer.VolumeResourceControl, @@ -55,7 +64,6 @@ func (transport *Transport) volumeListOperation(response *http.Response, executo if err != nil { return err } - // Overwrite the original volume list responseObject["Volumes"] = volumeData } @@ -73,6 +81,11 @@ func (transport *Transport) volumeInspectOperation(response *http.Response, exec return err } + if responseObject["Name"] == nil || responseObject["CreatedAt"] == nil { + return errors.New("missing identifier in Docker resource detail response") + } + responseObject[volumeObjectIdentifier] = responseObject["Name"].(string) + responseObject["CreatedAt"].(string) + resourceOperationParameters := &resourceOperationParameters{ resourceIdentifierAttribute: volumeObjectIdentifier, resourceType: portainer.VolumeResourceControl, @@ -123,7 +136,48 @@ func (transport *Transport) decorateVolumeResourceCreationOperation(request *htt } if response.StatusCode == http.StatusCreated { - err = transport.decorateGenericResourceCreationResponse(response, resourceIdentifierAttribute, resourceType, tokenData.ID) + err = transport.decorateVolumeCreationResponse(response, resourceIdentifierAttribute, resourceType, tokenData.ID) } return response, err } + +func (transport *Transport) decorateVolumeCreationResponse(response *http.Response, resourceIdentifierAttribute string, resourceType portainer.ResourceControlType, userID portainer.UserID) error { + responseObject, err := responseutils.GetResponseAsJSONOBject(response) + if err != nil { + return err + } + + if responseObject["Name"] == nil || responseObject["CreatedAt"] == nil { + return errors.New("missing identifier in Docker resource creation response") + } + resourceID := responseObject["Name"].(string) + responseObject["CreatedAt"].(string) + + resourceControl, err := transport.createPrivateResourceControl(resourceID, resourceType, userID) + if err != nil { + return err + } + + responseObject = decorateObject(responseObject, resourceControl) + + return responseutils.RewriteResponse(response, responseObject, http.StatusOK) +} + +func (transport *Transport) restrictedVolumeOperation(requestPath string, request *http.Request) (*http.Response, error) { + + if request.Method == http.MethodGet { + return transport.rewriteOperation(request, transport.volumeInspectOperation) + } + + cli := transport.dockerClient + volume, err := cli.VolumeInspect(context.Background(), path.Base(requestPath)) + if err != nil { + return nil, err + } + + volumeID := volume.Name + volume.CreatedAt + + if request.Method == http.MethodDelete { + return transport.executeGenericResourceDeletionOperation(request, volumeID, portainer.VolumeResourceControl) + } + return transport.restrictedResourceOperation(request, volumeID, portainer.VolumeResourceControl, false) +}