blob: 1ab7ad7a12a735e4420c0ceb3a58e7c14d7f60cb [file] [log] [blame]
Jon Perritt816d2a02014-03-11 20:49:46 -05001package objects
2
3import (
Jon Perritt90957602015-02-01 17:03:06 -07004 "crypto/hmac"
Jamie Hannaford08096232015-07-13 12:47:28 +02005 "crypto/md5"
Jon Perritt90957602015-02-01 17:03:06 -07006 "crypto/sha1"
Jon Perritt816d2a02014-03-11 20:49:46 -05007 "fmt"
Jon Perritt8c93a302014-09-28 22:35:57 -05008 "io"
Jon Perritt90957602015-02-01 17:03:06 -07009 "strings"
Jon Perritt8c93a302014-09-28 22:35:57 -050010 "time"
Ash Wilson604320e2014-09-10 16:02:28 -040011
Ash Wilson604320e2014-09-10 16:02:28 -040012 "github.com/rackspace/gophercloud"
Jon Perritt90957602015-02-01 17:03:06 -070013 "github.com/rackspace/gophercloud/openstack/objectstorage/v1/accounts"
Ash Wilsonca6f7562014-09-16 15:43:54 -040014 "github.com/rackspace/gophercloud/pagination"
Jon Perritt816d2a02014-03-11 20:49:46 -050015)
16
Jon Perritte90aced2014-10-12 23:24:06 -050017// ListOptsBuilder allows extensions to add additional parameters to the List
18// request.
19type ListOptsBuilder interface {
20 ToObjectListParams() (bool, string, error)
21}
22
Jon Perritt8c93a302014-09-28 22:35:57 -050023// ListOpts is a structure that holds parameters for listing objects.
24type ListOpts struct {
Jon Perritt9415ca72014-11-03 11:58:48 -060025 // Full is a true/false value that represents the amount of object information
26 // returned. If Full is set to true, then the content-type, number of bytes, hash
27 // date last modified, and name are returned. If set to false or not set, then
28 // only the object names are returned.
Jon Perritt8c93a302014-09-28 22:35:57 -050029 Full bool
Jon Perritt04851d32014-10-14 02:07:13 -050030 Limit int `q:"limit"`
31 Marker string `q:"marker"`
32 EndMarker string `q:"end_marker"`
33 Format string `q:"format"`
34 Prefix string `q:"prefix"`
35 Delimiter string `q:"delimiter"`
36 Path string `q:"path"`
Ash Wilsonca6f7562014-09-16 15:43:54 -040037}
38
Jon Perritte90aced2014-10-12 23:24:06 -050039// ToObjectListParams formats a ListOpts into a query string and boolean
40// representing whether to list complete information for each object.
41func (opts ListOpts) ToObjectListParams() (bool, string, error) {
42 q, err := gophercloud.BuildQueryString(opts)
43 if err != nil {
44 return false, "", err
45 }
46 return opts.Full, q.String(), nil
47}
48
Jon Perritt816d2a02014-03-11 20:49:46 -050049// List is a function that retrieves all objects in a container. It also returns the details
50// for the container. To extract only the object information or names, pass the ListResult
Jon Perritteb575642014-04-24 15:16:31 -050051// response to the ExtractInfo or ExtractNames function, respectively.
Jon Perritte90aced2014-10-12 23:24:06 -050052func List(c *gophercloud.ServiceClient, containerName string, opts ListOptsBuilder) pagination.Pager {
53 headers := map[string]string{"Accept": "text/plain", "Content-Type": "text/plain"}
Jon Perritt816d2a02014-03-11 20:49:46 -050054
Jon Perrittea4e3012014-10-09 22:03:19 -050055 url := listURL(c, containerName)
Jon Perrittde47eac2014-09-30 15:34:17 -050056 if opts != nil {
Jon Perritte90aced2014-10-12 23:24:06 -050057 full, query, err := opts.ToObjectListParams()
Jon Perrittde47eac2014-09-30 15:34:17 -050058 if err != nil {
Jon Perrittde47eac2014-09-30 15:34:17 -050059 return pagination.Pager{Err: err}
60 }
Jon Perritte90aced2014-10-12 23:24:06 -050061 url += query
Jon Perritt816d2a02014-03-11 20:49:46 -050062
Jon Perritte90aced2014-10-12 23:24:06 -050063 if full {
64 headers = map[string]string{"Accept": "application/json", "Content-Type": "application/json"}
Jon Perrittde47eac2014-09-30 15:34:17 -050065 }
Ash Wilsonca6f7562014-09-16 15:43:54 -040066 }
67
Ash Wilsonb8b16f82014-10-20 10:19:49 -040068 createPage := func(r pagination.PageResult) pagination.Page {
69 p := ObjectPage{pagination.MarkerPageBase{PageResult: r}}
Ash Wilsonca6f7562014-09-16 15:43:54 -040070 p.MarkerPageBase.Owner = p
71 return p
Jon Perritt816d2a02014-03-11 20:49:46 -050072 }
73
Ash Wilsonca6f7562014-09-16 15:43:54 -040074 pager := pagination.NewPager(c, url, createPage)
75 pager.Headers = headers
76 return pager
Jon Perritt816d2a02014-03-11 20:49:46 -050077}
78
Jon Perritte90aced2014-10-12 23:24:06 -050079// DownloadOptsBuilder allows extensions to add additional parameters to the
80// Download request.
81type DownloadOptsBuilder interface {
82 ToObjectDownloadParams() (map[string]string, string, error)
83}
84
Jon Perritt8c93a302014-09-28 22:35:57 -050085// DownloadOpts is a structure that holds parameters for downloading an object.
86type DownloadOpts struct {
87 IfMatch string `h:"If-Match"`
88 IfModifiedSince time.Time `h:"If-Modified-Since"`
89 IfNoneMatch string `h:"If-None-Match"`
90 IfUnmodifiedSince time.Time `h:"If-Unmodified-Since"`
91 Range string `h:"Range"`
92 Expires string `q:"expires"`
93 MultipartManifest string `q:"multipart-manifest"`
94 Signature string `q:"signature"`
95}
96
Jon Perritte90aced2014-10-12 23:24:06 -050097// ToObjectDownloadParams formats a DownloadOpts into a query string and map of
98// headers.
Paul Querna7dc6fe62014-11-01 08:09:41 -070099func (opts DownloadOpts) ToObjectDownloadParams() (map[string]string, string, error) {
Jon Perritte90aced2014-10-12 23:24:06 -0500100 q, err := gophercloud.BuildQueryString(opts)
101 if err != nil {
102 return nil, "", err
103 }
104 h, err := gophercloud.BuildHeaders(opts)
105 if err != nil {
106 return nil, q.String(), err
107 }
108 return h, q.String(), nil
109}
110
Jon Perritt816d2a02014-03-11 20:49:46 -0500111// Download is a function that retrieves the content and metadata for an object.
Jon Perritte90aced2014-10-12 23:24:06 -0500112// To extract just the content, pass the DownloadResult response to the
113// ExtractContent function.
114func Download(c *gophercloud.ServiceClient, containerName, objectName string, opts DownloadOptsBuilder) DownloadResult {
Jon Perritt5db08922014-09-30 21:32:48 -0500115 var res DownloadResult
Jon Perritt8c93a302014-09-28 22:35:57 -0500116
Jon Perrittea4e3012014-10-09 22:03:19 -0500117 url := downloadURL(c, containerName, objectName)
Ash Wilson77857dc2014-10-22 09:09:02 -0400118 h := c.AuthenticatedHeaders()
Jon Perritt816d2a02014-03-11 20:49:46 -0500119
Jon Perrittde47eac2014-09-30 15:34:17 -0500120 if opts != nil {
Jon Perritte90aced2014-10-12 23:24:06 -0500121 headers, query, err := opts.ToObjectDownloadParams()
Jon Perrittde47eac2014-09-30 15:34:17 -0500122 if err != nil {
Jon Perritt5db08922014-09-30 21:32:48 -0500123 res.Err = err
124 return res
Jon Perrittde47eac2014-09-30 15:34:17 -0500125 }
126
127 for k, v := range headers {
128 h[k] = v
129 }
130
Jon Perritte90aced2014-10-12 23:24:06 -0500131 url += query
Jon Perritt8c93a302014-09-28 22:35:57 -0500132 }
133
Ash Wilson4bf41a32015-02-12 15:52:44 -0500134 resp, err := c.Request("GET", url, gophercloud.RequestOpts{
Jon Perritt816d2a02014-03-11 20:49:46 -0500135 MoreHeaders: h,
Paul Querna9163df22014-11-01 09:38:51 -0700136 OkCodes: []int{200, 304},
Jon Perritt816d2a02014-03-11 20:49:46 -0500137 })
Jon Perritta2c88b22015-05-18 11:23:30 -0600138 if resp != nil {
139 res.Header = resp.Header
140 res.Body = resp.Body
141 }
Jon Perritt5db08922014-09-30 21:32:48 -0500142 res.Err = err
Jamie Hannaford2e784862014-10-27 10:40:27 +0100143
Jon Perritt5db08922014-09-30 21:32:48 -0500144 return res
Jon Perritt8c93a302014-09-28 22:35:57 -0500145}
146
Jon Perritte90aced2014-10-12 23:24:06 -0500147// CreateOptsBuilder allows extensions to add additional parameters to the
148// Create request.
149type CreateOptsBuilder interface {
150 ToObjectCreateParams() (map[string]string, string, error)
151}
152
Jon Perritt8c93a302014-09-28 22:35:57 -0500153// CreateOpts is a structure that holds parameters for creating an object.
154type CreateOpts struct {
155 Metadata map[string]string
156 ContentDisposition string `h:"Content-Disposition"`
157 ContentEncoding string `h:"Content-Encoding"`
Jon Perritte376fa52014-11-03 11:35:48 -0600158 ContentLength int64 `h:"Content-Length"`
Jon Perritt8c93a302014-09-28 22:35:57 -0500159 ContentType string `h:"Content-Type"`
160 CopyFrom string `h:"X-Copy-From"`
161 DeleteAfter int `h:"X-Delete-After"`
162 DeleteAt int `h:"X-Delete-At"`
163 DetectContentType string `h:"X-Detect-Content-Type"`
164 ETag string `h:"ETag"`
165 IfNoneMatch string `h:"If-None-Match"`
166 ObjectManifest string `h:"X-Object-Manifest"`
167 TransferEncoding string `h:"Transfer-Encoding"`
168 Expires string `q:"expires"`
169 MultipartManifest string `q:"multiple-manifest"`
170 Signature string `q:"signature"`
Jon Perritt816d2a02014-03-11 20:49:46 -0500171}
172
Jon Perritte90aced2014-10-12 23:24:06 -0500173// ToObjectCreateParams formats a CreateOpts into a query string and map of
174// headers.
175func (opts CreateOpts) ToObjectCreateParams() (map[string]string, string, error) {
176 q, err := gophercloud.BuildQueryString(opts)
177 if err != nil {
178 return nil, "", err
179 }
180 h, err := gophercloud.BuildHeaders(opts)
181 if err != nil {
182 return nil, q.String(), err
183 }
184
185 for k, v := range opts.Metadata {
186 h["X-Object-Meta-"+k] = v
187 }
188
189 return h, q.String(), nil
190}
191
Jamie Hannaford50fc97d2015-07-16 12:29:01 +0200192// Create is a function that creates a new object or replaces an existing object. If the returned response's ETag
193// 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 -0500194func Create(c *gophercloud.ServiceClient, containerName, objectName string, content io.ReadSeeker, opts CreateOptsBuilder) CreateResult {
Jon Perritt5db08922014-09-30 21:32:48 -0500195 var res CreateResult
Jon Perritt816d2a02014-03-11 20:49:46 -0500196
Jon Perrittea4e3012014-10-09 22:03:19 -0500197 url := createURL(c, containerName, objectName)
Ash Wilson322a7e62015-02-12 16:25:26 -0500198 h := make(map[string]string)
Jon Perritt816d2a02014-03-11 20:49:46 -0500199
Jon Perrittde47eac2014-09-30 15:34:17 -0500200 if opts != nil {
Jon Perritte90aced2014-10-12 23:24:06 -0500201 headers, query, err := opts.ToObjectCreateParams()
Jon Perrittde47eac2014-09-30 15:34:17 -0500202 if err != nil {
Jon Perritt5db08922014-09-30 21:32:48 -0500203 res.Err = err
204 return res
Jon Perrittde47eac2014-09-30 15:34:17 -0500205 }
Jon Perritt8c93a302014-09-28 22:35:57 -0500206
Jon Perrittde47eac2014-09-30 15:34:17 -0500207 for k, v := range headers {
208 h[k] = v
209 }
Jon Perritt816d2a02014-03-11 20:49:46 -0500210
Jon Perritte90aced2014-10-12 23:24:06 -0500211 url += query
Jon Perritt8c93a302014-09-28 22:35:57 -0500212 }
Jon Perritt816d2a02014-03-11 20:49:46 -0500213
Jamie Hannaford08096232015-07-13 12:47:28 +0200214 hash := md5.New()
215 io.Copy(hash, content)
216 localChecksum := hash.Sum(nil)
jrperrittf4ff7272015-07-30 11:42:48 -0600217 fmt.Printf("localChecksum: %s", fmt.Sprintf("%x", localChecksum))
Jamie Hannaford08096232015-07-13 12:47:28 +0200218
jrperritt56d51e92015-07-30 11:50:53 -0600219 h["ETag"] = fmt.Sprintf("%x", localChecksum)
220
221 ropts := gophercloud.RequestOpts{
222 RawBody: content,
223 MoreHeaders: h,
224 }
225
Jamie Hannaford08096232015-07-13 12:47:28 +0200226 for i := 1; i <= 3; i++ {
jrperritt56d51e92015-07-30 11:50:53 -0600227 resp, err := c.Request("PUT", url, ropts)
228 if resp != nil {
229 res.Header = resp.Header
230 }
jrperrittf4ff7272015-07-30 11:42:48 -0600231 fmt.Printf("ETag: %s", resp.Header.Get("ETag"))
Jamie Hannaford08096232015-07-13 12:47:28 +0200232 if resp.Header.Get("ETag") == fmt.Sprintf("%x", localChecksum) {
233 res.Err = err
234 break
235 }
236 if i == 3 {
237 res.Err = fmt.Errorf("Local checksum does not match API ETag header")
238 return res
239 }
240 }
241
Jon Perritt5db08922014-09-30 21:32:48 -0500242 return res
Jon Perritt816d2a02014-03-11 20:49:46 -0500243}
244
Jon Perritte90aced2014-10-12 23:24:06 -0500245// CopyOptsBuilder allows extensions to add additional parameters to the
246// Copy request.
247type CopyOptsBuilder interface {
Jon Perritt04851d32014-10-14 02:07:13 -0500248 ToObjectCopyMap() (map[string]string, error)
Jon Perritte90aced2014-10-12 23:24:06 -0500249}
250
251// CopyOpts is a structure that holds parameters for copying one object to
252// another.
Jon Perritt8c93a302014-09-28 22:35:57 -0500253type CopyOpts struct {
254 Metadata map[string]string
255 ContentDisposition string `h:"Content-Disposition"`
256 ContentEncoding string `h:"Content-Encoding"`
257 ContentType string `h:"Content-Type"`
258 Destination string `h:"Destination,required"`
259}
260
Jon Perritt04851d32014-10-14 02:07:13 -0500261// ToObjectCopyMap formats a CopyOpts into a map of headers.
262func (opts CopyOpts) ToObjectCopyMap() (map[string]string, error) {
Jon Perritte90aced2014-10-12 23:24:06 -0500263 if opts.Destination == "" {
264 return nil, fmt.Errorf("Required CopyOpts field 'Destination' not set.")
265 }
266 h, err := gophercloud.BuildHeaders(opts)
267 if err != nil {
268 return nil, err
269 }
270 for k, v := range opts.Metadata {
271 h["X-Object-Meta-"+k] = v
272 }
273 return h, nil
274}
275
Jon Perritt816d2a02014-03-11 20:49:46 -0500276// Copy is a function that copies one object to another.
Jon Perritte90aced2014-10-12 23:24:06 -0500277func Copy(c *gophercloud.ServiceClient, containerName, objectName string, opts CopyOptsBuilder) CopyResult {
Jon Perritt5db08922014-09-30 21:32:48 -0500278 var res CopyResult
Ash Wilson77857dc2014-10-22 09:09:02 -0400279 h := c.AuthenticatedHeaders()
Jon Perritt816d2a02014-03-11 20:49:46 -0500280
Jon Perritt04851d32014-10-14 02:07:13 -0500281 headers, err := opts.ToObjectCopyMap()
Jon Perritt8c93a302014-09-28 22:35:57 -0500282 if err != nil {
Jon Perritt5db08922014-09-30 21:32:48 -0500283 res.Err = err
284 return res
Jon Perritt816d2a02014-03-11 20:49:46 -0500285 }
Jon Perritte90aced2014-10-12 23:24:06 -0500286
Jon Perritt8c93a302014-09-28 22:35:57 -0500287 for k, v := range headers {
Jon Perritt816d2a02014-03-11 20:49:46 -0500288 h[k] = v
289 }
290
Jon Perrittea4e3012014-10-09 22:03:19 -0500291 url := copyURL(c, containerName, objectName)
Ash Wilson4bf41a32015-02-12 15:52:44 -0500292 resp, err := c.Request("COPY", url, gophercloud.RequestOpts{
Jon Perritt8c93a302014-09-28 22:35:57 -0500293 MoreHeaders: h,
294 OkCodes: []int{201},
295 })
Jon Perritta2c88b22015-05-18 11:23:30 -0600296 if resp != nil {
297 res.Header = resp.Header
298 }
jrperrittf7a8e282014-10-28 10:00:48 -0500299 res.Err = err
Jon Perritt5db08922014-09-30 21:32:48 -0500300 return res
Jon Perritt8c93a302014-09-28 22:35:57 -0500301}
302
Jon Perritte90aced2014-10-12 23:24:06 -0500303// DeleteOptsBuilder allows extensions to add additional parameters to the
304// Delete request.
305type DeleteOptsBuilder interface {
Jon Perritt26780d52014-10-14 11:35:58 -0500306 ToObjectDeleteQuery() (string, error)
Jon Perritte90aced2014-10-12 23:24:06 -0500307}
308
Jon Perritt8c93a302014-09-28 22:35:57 -0500309// DeleteOpts is a structure that holds parameters for deleting an object.
310type DeleteOpts struct {
311 MultipartManifest string `q:"multipart-manifest"`
312}
313
Jon Perritt26780d52014-10-14 11:35:58 -0500314// ToObjectDeleteQuery formats a DeleteOpts into a query string.
315func (opts DeleteOpts) ToObjectDeleteQuery() (string, error) {
Jon Perritte90aced2014-10-12 23:24:06 -0500316 q, err := gophercloud.BuildQueryString(opts)
317 if err != nil {
318 return "", err
319 }
320 return q.String(), nil
321}
322
Jon Perritt8c93a302014-09-28 22:35:57 -0500323// Delete is a function that deletes an object.
Jon Perritte90aced2014-10-12 23:24:06 -0500324func Delete(c *gophercloud.ServiceClient, containerName, objectName string, opts DeleteOptsBuilder) DeleteResult {
Jon Perritt5db08922014-09-30 21:32:48 -0500325 var res DeleteResult
Jon Perrittea4e3012014-10-09 22:03:19 -0500326 url := deleteURL(c, containerName, objectName)
Jon Perritt8c93a302014-09-28 22:35:57 -0500327
Jon Perrittde47eac2014-09-30 15:34:17 -0500328 if opts != nil {
Jon Perritt26780d52014-10-14 11:35:58 -0500329 query, err := opts.ToObjectDeleteQuery()
Jon Perrittde47eac2014-09-30 15:34:17 -0500330 if err != nil {
Jon Perritt5db08922014-09-30 21:32:48 -0500331 res.Err = err
332 return res
Jon Perrittde47eac2014-09-30 15:34:17 -0500333 }
Jon Perritte90aced2014-10-12 23:24:06 -0500334 url += query
Jon Perritt8c93a302014-09-28 22:35:57 -0500335 }
336
Jamie Hannaford1d27afa2015-03-24 16:20:45 +0100337 resp, err := c.Delete(url, nil)
Jon Perritta2c88b22015-05-18 11:23:30 -0600338 if resp != nil {
339 res.Header = resp.Header
340 }
Jon Perritt10a7ec12014-10-27 11:29:33 -0500341 res.Err = err
Jon Perritt5db08922014-09-30 21:32:48 -0500342 return res
Jon Perritt8c93a302014-09-28 22:35:57 -0500343}
344
Jon Perritte90aced2014-10-12 23:24:06 -0500345// GetOptsBuilder allows extensions to add additional parameters to the
346// Get request.
347type GetOptsBuilder interface {
Jon Perritt26780d52014-10-14 11:35:58 -0500348 ToObjectGetQuery() (string, error)
Jon Perritte90aced2014-10-12 23:24:06 -0500349}
350
Jon Perritt8c93a302014-09-28 22:35:57 -0500351// GetOpts is a structure that holds parameters for getting an object's metadata.
352type GetOpts struct {
353 Expires string `q:"expires"`
354 Signature string `q:"signature"`
355}
356
Jon Perritt26780d52014-10-14 11:35:58 -0500357// ToObjectGetQuery formats a GetOpts into a query string.
358func (opts GetOpts) ToObjectGetQuery() (string, error) {
Jon Perritte90aced2014-10-12 23:24:06 -0500359 q, err := gophercloud.BuildQueryString(opts)
360 if err != nil {
361 return "", err
362 }
363 return q.String(), nil
364}
365
Jon Perritt8c93a302014-09-28 22:35:57 -0500366// Get is a function that retrieves the metadata of an object. To extract just the custom
367// metadata, pass the GetResult response to the ExtractMetadata function.
Jon Perritte90aced2014-10-12 23:24:06 -0500368func Get(c *gophercloud.ServiceClient, containerName, objectName string, opts GetOptsBuilder) GetResult {
Jon Perritt5db08922014-09-30 21:32:48 -0500369 var res GetResult
Jon Perrittea4e3012014-10-09 22:03:19 -0500370 url := getURL(c, containerName, objectName)
Jon Perrittde47eac2014-09-30 15:34:17 -0500371
372 if opts != nil {
Jon Perritt26780d52014-10-14 11:35:58 -0500373 query, err := opts.ToObjectGetQuery()
Jon Perrittde47eac2014-09-30 15:34:17 -0500374 if err != nil {
Jon Perritt5db08922014-09-30 21:32:48 -0500375 res.Err = err
376 return res
Jon Perrittde47eac2014-09-30 15:34:17 -0500377 }
Jon Perritte90aced2014-10-12 23:24:06 -0500378 url += query
Jon Perritt8c93a302014-09-28 22:35:57 -0500379 }
380
Ash Wilson4bf41a32015-02-12 15:52:44 -0500381 resp, err := c.Request("HEAD", url, gophercloud.RequestOpts{
382 OkCodes: []int{200, 204},
Jon Perritt8c93a302014-09-28 22:35:57 -0500383 })
Jon Perritta2c88b22015-05-18 11:23:30 -0600384 if resp != nil {
385 res.Header = resp.Header
386 }
Jon Perritt5db08922014-09-30 21:32:48 -0500387 res.Err = err
Jon Perritt5db08922014-09-30 21:32:48 -0500388 return res
Jon Perritt8c93a302014-09-28 22:35:57 -0500389}
390
Jon Perritte90aced2014-10-12 23:24:06 -0500391// UpdateOptsBuilder allows extensions to add additional parameters to the
392// Update request.
393type UpdateOptsBuilder interface {
Jon Perritt04851d32014-10-14 02:07:13 -0500394 ToObjectUpdateMap() (map[string]string, error)
Jon Perritte90aced2014-10-12 23:24:06 -0500395}
396
Jon Perritt8c93a302014-09-28 22:35:57 -0500397// UpdateOpts is a structure that holds parameters for updating, creating, or deleting an
398// object's metadata.
399type UpdateOpts struct {
400 Metadata map[string]string
401 ContentDisposition string `h:"Content-Disposition"`
402 ContentEncoding string `h:"Content-Encoding"`
403 ContentType string `h:"Content-Type"`
404 DeleteAfter int `h:"X-Delete-After"`
405 DeleteAt int `h:"X-Delete-At"`
406 DetectContentType bool `h:"X-Detect-Content-Type"`
407}
408
Jon Perritt04851d32014-10-14 02:07:13 -0500409// ToObjectUpdateMap formats a UpdateOpts into a map of headers.
410func (opts UpdateOpts) ToObjectUpdateMap() (map[string]string, error) {
Jon Perritte90aced2014-10-12 23:24:06 -0500411 h, err := gophercloud.BuildHeaders(opts)
412 if err != nil {
413 return nil, err
414 }
415 for k, v := range opts.Metadata {
416 h["X-Object-Meta-"+k] = v
417 }
418 return h, nil
419}
420
Jon Perritt8c93a302014-09-28 22:35:57 -0500421// Update is a function that creates, updates, or deletes an object's metadata.
Jon Perritte90aced2014-10-12 23:24:06 -0500422func Update(c *gophercloud.ServiceClient, containerName, objectName string, opts UpdateOptsBuilder) UpdateResult {
Jon Perritt5db08922014-09-30 21:32:48 -0500423 var res UpdateResult
Ash Wilson77857dc2014-10-22 09:09:02 -0400424 h := c.AuthenticatedHeaders()
Jon Perritt8c93a302014-09-28 22:35:57 -0500425
Jon Perrittde47eac2014-09-30 15:34:17 -0500426 if opts != nil {
Jon Perritt04851d32014-10-14 02:07:13 -0500427 headers, err := opts.ToObjectUpdateMap()
Jon Perrittde47eac2014-09-30 15:34:17 -0500428 if err != nil {
Jon Perritt5db08922014-09-30 21:32:48 -0500429 res.Err = err
430 return res
Jon Perrittde47eac2014-09-30 15:34:17 -0500431 }
Jon Perritt8c93a302014-09-28 22:35:57 -0500432
Jon Perrittde47eac2014-09-30 15:34:17 -0500433 for k, v := range headers {
434 h[k] = v
435 }
Jon Perritt8c93a302014-09-28 22:35:57 -0500436 }
437
Jon Perrittea4e3012014-10-09 22:03:19 -0500438 url := updateURL(c, containerName, objectName)
Ash Wilson4bf41a32015-02-12 15:52:44 -0500439 resp, err := c.Request("POST", url, gophercloud.RequestOpts{
Jon Perritt816d2a02014-03-11 20:49:46 -0500440 MoreHeaders: h,
Jon Perritt816d2a02014-03-11 20:49:46 -0500441 })
Jon Perritta2c88b22015-05-18 11:23:30 -0600442 if resp != nil {
443 res.Header = resp.Header
444 }
Jon Perritt5db08922014-09-30 21:32:48 -0500445 res.Err = err
446 return res
Jon Perritt816d2a02014-03-11 20:49:46 -0500447}
Jon Perritt90957602015-02-01 17:03:06 -0700448
449// HTTPMethod represents an HTTP method string (e.g. "GET").
450type HTTPMethod string
451
452var (
453 // GET represents an HTTP "GET" method.
454 GET HTTPMethod = "GET"
455 // POST represents an HTTP "POST" method.
456 POST HTTPMethod = "POST"
457)
458
459// CreateTempURLOpts are options for creating a temporary URL for an object.
460type CreateTempURLOpts struct {
Jon Perritt28792af2015-02-02 11:00:04 -0700461 // (REQUIRED) Method is the HTTP method to allow for users of the temp URL. Valid values
Jon Perritt90957602015-02-01 17:03:06 -0700462 // are "GET" and "POST".
463 Method HTTPMethod
Jon Perritt28792af2015-02-02 11:00:04 -0700464 // (REQUIRED) TTL is the number of seconds the temp URL should be active.
Jon Perritt90957602015-02-01 17:03:06 -0700465 TTL int
Jon Perritt28792af2015-02-02 11:00:04 -0700466 // (Optional) Split is the string on which to split the object URL. Since only
467 // the object path is used in the hash, the object URL needs to be parsed. If
468 // empty, the default OpenStack URL split point will be used ("/v1/").
469 Split string
Jon Perritt90957602015-02-01 17:03:06 -0700470}
471
472// CreateTempURL is a function for creating a temporary URL for an object. It
473// allows users to have "GET" or "POST" access to a particular tenant's object
474// for a limited amount of time.
475func CreateTempURL(c *gophercloud.ServiceClient, containerName, objectName string, opts CreateTempURLOpts) (string, error) {
Jon Perritt28792af2015-02-02 11:00:04 -0700476 if opts.Split == "" {
477 opts.Split = "/v1/"
478 }
Jon Perritt90957602015-02-01 17:03:06 -0700479 duration := time.Duration(opts.TTL) * time.Second
480 expiry := time.Now().Add(duration).Unix()
481 getHeader, err := accounts.Get(c, nil).Extract()
482 if err != nil {
483 return "", err
484 }
485 secretKey := []byte(getHeader.TempURLKey)
486 url := getURL(c, containerName, objectName)
Jon Perritt28792af2015-02-02 11:00:04 -0700487 splitPath := strings.Split(url, opts.Split)
Jon Perritt90957602015-02-01 17:03:06 -0700488 baseURL, objectPath := splitPath[0], splitPath[1]
Jon Perritt28792af2015-02-02 11:00:04 -0700489 objectPath = opts.Split + objectPath
Jon Perritt90957602015-02-01 17:03:06 -0700490 body := fmt.Sprintf("%s\n%d\n%s", opts.Method, expiry, objectPath)
491 hash := hmac.New(sha1.New, secretKey)
492 hash.Write([]byte(body))
493 hexsum := fmt.Sprintf("%x", hash.Sum(nil))
494 return fmt.Sprintf("%s%s?temp_url_sig=%s&temp_url_expires=%d", baseURL, objectPath, hexsum, expiry), nil
495}