blob: 609878ba57cb1450ca77790ff4bc9bbdc1a39695 [file] [log] [blame]
Jon Perritt816d2a02014-03-11 20:49:46 -05001package objects
2
3import (
jrperritt046c8822015-07-30 12:02:28 -06004 "bytes"
Jon Perritt90957602015-02-01 17:03:06 -07005 "crypto/hmac"
Jamie Hannaford08096232015-07-13 12:47:28 +02006 "crypto/md5"
Jon Perritt90957602015-02-01 17:03:06 -07007 "crypto/sha1"
Jon Perritt816d2a02014-03-11 20:49:46 -05008 "fmt"
Jon Perritt8c93a302014-09-28 22:35:57 -05009 "io"
Jon Perritt90957602015-02-01 17:03:06 -070010 "strings"
Jon Perritt8c93a302014-09-28 22:35:57 -050011 "time"
Ash Wilson604320e2014-09-10 16:02:28 -040012
jrperritt046c8822015-07-30 12:02:28 -060013 "github.com/jrperritt/rack/internal/github.com/rackspace/gophercloud"
14 "github.com/jrperritt/rack/internal/github.com/rackspace/gophercloud/openstack/objectstorage/v1/accounts"
15 "github.com/jrperritt/rack/internal/github.com/rackspace/gophercloud/pagination"
Jon Perritt816d2a02014-03-11 20:49:46 -050016)
17
Jon Perritte90aced2014-10-12 23:24:06 -050018// ListOptsBuilder allows extensions to add additional parameters to the List
19// request.
20type ListOptsBuilder interface {
21 ToObjectListParams() (bool, string, error)
22}
23
Jon Perritt8c93a302014-09-28 22:35:57 -050024// ListOpts is a structure that holds parameters for listing objects.
25type ListOpts struct {
Jon Perritt9415ca72014-11-03 11:58:48 -060026 // Full is a true/false value that represents the amount of object information
27 // returned. If Full is set to true, then the content-type, number of bytes, hash
28 // date last modified, and name are returned. If set to false or not set, then
29 // only the object names are returned.
Jon Perritt8c93a302014-09-28 22:35:57 -050030 Full bool
Jon Perritt04851d32014-10-14 02:07:13 -050031 Limit int `q:"limit"`
32 Marker string `q:"marker"`
33 EndMarker string `q:"end_marker"`
34 Format string `q:"format"`
35 Prefix string `q:"prefix"`
36 Delimiter string `q:"delimiter"`
37 Path string `q:"path"`
Ash Wilsonca6f7562014-09-16 15:43:54 -040038}
39
Jon Perritte90aced2014-10-12 23:24:06 -050040// ToObjectListParams formats a ListOpts into a query string and boolean
41// representing whether to list complete information for each object.
42func (opts ListOpts) ToObjectListParams() (bool, string, error) {
43 q, err := gophercloud.BuildQueryString(opts)
44 if err != nil {
45 return false, "", err
46 }
47 return opts.Full, q.String(), nil
48}
49
Jon Perritt816d2a02014-03-11 20:49:46 -050050// List is a function that retrieves all objects in a container. It also returns the details
51// for the container. To extract only the object information or names, pass the ListResult
Jon Perritteb575642014-04-24 15:16:31 -050052// response to the ExtractInfo or ExtractNames function, respectively.
Jon Perritte90aced2014-10-12 23:24:06 -050053func List(c *gophercloud.ServiceClient, containerName string, opts ListOptsBuilder) pagination.Pager {
54 headers := map[string]string{"Accept": "text/plain", "Content-Type": "text/plain"}
Jon Perritt816d2a02014-03-11 20:49:46 -050055
Jon Perrittea4e3012014-10-09 22:03:19 -050056 url := listURL(c, containerName)
Jon Perrittde47eac2014-09-30 15:34:17 -050057 if opts != nil {
Jon Perritte90aced2014-10-12 23:24:06 -050058 full, query, err := opts.ToObjectListParams()
Jon Perrittde47eac2014-09-30 15:34:17 -050059 if err != nil {
Jon Perrittde47eac2014-09-30 15:34:17 -050060 return pagination.Pager{Err: err}
61 }
Jon Perritte90aced2014-10-12 23:24:06 -050062 url += query
Jon Perritt816d2a02014-03-11 20:49:46 -050063
Jon Perritte90aced2014-10-12 23:24:06 -050064 if full {
65 headers = map[string]string{"Accept": "application/json", "Content-Type": "application/json"}
Jon Perrittde47eac2014-09-30 15:34:17 -050066 }
Ash Wilsonca6f7562014-09-16 15:43:54 -040067 }
68
Ash Wilsonb8b16f82014-10-20 10:19:49 -040069 createPage := func(r pagination.PageResult) pagination.Page {
70 p := ObjectPage{pagination.MarkerPageBase{PageResult: r}}
Ash Wilsonca6f7562014-09-16 15:43:54 -040071 p.MarkerPageBase.Owner = p
72 return p
Jon Perritt816d2a02014-03-11 20:49:46 -050073 }
74
Ash Wilsonca6f7562014-09-16 15:43:54 -040075 pager := pagination.NewPager(c, url, createPage)
76 pager.Headers = headers
77 return pager
Jon Perritt816d2a02014-03-11 20:49:46 -050078}
79
Jon Perritte90aced2014-10-12 23:24:06 -050080// DownloadOptsBuilder allows extensions to add additional parameters to the
81// Download request.
82type DownloadOptsBuilder interface {
83 ToObjectDownloadParams() (map[string]string, string, error)
84}
85
Jon Perritt8c93a302014-09-28 22:35:57 -050086// DownloadOpts is a structure that holds parameters for downloading an object.
87type DownloadOpts struct {
88 IfMatch string `h:"If-Match"`
89 IfModifiedSince time.Time `h:"If-Modified-Since"`
90 IfNoneMatch string `h:"If-None-Match"`
91 IfUnmodifiedSince time.Time `h:"If-Unmodified-Since"`
92 Range string `h:"Range"`
93 Expires string `q:"expires"`
94 MultipartManifest string `q:"multipart-manifest"`
95 Signature string `q:"signature"`
96}
97
Jon Perritte90aced2014-10-12 23:24:06 -050098// ToObjectDownloadParams formats a DownloadOpts into a query string and map of
99// headers.
Paul Querna7dc6fe62014-11-01 08:09:41 -0700100func (opts DownloadOpts) ToObjectDownloadParams() (map[string]string, string, error) {
Jon Perritte90aced2014-10-12 23:24:06 -0500101 q, err := gophercloud.BuildQueryString(opts)
102 if err != nil {
103 return nil, "", err
104 }
105 h, err := gophercloud.BuildHeaders(opts)
106 if err != nil {
107 return nil, q.String(), err
108 }
109 return h, q.String(), nil
110}
111
Jon Perritt816d2a02014-03-11 20:49:46 -0500112// Download is a function that retrieves the content and metadata for an object.
Jon Perritte90aced2014-10-12 23:24:06 -0500113// To extract just the content, pass the DownloadResult response to the
114// ExtractContent function.
115func Download(c *gophercloud.ServiceClient, containerName, objectName string, opts DownloadOptsBuilder) DownloadResult {
Jon Perritt5db08922014-09-30 21:32:48 -0500116 var res DownloadResult
Jon Perritt8c93a302014-09-28 22:35:57 -0500117
Jon Perrittea4e3012014-10-09 22:03:19 -0500118 url := downloadURL(c, containerName, objectName)
Ash Wilson77857dc2014-10-22 09:09:02 -0400119 h := c.AuthenticatedHeaders()
Jon Perritt816d2a02014-03-11 20:49:46 -0500120
Jon Perrittde47eac2014-09-30 15:34:17 -0500121 if opts != nil {
Jon Perritte90aced2014-10-12 23:24:06 -0500122 headers, query, err := opts.ToObjectDownloadParams()
Jon Perrittde47eac2014-09-30 15:34:17 -0500123 if err != nil {
Jon Perritt5db08922014-09-30 21:32:48 -0500124 res.Err = err
125 return res
Jon Perrittde47eac2014-09-30 15:34:17 -0500126 }
127
128 for k, v := range headers {
129 h[k] = v
130 }
131
Jon Perritte90aced2014-10-12 23:24:06 -0500132 url += query
Jon Perritt8c93a302014-09-28 22:35:57 -0500133 }
134
Ash Wilson4bf41a32015-02-12 15:52:44 -0500135 resp, err := c.Request("GET", url, gophercloud.RequestOpts{
Jon Perritt816d2a02014-03-11 20:49:46 -0500136 MoreHeaders: h,
Paul Querna9163df22014-11-01 09:38:51 -0700137 OkCodes: []int{200, 304},
Jon Perritt816d2a02014-03-11 20:49:46 -0500138 })
Jon Perritta2c88b22015-05-18 11:23:30 -0600139 if resp != nil {
140 res.Header = resp.Header
141 res.Body = resp.Body
142 }
Jon Perritt5db08922014-09-30 21:32:48 -0500143 res.Err = err
Jamie Hannaford2e784862014-10-27 10:40:27 +0100144
Jon Perritt5db08922014-09-30 21:32:48 -0500145 return res
Jon Perritt8c93a302014-09-28 22:35:57 -0500146}
147
Jon Perritte90aced2014-10-12 23:24:06 -0500148// CreateOptsBuilder allows extensions to add additional parameters to the
149// Create request.
150type CreateOptsBuilder interface {
151 ToObjectCreateParams() (map[string]string, string, error)
152}
153
Jon Perritt8c93a302014-09-28 22:35:57 -0500154// CreateOpts is a structure that holds parameters for creating an object.
155type CreateOpts struct {
156 Metadata map[string]string
157 ContentDisposition string `h:"Content-Disposition"`
158 ContentEncoding string `h:"Content-Encoding"`
Jon Perritte376fa52014-11-03 11:35:48 -0600159 ContentLength int64 `h:"Content-Length"`
Jon Perritt8c93a302014-09-28 22:35:57 -0500160 ContentType string `h:"Content-Type"`
161 CopyFrom string `h:"X-Copy-From"`
162 DeleteAfter int `h:"X-Delete-After"`
163 DeleteAt int `h:"X-Delete-At"`
164 DetectContentType string `h:"X-Detect-Content-Type"`
165 ETag string `h:"ETag"`
166 IfNoneMatch string `h:"If-None-Match"`
167 ObjectManifest string `h:"X-Object-Manifest"`
168 TransferEncoding string `h:"Transfer-Encoding"`
169 Expires string `q:"expires"`
170 MultipartManifest string `q:"multiple-manifest"`
171 Signature string `q:"signature"`
Jon Perritt816d2a02014-03-11 20:49:46 -0500172}
173
Jon Perritte90aced2014-10-12 23:24:06 -0500174// ToObjectCreateParams formats a CreateOpts into a query string and map of
175// headers.
176func (opts CreateOpts) ToObjectCreateParams() (map[string]string, string, error) {
177 q, err := gophercloud.BuildQueryString(opts)
178 if err != nil {
179 return nil, "", err
180 }
181 h, err := gophercloud.BuildHeaders(opts)
182 if err != nil {
183 return nil, q.String(), err
184 }
185
186 for k, v := range opts.Metadata {
187 h["X-Object-Meta-"+k] = v
188 }
189
190 return h, q.String(), nil
191}
192
Jamie Hannaford50fc97d2015-07-16 12:29:01 +0200193// Create is a function that creates a new object or replaces an existing object. If the returned response's ETag
194// header fails to match the local checksum, the failed request will automatically be retried up to a maximum of 3 times.
Brendan ODonnella69b3472015-04-27 13:59:41 -0500195func Create(c *gophercloud.ServiceClient, containerName, objectName string, content io.ReadSeeker, opts CreateOptsBuilder) CreateResult {
Jon Perritt5db08922014-09-30 21:32:48 -0500196 var res CreateResult
Jon Perritt816d2a02014-03-11 20:49:46 -0500197
Jon Perrittea4e3012014-10-09 22:03:19 -0500198 url := createURL(c, containerName, objectName)
Ash Wilson322a7e62015-02-12 16:25:26 -0500199 h := make(map[string]string)
Jon Perritt816d2a02014-03-11 20:49:46 -0500200
Jon Perrittde47eac2014-09-30 15:34:17 -0500201 if opts != nil {
Jon Perritte90aced2014-10-12 23:24:06 -0500202 headers, query, err := opts.ToObjectCreateParams()
Jon Perrittde47eac2014-09-30 15:34:17 -0500203 if err != nil {
Jon Perritt5db08922014-09-30 21:32:48 -0500204 res.Err = err
205 return res
Jon Perrittde47eac2014-09-30 15:34:17 -0500206 }
Jon Perritt8c93a302014-09-28 22:35:57 -0500207
Jon Perrittde47eac2014-09-30 15:34:17 -0500208 for k, v := range headers {
209 h[k] = v
210 }
Jon Perritt816d2a02014-03-11 20:49:46 -0500211
Jon Perritte90aced2014-10-12 23:24:06 -0500212 url += query
Jon Perritt8c93a302014-09-28 22:35:57 -0500213 }
Jon Perritt816d2a02014-03-11 20:49:46 -0500214
Jamie Hannaford08096232015-07-13 12:47:28 +0200215 hash := md5.New()
Jamie Hannaford08096232015-07-13 12:47:28 +0200216
jrperritte6e8c652015-07-30 12:17:01 -0600217 contentBuffer := bytes.NewBuffer([]byte{})
218 _, err := io.Copy(contentBuffer, io.TeeReader(content, hash))
219 if err != nil {
220 res.Err = err
221 return res
222 }
223
224 localChecksum := hash.Sum(nil)
jrperritt56d51e92015-07-30 11:50:53 -0600225 h["ETag"] = fmt.Sprintf("%x", localChecksum)
226
227 ropts := gophercloud.RequestOpts{
jrperritt046c8822015-07-30 12:02:28 -0600228 RawBody: strings.NewReader(contentBuffer.String()),
jrperritt56d51e92015-07-30 11:50:53 -0600229 MoreHeaders: h,
230 }
231
Jamie Hannaford08096232015-07-13 12:47:28 +0200232 for i := 1; i <= 3; i++ {
jrperritt56d51e92015-07-30 11:50:53 -0600233 resp, err := c.Request("PUT", url, ropts)
234 if resp != nil {
235 res.Header = resp.Header
236 }
Jamie Hannaford08096232015-07-13 12:47:28 +0200237 if resp.Header.Get("ETag") == fmt.Sprintf("%x", localChecksum) {
238 res.Err = err
239 break
240 }
241 if i == 3 {
242 res.Err = fmt.Errorf("Local checksum does not match API ETag header")
243 return res
244 }
245 }
246
Jon Perritt5db08922014-09-30 21:32:48 -0500247 return res
Jon Perritt816d2a02014-03-11 20:49:46 -0500248}
249
Jon Perritte90aced2014-10-12 23:24:06 -0500250// CopyOptsBuilder allows extensions to add additional parameters to the
251// Copy request.
252type CopyOptsBuilder interface {
Jon Perritt04851d32014-10-14 02:07:13 -0500253 ToObjectCopyMap() (map[string]string, error)
Jon Perritte90aced2014-10-12 23:24:06 -0500254}
255
256// CopyOpts is a structure that holds parameters for copying one object to
257// another.
Jon Perritt8c93a302014-09-28 22:35:57 -0500258type CopyOpts struct {
259 Metadata map[string]string
260 ContentDisposition string `h:"Content-Disposition"`
261 ContentEncoding string `h:"Content-Encoding"`
262 ContentType string `h:"Content-Type"`
263 Destination string `h:"Destination,required"`
264}
265
Jon Perritt04851d32014-10-14 02:07:13 -0500266// ToObjectCopyMap formats a CopyOpts into a map of headers.
267func (opts CopyOpts) ToObjectCopyMap() (map[string]string, error) {
Jon Perritte90aced2014-10-12 23:24:06 -0500268 if opts.Destination == "" {
269 return nil, fmt.Errorf("Required CopyOpts field 'Destination' not set.")
270 }
271 h, err := gophercloud.BuildHeaders(opts)
272 if err != nil {
273 return nil, err
274 }
275 for k, v := range opts.Metadata {
276 h["X-Object-Meta-"+k] = v
277 }
278 return h, nil
279}
280
Jon Perritt816d2a02014-03-11 20:49:46 -0500281// Copy is a function that copies one object to another.
Jon Perritte90aced2014-10-12 23:24:06 -0500282func Copy(c *gophercloud.ServiceClient, containerName, objectName string, opts CopyOptsBuilder) CopyResult {
Jon Perritt5db08922014-09-30 21:32:48 -0500283 var res CopyResult
Ash Wilson77857dc2014-10-22 09:09:02 -0400284 h := c.AuthenticatedHeaders()
Jon Perritt816d2a02014-03-11 20:49:46 -0500285
Jon Perritt04851d32014-10-14 02:07:13 -0500286 headers, err := opts.ToObjectCopyMap()
Jon Perritt8c93a302014-09-28 22:35:57 -0500287 if err != nil {
Jon Perritt5db08922014-09-30 21:32:48 -0500288 res.Err = err
289 return res
Jon Perritt816d2a02014-03-11 20:49:46 -0500290 }
Jon Perritte90aced2014-10-12 23:24:06 -0500291
Jon Perritt8c93a302014-09-28 22:35:57 -0500292 for k, v := range headers {
Jon Perritt816d2a02014-03-11 20:49:46 -0500293 h[k] = v
294 }
295
Jon Perrittea4e3012014-10-09 22:03:19 -0500296 url := copyURL(c, containerName, objectName)
Ash Wilson4bf41a32015-02-12 15:52:44 -0500297 resp, err := c.Request("COPY", url, gophercloud.RequestOpts{
Jon Perritt8c93a302014-09-28 22:35:57 -0500298 MoreHeaders: h,
299 OkCodes: []int{201},
300 })
Jon Perritta2c88b22015-05-18 11:23:30 -0600301 if resp != nil {
302 res.Header = resp.Header
303 }
jrperrittf7a8e282014-10-28 10:00:48 -0500304 res.Err = err
Jon Perritt5db08922014-09-30 21:32:48 -0500305 return res
Jon Perritt8c93a302014-09-28 22:35:57 -0500306}
307
Jon Perritte90aced2014-10-12 23:24:06 -0500308// DeleteOptsBuilder allows extensions to add additional parameters to the
309// Delete request.
310type DeleteOptsBuilder interface {
Jon Perritt26780d52014-10-14 11:35:58 -0500311 ToObjectDeleteQuery() (string, error)
Jon Perritte90aced2014-10-12 23:24:06 -0500312}
313
Jon Perritt8c93a302014-09-28 22:35:57 -0500314// DeleteOpts is a structure that holds parameters for deleting an object.
315type DeleteOpts struct {
316 MultipartManifest string `q:"multipart-manifest"`
317}
318
Jon Perritt26780d52014-10-14 11:35:58 -0500319// ToObjectDeleteQuery formats a DeleteOpts into a query string.
320func (opts DeleteOpts) ToObjectDeleteQuery() (string, error) {
Jon Perritte90aced2014-10-12 23:24:06 -0500321 q, err := gophercloud.BuildQueryString(opts)
322 if err != nil {
323 return "", err
324 }
325 return q.String(), nil
326}
327
Jon Perritt8c93a302014-09-28 22:35:57 -0500328// Delete is a function that deletes an object.
Jon Perritte90aced2014-10-12 23:24:06 -0500329func Delete(c *gophercloud.ServiceClient, containerName, objectName string, opts DeleteOptsBuilder) DeleteResult {
Jon Perritt5db08922014-09-30 21:32:48 -0500330 var res DeleteResult
Jon Perrittea4e3012014-10-09 22:03:19 -0500331 url := deleteURL(c, containerName, objectName)
Jon Perritt8c93a302014-09-28 22:35:57 -0500332
Jon Perrittde47eac2014-09-30 15:34:17 -0500333 if opts != nil {
Jon Perritt26780d52014-10-14 11:35:58 -0500334 query, err := opts.ToObjectDeleteQuery()
Jon Perrittde47eac2014-09-30 15:34:17 -0500335 if err != nil {
Jon Perritt5db08922014-09-30 21:32:48 -0500336 res.Err = err
337 return res
Jon Perrittde47eac2014-09-30 15:34:17 -0500338 }
Jon Perritte90aced2014-10-12 23:24:06 -0500339 url += query
Jon Perritt8c93a302014-09-28 22:35:57 -0500340 }
341
Jamie Hannaford1d27afa2015-03-24 16:20:45 +0100342 resp, err := c.Delete(url, nil)
Jon Perritta2c88b22015-05-18 11:23:30 -0600343 if resp != nil {
344 res.Header = resp.Header
345 }
Jon Perritt10a7ec12014-10-27 11:29:33 -0500346 res.Err = err
Jon Perritt5db08922014-09-30 21:32:48 -0500347 return res
Jon Perritt8c93a302014-09-28 22:35:57 -0500348}
349
Jon Perritte90aced2014-10-12 23:24:06 -0500350// GetOptsBuilder allows extensions to add additional parameters to the
351// Get request.
352type GetOptsBuilder interface {
Jon Perritt26780d52014-10-14 11:35:58 -0500353 ToObjectGetQuery() (string, error)
Jon Perritte90aced2014-10-12 23:24:06 -0500354}
355
Jon Perritt8c93a302014-09-28 22:35:57 -0500356// GetOpts is a structure that holds parameters for getting an object's metadata.
357type GetOpts struct {
358 Expires string `q:"expires"`
359 Signature string `q:"signature"`
360}
361
Jon Perritt26780d52014-10-14 11:35:58 -0500362// ToObjectGetQuery formats a GetOpts into a query string.
363func (opts GetOpts) ToObjectGetQuery() (string, error) {
Jon Perritte90aced2014-10-12 23:24:06 -0500364 q, err := gophercloud.BuildQueryString(opts)
365 if err != nil {
366 return "", err
367 }
368 return q.String(), nil
369}
370
Jon Perritt8c93a302014-09-28 22:35:57 -0500371// Get is a function that retrieves the metadata of an object. To extract just the custom
372// metadata, pass the GetResult response to the ExtractMetadata function.
Jon Perritte90aced2014-10-12 23:24:06 -0500373func Get(c *gophercloud.ServiceClient, containerName, objectName string, opts GetOptsBuilder) GetResult {
Jon Perritt5db08922014-09-30 21:32:48 -0500374 var res GetResult
Jon Perrittea4e3012014-10-09 22:03:19 -0500375 url := getURL(c, containerName, objectName)
Jon Perrittde47eac2014-09-30 15:34:17 -0500376
377 if opts != nil {
Jon Perritt26780d52014-10-14 11:35:58 -0500378 query, err := opts.ToObjectGetQuery()
Jon Perrittde47eac2014-09-30 15:34:17 -0500379 if err != nil {
Jon Perritt5db08922014-09-30 21:32:48 -0500380 res.Err = err
381 return res
Jon Perrittde47eac2014-09-30 15:34:17 -0500382 }
Jon Perritte90aced2014-10-12 23:24:06 -0500383 url += query
Jon Perritt8c93a302014-09-28 22:35:57 -0500384 }
385
Ash Wilson4bf41a32015-02-12 15:52:44 -0500386 resp, err := c.Request("HEAD", url, gophercloud.RequestOpts{
387 OkCodes: []int{200, 204},
Jon Perritt8c93a302014-09-28 22:35:57 -0500388 })
Jon Perritta2c88b22015-05-18 11:23:30 -0600389 if resp != nil {
390 res.Header = resp.Header
391 }
Jon Perritt5db08922014-09-30 21:32:48 -0500392 res.Err = err
Jon Perritt5db08922014-09-30 21:32:48 -0500393 return res
Jon Perritt8c93a302014-09-28 22:35:57 -0500394}
395
Jon Perritte90aced2014-10-12 23:24:06 -0500396// UpdateOptsBuilder allows extensions to add additional parameters to the
397// Update request.
398type UpdateOptsBuilder interface {
Jon Perritt04851d32014-10-14 02:07:13 -0500399 ToObjectUpdateMap() (map[string]string, error)
Jon Perritte90aced2014-10-12 23:24:06 -0500400}
401
Jon Perritt8c93a302014-09-28 22:35:57 -0500402// UpdateOpts is a structure that holds parameters for updating, creating, or deleting an
403// object's metadata.
404type UpdateOpts struct {
405 Metadata map[string]string
406 ContentDisposition string `h:"Content-Disposition"`
407 ContentEncoding string `h:"Content-Encoding"`
408 ContentType string `h:"Content-Type"`
409 DeleteAfter int `h:"X-Delete-After"`
410 DeleteAt int `h:"X-Delete-At"`
411 DetectContentType bool `h:"X-Detect-Content-Type"`
412}
413
Jon Perritt04851d32014-10-14 02:07:13 -0500414// ToObjectUpdateMap formats a UpdateOpts into a map of headers.
415func (opts UpdateOpts) ToObjectUpdateMap() (map[string]string, error) {
Jon Perritte90aced2014-10-12 23:24:06 -0500416 h, err := gophercloud.BuildHeaders(opts)
417 if err != nil {
418 return nil, err
419 }
420 for k, v := range opts.Metadata {
421 h["X-Object-Meta-"+k] = v
422 }
423 return h, nil
424}
425
Jon Perritt8c93a302014-09-28 22:35:57 -0500426// Update is a function that creates, updates, or deletes an object's metadata.
Jon Perritte90aced2014-10-12 23:24:06 -0500427func Update(c *gophercloud.ServiceClient, containerName, objectName string, opts UpdateOptsBuilder) UpdateResult {
Jon Perritt5db08922014-09-30 21:32:48 -0500428 var res UpdateResult
Ash Wilson77857dc2014-10-22 09:09:02 -0400429 h := c.AuthenticatedHeaders()
Jon Perritt8c93a302014-09-28 22:35:57 -0500430
Jon Perrittde47eac2014-09-30 15:34:17 -0500431 if opts != nil {
Jon Perritt04851d32014-10-14 02:07:13 -0500432 headers, err := opts.ToObjectUpdateMap()
Jon Perrittde47eac2014-09-30 15:34:17 -0500433 if err != nil {
Jon Perritt5db08922014-09-30 21:32:48 -0500434 res.Err = err
435 return res
Jon Perrittde47eac2014-09-30 15:34:17 -0500436 }
Jon Perritt8c93a302014-09-28 22:35:57 -0500437
Jon Perrittde47eac2014-09-30 15:34:17 -0500438 for k, v := range headers {
439 h[k] = v
440 }
Jon Perritt8c93a302014-09-28 22:35:57 -0500441 }
442
Jon Perrittea4e3012014-10-09 22:03:19 -0500443 url := updateURL(c, containerName, objectName)
Ash Wilson4bf41a32015-02-12 15:52:44 -0500444 resp, err := c.Request("POST", url, gophercloud.RequestOpts{
Jon Perritt816d2a02014-03-11 20:49:46 -0500445 MoreHeaders: h,
Jon Perritt816d2a02014-03-11 20:49:46 -0500446 })
Jon Perritta2c88b22015-05-18 11:23:30 -0600447 if resp != nil {
448 res.Header = resp.Header
449 }
Jon Perritt5db08922014-09-30 21:32:48 -0500450 res.Err = err
451 return res
Jon Perritt816d2a02014-03-11 20:49:46 -0500452}
Jon Perritt90957602015-02-01 17:03:06 -0700453
454// HTTPMethod represents an HTTP method string (e.g. "GET").
455type HTTPMethod string
456
457var (
458 // GET represents an HTTP "GET" method.
459 GET HTTPMethod = "GET"
460 // POST represents an HTTP "POST" method.
461 POST HTTPMethod = "POST"
462)
463
464// CreateTempURLOpts are options for creating a temporary URL for an object.
465type CreateTempURLOpts struct {
Jon Perritt28792af2015-02-02 11:00:04 -0700466 // (REQUIRED) Method is the HTTP method to allow for users of the temp URL. Valid values
Jon Perritt90957602015-02-01 17:03:06 -0700467 // are "GET" and "POST".
468 Method HTTPMethod
Jon Perritt28792af2015-02-02 11:00:04 -0700469 // (REQUIRED) TTL is the number of seconds the temp URL should be active.
Jon Perritt90957602015-02-01 17:03:06 -0700470 TTL int
Jon Perritt28792af2015-02-02 11:00:04 -0700471 // (Optional) Split is the string on which to split the object URL. Since only
472 // the object path is used in the hash, the object URL needs to be parsed. If
473 // empty, the default OpenStack URL split point will be used ("/v1/").
474 Split string
Jon Perritt90957602015-02-01 17:03:06 -0700475}
476
477// CreateTempURL is a function for creating a temporary URL for an object. It
478// allows users to have "GET" or "POST" access to a particular tenant's object
479// for a limited amount of time.
480func CreateTempURL(c *gophercloud.ServiceClient, containerName, objectName string, opts CreateTempURLOpts) (string, error) {
Jon Perritt28792af2015-02-02 11:00:04 -0700481 if opts.Split == "" {
482 opts.Split = "/v1/"
483 }
Jon Perritt90957602015-02-01 17:03:06 -0700484 duration := time.Duration(opts.TTL) * time.Second
485 expiry := time.Now().Add(duration).Unix()
486 getHeader, err := accounts.Get(c, nil).Extract()
487 if err != nil {
488 return "", err
489 }
490 secretKey := []byte(getHeader.TempURLKey)
491 url := getURL(c, containerName, objectName)
Jon Perritt28792af2015-02-02 11:00:04 -0700492 splitPath := strings.Split(url, opts.Split)
Jon Perritt90957602015-02-01 17:03:06 -0700493 baseURL, objectPath := splitPath[0], splitPath[1]
Jon Perritt28792af2015-02-02 11:00:04 -0700494 objectPath = opts.Split + objectPath
Jon Perritt90957602015-02-01 17:03:06 -0700495 body := fmt.Sprintf("%s\n%d\n%s", opts.Method, expiry, objectPath)
496 hash := hmac.New(sha1.New, secretKey)
497 hash.Write([]byte(body))
498 hexsum := fmt.Sprintf("%x", hash.Sum(nil))
499 return fmt.Sprintf("%s%s?temp_url_sig=%s&temp_url_expires=%d", baseURL, objectPath, hexsum, expiry), nil
500}