github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/resource/api/server/upload.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package server 5 6 import ( 7 "io" 8 "net/http" 9 "path" 10 11 "github.com/juju/errors" 12 charmresource "gopkg.in/juju/charm.v6-unstable/resource" 13 14 "github.com/juju/juju/resource" 15 "github.com/juju/juju/resource/api" 16 ) 17 18 // UploadDataStore describes the the portion of Juju's "state" 19 // needed for handling upload requests. 20 type UploadDataStore interface { 21 // GetResource returns the identified resource. 22 GetResource(applicationID, name string) (resource.Resource, error) 23 24 // GetPendingResource returns the identified resource. 25 GetPendingResource(applicationID, name, pendingID string) (resource.Resource, error) 26 27 // SetResource adds the resource to blob storage and updates the metadata. 28 SetResource(applicationID, userID string, res charmresource.Resource, r io.Reader) (resource.Resource, error) 29 30 // UpdatePendingResource adds the resource to blob storage and updates the metadata. 31 UpdatePendingResource(applicationID, pendingID, userID string, res charmresource.Resource, r io.Reader) (resource.Resource, error) 32 } 33 34 // TODO(ericsnow) Replace UploadedResource with resource.Opened. 35 36 // UploadedResource holds both the information about an uploaded 37 // resource and the reader containing its data. 38 type UploadedResource struct { 39 // Service is the name of the application associated with the resource. 40 Service string 41 42 // PendingID is the resource-specific sub-ID for a pending resource. 43 PendingID string 44 45 // Resource is the information about the resource. 46 Resource charmresource.Resource 47 48 // Data holds the resource blob. 49 Data io.ReadCloser 50 } 51 52 // UploadHandler provides the functionality to handle upload requests. 53 type UploadHandler struct { 54 // Username is the ID of the user making the upload request. 55 Username string 56 57 // Store is the data store into which the resource will be stored. 58 Store UploadDataStore 59 } 60 61 // HandleRequest handles a resource upload request. 62 func (uh UploadHandler) HandleRequest(req *http.Request) (*api.UploadResult, error) { 63 defer req.Body.Close() 64 65 uploaded, err := uh.ReadResource(req) 66 if err != nil { 67 return nil, errors.Trace(err) 68 } 69 70 var stored resource.Resource 71 if uploaded.PendingID != "" { 72 stored, err = uh.Store.UpdatePendingResource(uploaded.Service, uploaded.PendingID, uh.Username, uploaded.Resource, uploaded.Data) 73 if err != nil { 74 return nil, errors.Trace(err) 75 } 76 } else { 77 stored, err = uh.Store.SetResource(uploaded.Service, uh.Username, uploaded.Resource, uploaded.Data) 78 if err != nil { 79 return nil, errors.Trace(err) 80 } 81 } 82 83 result := &api.UploadResult{ 84 Resource: api.Resource2API(stored), 85 } 86 return result, nil 87 } 88 89 // ReadResource extracts the relevant info from the request. 90 func (uh UploadHandler) ReadResource(req *http.Request) (*UploadedResource, error) { 91 uReq, err := api.ExtractUploadRequest(req) 92 if err != nil { 93 return nil, errors.Trace(err) 94 } 95 var res resource.Resource 96 if uReq.PendingID != "" { 97 res, err = uh.Store.GetPendingResource(uReq.Service, uReq.Name, uReq.PendingID) 98 if err != nil { 99 return nil, errors.Trace(err) 100 } 101 } else { 102 res, err = uh.Store.GetResource(uReq.Service, uReq.Name) 103 if err != nil { 104 return nil, errors.Trace(err) 105 } 106 } 107 108 ext := path.Ext(res.Path) 109 if path.Ext(uReq.Filename) != ext { 110 return nil, errors.Errorf("incorrect extension on resource upload %q, expected %q", uReq.Filename, ext) 111 } 112 113 chRes, err := uh.updateResource(res.Resource, uReq.Fingerprint, uReq.Size) 114 if err != nil { 115 return nil, errors.Trace(err) 116 } 117 118 uploaded := &UploadedResource{ 119 Service: uReq.Service, 120 PendingID: uReq.PendingID, 121 Resource: chRes, 122 Data: req.Body, 123 } 124 return uploaded, nil 125 } 126 127 // updateResource returns a copy of the provided resource, updated with 128 // the given information. 129 func (uh UploadHandler) updateResource(res charmresource.Resource, fp charmresource.Fingerprint, size int64) (charmresource.Resource, error) { 130 res.Origin = charmresource.OriginUpload 131 res.Revision = 0 132 res.Fingerprint = fp 133 res.Size = size 134 135 if err := res.Validate(); err != nil { 136 return res, errors.Trace(err) 137 } 138 return res, nil 139 }