Import upstream version 2.0.0+git20210122.1.30f52fe
Debian Janitor
2 years ago
0 | 0 | language: go |
1 | 1 | sudo: false |
2 | 2 | |
3 | arch: | |
4 | - amd64 | |
5 | - ppc64le | |
6 | ||
7 | go_import_path: github.com/ncw/swift | |
8 | ||
3 | 9 | go: |
4 | - 1.2.x | |
5 | - 1.3.x | |
6 | - 1.4.x | |
7 | - 1.5.x | |
8 | - 1.6.x | |
9 | - 1.7.x | |
10 | - 1.8.x | |
11 | - 1.9.x | |
12 | - 1.10.x | |
13 | - 1.11.x | |
14 | - 1.12.x | |
15 | 10 | - 1.13.x |
16 | 11 | - 1.14.x |
12 | - 1.15.x | |
17 | 13 | - master |
18 | 14 | |
19 | 15 | matrix: |
20 | 16 | include: |
21 | - go: 1.14.x | |
17 | - go: 1.15.x | |
22 | 18 | env: TEST_REAL_SERVER=rackspace |
23 | - go: 1.14.x | |
19 | - go: 1.15.x | |
20 | env: TEST_REAL_SERVER=memset | |
21 | - go: 1.15.x | |
22 | arch: ppc64le | |
23 | env: TEST_REAL_SERVER=rackspace | |
24 | - go: 1.15.x | |
25 | arch: ppc64le | |
24 | 26 | env: TEST_REAL_SERVER=memset |
25 | 27 | allow_failures: |
26 | - go: 1.14.x | |
28 | - go: 1.15.x | |
27 | 29 | env: TEST_REAL_SERVER=rackspace |
28 | - go: 1.14.x | |
30 | - go: 1.15.x | |
31 | env: TEST_REAL_SERVER=memset | |
32 | - go: 1.15.x | |
33 | arch: ppc64le | |
34 | env: TEST_REAL_SERVER=rackspace | |
35 | - go: 1.15.x | |
36 | arch: ppc64le | |
29 | 37 | env: TEST_REAL_SERVER=memset |
30 | 38 | install: go test -i ./... |
31 | 39 | script: |
0 | 0 | Swift |
1 | 1 | ===== |
2 | 2 | |
3 | This package provides an easy to use library for interfacing with | |
4 | Swift / Openstack Object Storage / Rackspace cloud files from the Go | |
5 | Language | |
3 | This package provides an easy to use library for interfacing with Swift / Openstack Object Storage / Rackspace cloud | |
4 | files from the Go Language | |
6 | 5 | |
7 | 6 | See here for package docs |
8 | 7 | |
9 | http://godoc.org/github.com/ncw/swift | |
8 | http://godoc.org/github.com/ncw/swift/v2 | |
10 | 9 | |
11 | [![Build Status](https://api.travis-ci.org/ncw/swift.svg?branch=master)](https://travis-ci.org/ncw/swift) [![GoDoc](https://godoc.org/github.com/ncw/swift?status.svg)](https://godoc.org/github.com/ncw/swift) | |
10 | [![Build Status](https://api.travis-ci.org/ncw/swift.svg?branch=master)](https://travis-ci.org/ncw/swift) [![GoDoc](https://godoc.org/github.com/ncw/swift/v2?status.svg)](https://godoc.org/github.com/ncw/swift/v2) | |
12 | 11 | |
13 | 12 | Install |
14 | 13 | ------- |
15 | 14 | |
16 | 15 | Use go to install the library |
17 | 16 | |
18 | go get github.com/ncw/swift | |
17 | go get github.com/ncw/swift/v2 | |
19 | 18 | |
20 | 19 | Usage |
21 | 20 | ----- |
22 | 21 | |
23 | 22 | See here for full package docs |
24 | 23 | |
25 | - http://godoc.org/github.com/ncw/swift | |
24 | - http://godoc.org/github.com/ncw/swift/v2 | |
26 | 25 | |
27 | 26 | Here is a short example from the docs |
27 | ||
28 | 28 | ```go |
29 | import "github.com/ncw/swift" | |
29 | import "github.com/ncw/swift/v2" | |
30 | 30 | |
31 | 31 | // Create a connection |
32 | 32 | c := swift.Connection{ |
33 | UserName: "user", | |
34 | ApiKey: "key", | |
35 | AuthUrl: "auth_url", | |
36 | Domain: "domain", // Name of the domain (v3 auth only) | |
37 | Tenant: "tenant", // Name of the tenant (v2 auth only) | |
33 | UserName: "user", | |
34 | ApiKey: "key", | |
35 | AuthUrl: "auth_url", | |
36 | Domain: "domain", // Name of the domain (v3 auth only) | |
37 | Tenant: "tenant", // Name of the tenant (v2 auth only) | |
38 | 38 | } |
39 | 39 | // Authenticate |
40 | 40 | err := c.Authenticate() |
41 | 41 | if err != nil { |
42 | panic(err) | |
42 | panic(err) | |
43 | 43 | } |
44 | 44 | // List all the containers |
45 | 45 | containers, err := c.ContainerNames(nil) |
46 | 46 | fmt.Println(containers) |
47 | 47 | // etc... |
48 | 48 | ``` |
49 | ||
50 | Migrating from `v1` | |
51 | ----- | |
52 | The library has current major version v2. If you want to migrate from the first version of | |
53 | library `github.com/ncw/swift` you have to explicitly add the `/v2` suffix to the imports. | |
54 | ||
55 | Most of the exported functions were added a new `context.Context` parameter in the `v2`, which you will have to provide | |
56 | when migrating. | |
49 | 57 | |
50 | 58 | Additions |
51 | 59 | --------- |
55 | 63 | Testing |
56 | 64 | ------- |
57 | 65 | |
58 | To run the tests you can either use an embedded fake Swift server | |
59 | either use a real Openstack Swift server or a Rackspace Cloud files account. | |
66 | To run the tests you can either use an embedded fake Swift server either use a real Openstack Swift server or a | |
67 | Rackspace Cloud files account. | |
60 | 68 | |
61 | When using a real Swift server, you need to set these environment variables | |
62 | before running the tests | |
69 | When using a real Swift server, you need to set these environment variables before running the tests | |
63 | 70 | |
64 | 71 | export SWIFT_API_USER='user' |
65 | 72 | export SWIFT_API_KEY='key' |
98 | 105 | License |
99 | 106 | ------- |
100 | 107 | |
101 | This is free software under the terms of MIT license (check COPYING file | |
102 | included in this package). | |
108 | This is free software under the terms of MIT license (check COPYING file included in this package). | |
103 | 109 | |
104 | 110 | Contact and support |
105 | 111 | ------------------- |
160 | 166 | - Brandon WELSCH <dev@brandon-welsch.eu> |
161 | 167 | - Damien Tournoud <damien@platform.sh> |
162 | 168 | - Pedro Kiefer <pedro@kiefer.com.br> |
169 | - Martin Chodur <m.chodur@seznam.cz> | |
170 | - Devendra <devendranath.thadi3@gmail.com> |
1 | 1 | |
2 | 2 | import ( |
3 | 3 | "bytes" |
4 | "context" | |
4 | 5 | "encoding/json" |
5 | 6 | "net/http" |
6 | 7 | "net/url" |
13 | 14 | // This encapsulates the different authentication schemes in use |
14 | 15 | type Authenticator interface { |
15 | 16 | // Request creates an http.Request for the auth - return nil if not needed |
16 | Request(*Connection) (*http.Request, error) | |
17 | Request(context.Context, *Connection) (*http.Request, error) | |
17 | 18 | // Response parses the http.Response |
18 | Response(resp *http.Response) error | |
19 | Response(ctx context.Context, resp *http.Response) error | |
19 | 20 | // The public storage URL - set Internal to true to read |
20 | 21 | // internal/service net URL |
21 | 22 | StorageUrl(Internal bool) string |
87 | 88 | } |
88 | 89 | |
89 | 90 | // v1 Authentication - make request |
90 | func (auth *v1Auth) Request(c *Connection) (*http.Request, error) { | |
91 | req, err := http.NewRequest("GET", c.AuthUrl, nil) | |
91 | func (auth *v1Auth) Request(ctx context.Context, c *Connection) (*http.Request, error) { | |
92 | req, err := http.NewRequestWithContext(ctx, "GET", c.AuthUrl, nil) | |
92 | 93 | if err != nil { |
93 | 94 | return nil, err |
94 | 95 | } |
99 | 100 | } |
100 | 101 | |
101 | 102 | // v1 Authentication - read response |
102 | func (auth *v1Auth) Response(resp *http.Response) error { | |
103 | func (auth *v1Auth) Response(_ context.Context, resp *http.Response) error { | |
103 | 104 | auth.Headers = resp.Header |
104 | 105 | return nil |
105 | 106 | } |
140 | 141 | } |
141 | 142 | |
142 | 143 | // v2 Authentication - make request |
143 | func (auth *v2Auth) Request(c *Connection) (*http.Request, error) { | |
144 | func (auth *v2Auth) Request(ctx context.Context, c *Connection) (*http.Request, error) { | |
144 | 145 | auth.Region = c.Region |
145 | 146 | // Toggle useApiKey if not first run and not OK yet |
146 | 147 | if auth.notFirst && !auth.useApiKeyOk { |
175 | 176 | url += "/" |
176 | 177 | } |
177 | 178 | url += "tokens" |
178 | req, err := http.NewRequest("POST", url, bytes.NewBuffer(body)) | |
179 | req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer(body)) | |
179 | 180 | if err != nil { |
180 | 181 | return nil, err |
181 | 182 | } |
185 | 186 | } |
186 | 187 | |
187 | 188 | // v2 Authentication - read response |
188 | func (auth *v2Auth) Response(resp *http.Response) error { | |
189 | func (auth *v2Auth) Response(_ context.Context, resp *http.Response) error { | |
189 | 190 | auth.Auth = new(v2AuthResponse) |
190 | 191 | err := readJson(resp, auth.Auth) |
191 | 192 | // If successfully read Auth then no need to toggle useApiKey any more |
1 | 1 | |
2 | 2 | import ( |
3 | 3 | "bytes" |
4 | "context" | |
4 | 5 | "encoding/json" |
5 | 6 | "fmt" |
6 | 7 | "net/http" |
121 | 122 | Headers http.Header |
122 | 123 | } |
123 | 124 | |
124 | func (auth *v3Auth) Request(c *Connection) (*http.Request, error) { | |
125 | func (auth *v3Auth) Request(ctx context.Context, c *Connection) (*http.Request, error) { | |
125 | 126 | auth.Region = c.Region |
126 | 127 | |
127 | 128 | var v3i interface{} |
241 | 242 | url += "/" |
242 | 243 | } |
243 | 244 | url += "auth/tokens" |
244 | req, err := http.NewRequest("POST", url, bytes.NewBuffer(body)) | |
245 | req, err := http.NewRequestWithContext(ctx, "POST", url, bytes.NewBuffer(body)) | |
245 | 246 | if err != nil { |
246 | 247 | return nil, err |
247 | 248 | } |
250 | 251 | return req, nil |
251 | 252 | } |
252 | 253 | |
253 | func (auth *v3Auth) Response(resp *http.Response) error { | |
254 | func (auth *v3Auth) Response(_ context.Context, resp *http.Response) error { | |
254 | 255 | auth.Auth = &v3AuthResponse{} |
255 | 256 | auth.Headers = resp.Header |
256 | 257 | err := readJson(resp, auth.Auth) |
0 | 0 | package swift |
1 | 1 | |
2 | 2 | import ( |
3 | "context" | |
3 | 4 | "os" |
4 | 5 | "strings" |
5 | 6 | ) |
13 | 14 | // returning an object which satisfies io.Writer, io.Seeker, io.Closer |
14 | 15 | // and io.ReaderFrom. The flags are as passes to the |
15 | 16 | // largeObjectCreate method. |
16 | func (c *Connection) DynamicLargeObjectCreateFile(opts *LargeObjectOpts) (LargeObjectFile, error) { | |
17 | lo, err := c.largeObjectCreate(opts) | |
17 | func (c *Connection) DynamicLargeObjectCreateFile(ctx context.Context, opts *LargeObjectOpts) (LargeObjectFile, error) { | |
18 | lo, err := c.largeObjectCreate(ctx, opts) | |
18 | 19 | if err != nil { |
19 | 20 | return nil, err |
20 | 21 | } |
27 | 28 | // DynamicLargeObjectCreate creates or truncates an existing dynamic |
28 | 29 | // large object returning a writeable object. This sets opts.Flags to |
29 | 30 | // an appropriate value before calling DynamicLargeObjectCreateFile |
30 | func (c *Connection) DynamicLargeObjectCreate(opts *LargeObjectOpts) (LargeObjectFile, error) { | |
31 | func (c *Connection) DynamicLargeObjectCreate(ctx context.Context, opts *LargeObjectOpts) (LargeObjectFile, error) { | |
31 | 32 | opts.Flags = os.O_TRUNC | os.O_CREATE |
32 | return c.DynamicLargeObjectCreateFile(opts) | |
33 | return c.DynamicLargeObjectCreateFile(ctx, opts) | |
33 | 34 | } |
34 | 35 | |
35 | 36 | // DynamicLargeObjectDelete deletes a dynamic large object and all of its segments. |
36 | func (c *Connection) DynamicLargeObjectDelete(container string, path string) error { | |
37 | return c.LargeObjectDelete(container, path) | |
37 | func (c *Connection) DynamicLargeObjectDelete(ctx context.Context, container string, path string) error { | |
38 | return c.LargeObjectDelete(ctx, container, path) | |
38 | 39 | } |
39 | 40 | |
40 | 41 | // DynamicLargeObjectMove moves a dynamic large object from srcContainer, srcObjectName to dstContainer, dstObjectName |
41 | func (c *Connection) DynamicLargeObjectMove(srcContainer string, srcObjectName string, dstContainer string, dstObjectName string) error { | |
42 | info, headers, err := c.Object(srcContainer, srcObjectName) | |
42 | func (c *Connection) DynamicLargeObjectMove(ctx context.Context, srcContainer string, srcObjectName string, dstContainer string, dstObjectName string) error { | |
43 | info, headers, err := c.Object(ctx, srcContainer, srcObjectName) | |
43 | 44 | if err != nil { |
44 | 45 | return err |
45 | 46 | } |
46 | 47 | |
47 | 48 | segmentContainer, segmentPath := parseFullPath(headers["X-Object-Manifest"]) |
48 | if err := c.createDLOManifest(dstContainer, dstObjectName, segmentContainer+"/"+segmentPath, info.ContentType, sanitizeLargeObjectMoveHeaders(headers)); err != nil { | |
49 | if err := c.createDLOManifest(ctx, dstContainer, dstObjectName, segmentContainer+"/"+segmentPath, info.ContentType, sanitizeLargeObjectMoveHeaders(headers)); err != nil { | |
49 | 50 | return err |
50 | 51 | } |
51 | 52 | |
52 | if err := c.ObjectDelete(srcContainer, srcObjectName); err != nil { | |
53 | if err := c.ObjectDelete(ctx, srcContainer, srcObjectName); err != nil { | |
53 | 54 | return err |
54 | 55 | } |
55 | 56 | |
67 | 68 | } |
68 | 69 | |
69 | 70 | // createDLOManifest creates a dynamic large object manifest |
70 | func (c *Connection) createDLOManifest(container string, objectName string, prefix string, contentType string, headers Headers) error { | |
71 | func (c *Connection) createDLOManifest(ctx context.Context, container string, objectName string, prefix string, contentType string, headers Headers) error { | |
71 | 72 | if headers == nil { |
72 | 73 | headers = make(Headers) |
73 | 74 | } |
74 | 75 | headers["X-Object-Manifest"] = prefix |
75 | manifest, err := c.ObjectCreate(container, objectName, false, "", contentType, headers) | |
76 | manifest, err := c.ObjectCreate(ctx, container, objectName, false, "", contentType, headers) | |
76 | 77 | if err != nil { |
77 | 78 | return err |
78 | 79 | } |
86 | 87 | |
87 | 88 | // Close satisfies the io.Closer interface |
88 | 89 | func (file *DynamicLargeObjectCreateFile) Close() error { |
89 | return file.Flush() | |
90 | return file.CloseWithContext(context.Background()) | |
90 | 91 | } |
91 | 92 | |
92 | func (file *DynamicLargeObjectCreateFile) Flush() error { | |
93 | err := file.conn.createDLOManifest(file.container, file.objectName, file.segmentContainer+"/"+file.prefix, file.contentType, file.headers) | |
93 | func (file *DynamicLargeObjectCreateFile) CloseWithContext(ctx context.Context) error { | |
94 | return file.Flush(ctx) | |
95 | } | |
96 | ||
97 | func (file *DynamicLargeObjectCreateFile) Flush(ctx context.Context) error { | |
98 | err := file.conn.createDLOManifest(ctx, file.container, file.objectName, file.segmentContainer+"/"+file.prefix, file.contentType, file.headers) | |
94 | 99 | if err != nil { |
95 | 100 | return err |
96 | 101 | } |
97 | return file.conn.waitForSegmentsToShowUp(file.container, file.objectName, file.Size()) | |
102 | return file.conn.waitForSegmentsToShowUp(ctx, file.container, file.objectName, file.Size()) | |
98 | 103 | } |
99 | 104 | |
100 | func (c *Connection) getAllDLOSegments(segmentContainer, segmentPath string) ([]Object, error) { | |
105 | func (c *Connection) getAllDLOSegments(ctx context.Context, segmentContainer, segmentPath string) ([]Object, error) { | |
101 | 106 | //a simple container listing works 99.9% of the time |
102 | segments, err := c.ObjectsAll(segmentContainer, &ObjectsOpts{Prefix: segmentPath}) | |
107 | segments, err := c.ObjectsAll(ctx, segmentContainer, &ObjectsOpts{Prefix: segmentPath}) | |
103 | 108 | if err != nil { |
104 | 109 | return nil, err |
105 | 110 | } |
125 | 130 | //guaranteed to return the correct metadata, except for the pathological |
126 | 131 | //case of an outage of large parts of the Swift cluster or its network, |
127 | 132 | //since every segment is only written once.) |
128 | segment, _, err := c.Object(segmentContainer, segmentName) | |
133 | segment, _, err := c.Object(ctx, segmentContainer, segmentName) | |
129 | 134 | switch err { |
130 | 135 | case nil: |
131 | 136 | //found new segment -> add it in the correct position and keep |
1 | 1 | |
2 | 2 | import ( |
3 | 3 | "bytes" |
4 | "github.com/ncw/swift/swifttest" | |
4 | "context" | |
5 | "github.com/ncw/swift/v2/swifttest" | |
5 | 6 | "net/http" |
6 | 7 | "testing" |
7 | 8 | "time" |
46 | 47 | Domain: "Default", |
47 | 48 | AuthVersion: 1, |
48 | 49 | } |
49 | err = swiftCon.Authenticate() | |
50 | err = swiftCon.Authenticate(context.Background()) | |
50 | 51 | return &swiftCon, err |
51 | 52 | } |
52 | 53 | func TestCases(t *testing.T) { |
62 | 63 | } |
63 | 64 | |
64 | 65 | func createContainers(containers []string, t *testing.T) { |
66 | ctx := context.Background() | |
65 | 67 | for i := 0; i < len(containers); i++ { |
66 | err = con.ContainerCreate(containers[i], nil) // Create container | |
68 | err = con.ContainerCreate(ctx, containers[i], nil) // Create container | |
67 | 69 | if err != nil { |
68 | 70 | t.Errorf("Fail at create container %s", containers[i]) |
69 | 71 | } |
71 | 73 | } |
72 | 74 | |
73 | 75 | func createDynamicObject(container, object string, t *testing.T) { |
76 | ctx := context.Background() | |
74 | 77 | metadata := map[string]string{} |
75 | 78 | metadata["Custom-Field"] = "SomeValue" |
76 | 79 | ops := LargeObjectOpts{ |
82 | 85 | SegmentContainer: segmentContainer, // Name of the container to place segments |
83 | 86 | SegmentPrefix: "sg", // Prefix to use for the segments |
84 | 87 | } |
85 | bigfile, err := con.DynamicLargeObjectCreate(&ops) | |
88 | bigfile, err := con.DynamicLargeObjectCreate(ctx, &ops) | |
86 | 89 | if err != nil { |
87 | 90 | t.Errorf("Fail at dynamic create Large Object") |
88 | 91 | } |
89 | bigfile.Write(filecontent) | |
90 | bigfile.Close() | |
92 | bigfile.WriteWithContext(ctx, filecontent) | |
93 | bigfile.CloseWithContext(ctx) | |
91 | 94 | checkObject(container, object, t) |
92 | 95 | } |
93 | 96 | func checkObject(container, object string, t *testing.T) { |
94 | info, header, err := con.Object(container, object) | |
97 | ctx := context.Background() | |
98 | info, header, err := con.Object(ctx, container, object) | |
95 | 99 | if err != nil { |
96 | 100 | t.Errorf("Fail at get Large Object metadata: %s", err.Error()) |
97 | 101 | } |
105 | 109 | t.Errorf("Fail: lost custom metadata header") |
106 | 110 | } |
107 | 111 | |
108 | content, err := con.ObjectGetBytes(container, object) | |
112 | content, err := con.ObjectGetBytes(ctx, container, object) | |
109 | 113 | if err != nil { |
110 | 114 | t.Errorf("Fail at read Large Object : %s", err.Error()) |
111 | 115 | } |
115 | 119 | |
116 | 120 | } |
117 | 121 | func checkNotExistObject(container, object string, t *testing.T) { |
118 | _, _, err = con.Object(container, object) | |
122 | _, _, err = con.Object(context.Background(), container, object) | |
119 | 123 | if err == nil || err.Error() != "Object Not Found" { |
120 | 124 | t.Errorf("Fail at checkNotExistObject object: %s", err) |
121 | 125 | } |
122 | 126 | } |
123 | 127 | func moveDynamicObject(sc, so, dc, do string, t *testing.T) { |
124 | err = con.DynamicLargeObjectMove(sc, so, dc, do) | |
128 | err = con.DynamicLargeObjectMove(context.Background(), sc, so, dc, do) | |
125 | 129 | if err != nil { |
126 | 130 | t.Errorf("Fail at dynamic move Large Object: %s", err.Error()) |
127 | 131 | } |
129 | 133 | checkObject(dc, do, t) |
130 | 134 | } |
131 | 135 | func deleteDynamicObject(container, object string, t *testing.T) { |
132 | err = con.DynamicLargeObjectDelete(container, object) | |
136 | ctx := context.Background() | |
137 | err = con.DynamicLargeObjectDelete(ctx, container, object) | |
133 | 138 | if err != nil { |
134 | 139 | t.Errorf("Fail at delte dynamic Large Object: %s", err.Error()) |
135 | 140 | } |
136 | 141 | checkNotExistObject(container, object, t) |
137 | objs, err := con.ObjectsAll(segmentContainer, nil) | |
142 | objs, err := con.ObjectsAll(ctx, segmentContainer, nil) | |
138 | 143 | if err != nil { |
139 | 144 | t.Errorf("Fail at check delte dynamic Large Object: %s", err.Error()) |
140 | 145 | } |
143 | 148 | } |
144 | 149 | } |
145 | 150 | func createStaticObject(container, object string, t *testing.T) { |
151 | ctx := context.Background() | |
146 | 152 | metadata := map[string]string{} |
147 | 153 | metadata["Custom-Field"] = "SomeValue" |
148 | 154 | ops := LargeObjectOpts{ |
154 | 160 | SegmentContainer: segmentContainer, // Name of the container to place segments |
155 | 161 | SegmentPrefix: "sg", // Prefix to use for the segments |
156 | 162 | } |
157 | bigfile, err := con.StaticLargeObjectCreate(&ops) | |
163 | bigfile, err := con.StaticLargeObjectCreate(ctx, &ops) | |
158 | 164 | if err != nil { |
159 | 165 | t.Errorf("Fail at static create Large Object") |
160 | 166 | } |
161 | bigfile.Write(filecontent) | |
162 | bigfile.Close() | |
167 | bigfile.WriteWithContext(ctx, filecontent) | |
168 | bigfile.CloseWithContext(ctx) | |
163 | 169 | checkObject(container, object, t) |
164 | 170 | } |
165 | 171 | func moveStaticObject(sc, so, dc, do string, t *testing.T) { |
166 | err = con.StaticLargeObjectMove(sc, so, dc, do) | |
172 | err = con.StaticLargeObjectMove(context.Background(), sc, so, dc, do) | |
167 | 173 | if err != nil { |
168 | 174 | t.Errorf("Fail at static move Large Object: %s", err.Error()) |
169 | 175 | } |
171 | 177 | checkObject(dc, do, t) |
172 | 178 | } |
173 | 179 | func deleteStaticObject(container, object string, t *testing.T) { |
174 | err = con.StaticLargeObjectDelete(container, object) | |
180 | ctx := context.Background() | |
181 | err = con.StaticLargeObjectDelete(ctx, container, object) | |
175 | 182 | if err != nil { |
176 | 183 | t.Errorf("Fail at delte dynamic Large Object: %s", err.Error()) |
177 | 184 | } |
178 | 185 | checkNotExistObject(container, object, t) |
179 | objs, err := con.ObjectsAll(segmentContainer, nil) | |
186 | objs, err := con.ObjectsAll(ctx, segmentContainer, nil) | |
180 | 187 | if err != nil { |
181 | 188 | t.Errorf("Fail at check delte dynamic Large Object: %s", err.Error()) |
182 | 189 | } |
3 | 3 | package swift_test |
4 | 4 | |
5 | 5 | import ( |
6 | "context" | |
6 | 7 | "fmt" |
7 | 8 | |
8 | "github.com/ncw/swift" | |
9 | "github.com/ncw/swift/v2" | |
9 | 10 | ) |
10 | 11 | |
11 | 12 | func ExampleConnection() { |
13 | ctx := context.Background() | |
12 | 14 | // Create a v1 auth connection |
13 | 15 | c := &swift.Connection{ |
14 | 16 | // This should be your username |
23 | 25 | } |
24 | 26 | |
25 | 27 | // Authenticate |
26 | err := c.Authenticate() | |
28 | err := c.Authenticate(ctx) | |
27 | 29 | if err != nil { |
28 | 30 | panic(err) |
29 | 31 | } |
30 | 32 | // List all the containers |
31 | containers, err := c.ContainerNames(nil) | |
33 | containers, err := c.ContainerNames(ctx, nil) | |
32 | 34 | fmt.Println(containers) |
33 | 35 | // etc... |
34 | 36 | |
60 | 62 | defer rollback() |
61 | 63 | |
62 | 64 | objects := make([]string, 0) |
63 | err := c.ObjectsWalk(container, nil, func(opts *swift.ObjectsOpts) (interface{}, error) { | |
64 | newObjects, err := c.ObjectNames(container, opts) | |
65 | err := c.ObjectsWalk(context.Background(), container, nil, func(ctx context.Context, opts *swift.ObjectsOpts) (interface{}, error) { | |
66 | newObjects, err := c.ObjectNames(ctx, container, opts) | |
65 | 67 | if err == nil { |
66 | 68 | objects = append(objects, newObjects...) |
67 | 69 | } |
75 | 77 | defer rollback() |
76 | 78 | |
77 | 79 | // Use the helper method to create the current and versions container. |
78 | if err := c.VersionContainerCreate("cds", "cd-versions"); err != nil { | |
80 | if err := c.VersionContainerCreate(context.Background(), "cds", "cd-versions"); err != nil { | |
79 | 81 | fmt.Print(err.Error()) |
80 | 82 | } |
81 | 83 | } |
82 | 84 | |
83 | 85 | func ExampleConnection_VersionEnable() { |
86 | ctx := context.Background() | |
84 | 87 | c, rollback := makeConnection(nil) |
85 | 88 | defer rollback() |
86 | 89 | |
87 | 90 | // Build the containers manually and enable them. |
88 | if err := c.ContainerCreate("movie-versions", nil); err != nil { | |
91 | if err := c.ContainerCreate(ctx, "movie-versions", nil); err != nil { | |
89 | 92 | fmt.Print(err.Error()) |
90 | 93 | } |
91 | if err := c.ContainerCreate("movies", nil); err != nil { | |
94 | if err := c.ContainerCreate(ctx, "movies", nil); err != nil { | |
92 | 95 | fmt.Print(err.Error()) |
93 | 96 | } |
94 | if err := c.VersionEnable("movies", "movie-versions"); err != nil { | |
97 | if err := c.VersionEnable(ctx, "movies", "movie-versions"); err != nil { | |
95 | 98 | fmt.Print(err.Error()) |
96 | 99 | } |
97 | 100 | |
104 | 107 | defer rollback() |
105 | 108 | |
106 | 109 | // Disable versioning on a container. Note that this does not delete the versioning container. |
107 | c.VersionDisable("movies") | |
110 | c.VersionDisable(context.Background(), "movies") | |
108 | 111 | } |
2 | 2 | import ( |
3 | 3 | "bufio" |
4 | 4 | "bytes" |
5 | "context" | |
5 | 6 | "crypto/rand" |
6 | 7 | "crypto/sha1" |
7 | 8 | "encoding/hex" |
78 | 79 | return headers.IsLargeObjectSLO() || headers.IsLargeObjectDLO() |
79 | 80 | } |
80 | 81 | |
81 | func (c *Connection) getAllSegments(container string, path string, headers Headers) (string, []Object, error) { | |
82 | func (c *Connection) getAllSegments(ctx context.Context, container string, path string, headers Headers) (string, []Object, error) { | |
82 | 83 | if manifest, isDLO := headers["X-Object-Manifest"]; isDLO { |
83 | 84 | segmentContainer, segmentPath := parseFullPath(manifest) |
84 | segments, err := c.getAllDLOSegments(segmentContainer, segmentPath) | |
85 | segments, err := c.getAllDLOSegments(ctx, segmentContainer, segmentPath) | |
85 | 86 | return segmentContainer, segments, err |
86 | 87 | } |
87 | 88 | if headers.IsLargeObjectSLO() { |
88 | return c.getAllSLOSegments(container, path) | |
89 | return c.getAllSLOSegments(ctx, container, path) | |
89 | 90 | } |
90 | 91 | return "", nil, NotLargeObject |
91 | 92 | } |
107 | 108 | } |
108 | 109 | |
109 | 110 | type LargeObjectFile interface { |
111 | io.Seeker | |
110 | 112 | io.Writer |
111 | io.Seeker | |
112 | 113 | io.Closer |
114 | ||
115 | WriteWithContext(ctx context.Context, p []byte) (n int, err error) | |
116 | CloseWithContext(ctx context.Context) error | |
113 | 117 | Size() int64 |
114 | Flush() error | |
118 | Flush(ctx context.Context) error | |
115 | 119 | } |
116 | 120 | |
117 | 121 | // largeObjectCreate creates a large object at opts.Container, opts.ObjectName. |
119 | 123 | // opts.Flags can have the following bits set |
120 | 124 | // os.TRUNC - remove the contents of the large object if it exists |
121 | 125 | // os.APPEND - write at the end of the large object |
122 | func (c *Connection) largeObjectCreate(opts *LargeObjectOpts) (*largeObjectCreateFile, error) { | |
126 | func (c *Connection) largeObjectCreate(ctx context.Context, opts *LargeObjectOpts) (*largeObjectCreateFile, error) { | |
123 | 127 | var ( |
124 | 128 | segmentPath string |
125 | 129 | segmentContainer string |
134 | 138 | return nil, err |
135 | 139 | } |
136 | 140 | |
137 | if info, headers, err := c.Object(opts.Container, opts.ObjectName); err == nil { | |
141 | if info, headers, err := c.Object(ctx, opts.Container, opts.ObjectName); err == nil { | |
138 | 142 | if opts.Flags&os.O_TRUNC != 0 { |
139 | c.LargeObjectDelete(opts.Container, opts.ObjectName) | |
143 | c.LargeObjectDelete(ctx, opts.Container, opts.ObjectName) | |
140 | 144 | } else { |
141 | 145 | currentLength = info.Bytes |
142 | 146 | if headers.IsLargeObject() { |
143 | segmentContainer, segments, err = c.getAllSegments(opts.Container, opts.ObjectName, headers) | |
147 | segmentContainer, segments, err = c.getAllSegments(ctx, opts.Container, opts.ObjectName, headers) | |
144 | 148 | if err != nil { |
145 | 149 | return nil, err |
146 | 150 | } |
148 | 152 | segmentPath = gopath.Dir(segments[0].Name) |
149 | 153 | } |
150 | 154 | } else { |
151 | if err = c.ObjectMove(opts.Container, opts.ObjectName, opts.Container, getSegment(segmentPath, 1)); err != nil { | |
155 | if err = c.ObjectMove(ctx, opts.Container, opts.ObjectName, opts.Container, getSegment(segmentPath, 1)); err != nil { | |
152 | 156 | return nil, err |
153 | 157 | } |
154 | 158 | segments = append(segments, info) |
197 | 201 | } |
198 | 202 | |
199 | 203 | // LargeObjectDelete deletes the large object named by container, path |
200 | func (c *Connection) LargeObjectDelete(container string, objectName string) error { | |
201 | _, headers, err := c.Object(container, objectName) | |
204 | func (c *Connection) LargeObjectDelete(ctx context.Context, container string, objectName string) error { | |
205 | _, headers, err := c.Object(ctx, container, objectName) | |
202 | 206 | if err != nil { |
203 | 207 | return err |
204 | 208 | } |
205 | 209 | |
206 | 210 | var objects [][]string |
207 | 211 | if headers.IsLargeObject() { |
208 | segmentContainer, segments, err := c.getAllSegments(container, objectName, headers) | |
212 | segmentContainer, segments, err := c.getAllSegments(ctx, container, objectName, headers) | |
209 | 213 | if err != nil { |
210 | 214 | return err |
211 | 215 | } |
215 | 219 | } |
216 | 220 | objects = append(objects, []string{container, objectName}) |
217 | 221 | |
218 | info, err := c.cachedQueryInfo() | |
222 | info, err := c.cachedQueryInfo(ctx) | |
219 | 223 | if err == nil && info.SupportsBulkDelete() && len(objects) > 0 { |
220 | 224 | filenames := make([]string, len(objects)) |
221 | 225 | for i, obj := range objects { |
222 | 226 | filenames[i] = obj[0] + "/" + obj[1] |
223 | 227 | } |
224 | _, err = c.doBulkDelete(filenames, nil) | |
228 | _, err = c.doBulkDelete(ctx, filenames, nil) | |
225 | 229 | // Don't fail on ObjectNotFound because eventual consistency |
226 | 230 | // makes this situation normal. |
227 | 231 | if err != nil && err != Forbidden && err != ObjectNotFound { |
229 | 233 | } |
230 | 234 | } else { |
231 | 235 | for _, obj := range objects { |
232 | if err := c.ObjectDelete(obj[0], obj[1]); err != nil { | |
236 | if err := c.ObjectDelete(ctx, obj[0], obj[1]); err != nil { | |
233 | 237 | return err |
234 | 238 | } |
235 | 239 | } |
243 | 247 | // that have the prefix as indicated by the manifest. |
244 | 248 | // If the object is a Static Large Object (SLO), it retrieves the JSON content |
245 | 249 | // of the manifest and return all the segments of it. |
246 | func (c *Connection) LargeObjectGetSegments(container string, path string) (string, []Object, error) { | |
247 | _, headers, err := c.Object(container, path) | |
250 | func (c *Connection) LargeObjectGetSegments(ctx context.Context, container string, path string) (string, []Object, error) { | |
251 | _, headers, err := c.Object(ctx, container, path) | |
248 | 252 | if err != nil { |
249 | 253 | return "", nil, err |
250 | 254 | } |
251 | 255 | |
252 | return c.getAllSegments(container, path, headers) | |
256 | return c.getAllSegments(ctx, container, path, headers) | |
253 | 257 | } |
254 | 258 | |
255 | 259 | // Seek sets the offset for the next write operation |
300 | 304 | } |
301 | 305 | } |
302 | 306 | |
303 | func (c *Connection) waitForSegmentsToShowUp(container, objectName string, expectedSize int64) (err error) { | |
307 | func (c *Connection) waitForSegmentsToShowUp(ctx context.Context, container, objectName string, expectedSize int64) (err error) { | |
304 | 308 | err = withLORetry(expectedSize, func() (Headers, int64, error) { |
305 | 309 | var info Object |
306 | 310 | var headers Headers |
307 | info, headers, err = c.objectBase(container, objectName) | |
311 | info, headers, err = c.objectBase(ctx, container, objectName) | |
308 | 312 | if err != nil { |
309 | 313 | return headers, 0, err |
310 | 314 | } |
313 | 317 | return |
314 | 318 | } |
315 | 319 | |
316 | // Write satisfies the io.Writer interface | |
317 | 320 | func (file *largeObjectCreateFile) Write(buf []byte) (int, error) { |
321 | return file.WriteWithContext(context.Background(), buf) | |
322 | } | |
323 | ||
324 | func (file *largeObjectCreateFile) WriteWithContext(ctx context.Context, buf []byte) (int, error) { | |
318 | 325 | var sz int64 |
319 | 326 | var relativeFilePos int |
320 | 327 | writeSegmentIdx := 0 |
328 | 335 | } |
329 | 336 | sizeToWrite := len(buf) |
330 | 337 | for offset := 0; offset < sizeToWrite; { |
331 | newSegment, n, err := file.writeSegment(buf[offset:], writeSegmentIdx, relativeFilePos) | |
338 | newSegment, n, err := file.writeSegment(ctx, buf[offset:], writeSegmentIdx, relativeFilePos) | |
332 | 339 | if err != nil { |
333 | 340 | return 0, err |
334 | 341 | } |
349 | 356 | return sizeToWrite, nil |
350 | 357 | } |
351 | 358 | |
352 | func (file *largeObjectCreateFile) writeSegment(buf []byte, writeSegmentIdx int, relativeFilePos int) (*Object, int, error) { | |
359 | func (file *largeObjectCreateFile) writeSegment(ctx context.Context, buf []byte, writeSegmentIdx int, relativeFilePos int) (*Object, int, error) { | |
353 | 360 | var ( |
354 | 361 | readers []io.Reader |
355 | 362 | existingSegment *Object |
365 | 372 | if relativeFilePos > 0 { |
366 | 373 | headers := make(Headers) |
367 | 374 | headers["Range"] = "bytes=0-" + strconv.FormatInt(int64(relativeFilePos-1), 10) |
368 | existingSegmentReader, _, err := file.conn.ObjectOpen(file.segmentContainer, segmentName, true, headers) | |
375 | existingSegmentReader, _, err := file.conn.ObjectOpen(ctx, file.segmentContainer, segmentName, true, headers) | |
369 | 376 | if err != nil { |
370 | 377 | return nil, 0, err |
371 | 378 | } |
383 | 390 | if existingSegment != nil && segmentSize < int(existingSegment.Bytes) { |
384 | 391 | headers := make(Headers) |
385 | 392 | headers["Range"] = "bytes=" + strconv.FormatInt(int64(segmentSize), 10) + "-" |
386 | tailSegmentReader, _, err := file.conn.ObjectOpen(file.segmentContainer, segmentName, true, headers) | |
393 | tailSegmentReader, _, err := file.conn.ObjectOpen(ctx, file.segmentContainer, segmentName, true, headers) | |
387 | 394 | if err != nil { |
388 | 395 | return nil, 0, err |
389 | 396 | } |
392 | 399 | readers = append(readers, tailSegmentReader) |
393 | 400 | } |
394 | 401 | segmentReader := io.MultiReader(readers...) |
395 | headers, err := file.conn.ObjectPut(file.segmentContainer, segmentName, segmentReader, true, "", file.contentType, nil) | |
402 | headers, err := file.conn.ObjectPut(ctx, file.segmentContainer, segmentName, segmentReader, true, "", file.contentType, nil) | |
396 | 403 | if err != nil { |
397 | 404 | return nil, 0, err |
398 | 405 | } |
415 | 422 | } |
416 | 423 | |
417 | 424 | func (blo *bufferedLargeObjectFile) Close() error { |
425 | return blo.CloseWithContext(context.Background()) | |
426 | } | |
427 | ||
428 | func (blo *bufferedLargeObjectFile) CloseWithContext(ctx context.Context) error { | |
418 | 429 | err := blo.bw.Flush() |
419 | 430 | if err != nil { |
420 | 431 | return err |
421 | 432 | } |
422 | return blo.LargeObjectFile.Close() | |
433 | return blo.LargeObjectFile.CloseWithContext(ctx) | |
434 | } | |
435 | ||
436 | func (blo *bufferedLargeObjectFile) WriteWithContext(_ context.Context, p []byte) (n int, err error) { | |
437 | return blo.Write(p) | |
423 | 438 | } |
424 | 439 | |
425 | 440 | func (blo *bufferedLargeObjectFile) Write(p []byte) (n int, err error) { |
438 | 453 | return blo.LargeObjectFile.Size() + int64(blo.bw.Buffered()) |
439 | 454 | } |
440 | 455 | |
441 | func (blo *bufferedLargeObjectFile) Flush() error { | |
456 | func (blo *bufferedLargeObjectFile) Flush(ctx context.Context) error { | |
442 | 457 | err := blo.bw.Flush() |
443 | 458 | if err != nil { |
444 | 459 | return err |
445 | 460 | } |
446 | return blo.LargeObjectFile.Flush() | |
447 | } | |
461 | return blo.LargeObjectFile.Flush(ctx) | |
462 | } |
0 | 0 | package rs |
1 | 1 | |
2 | 2 | import ( |
3 | "context" | |
3 | 4 | "errors" |
4 | 5 | "net/http" |
5 | 6 | "strconv" |
6 | 7 | |
7 | "github.com/ncw/swift" | |
8 | "github.com/ncw/swift/v2" | |
8 | 9 | ) |
9 | 10 | |
10 | 11 | // RsConnection is a RackSpace specific wrapper to the core swift library which |
15 | 16 | } |
16 | 17 | |
17 | 18 | // manage is similar to the swift storage method, but uses the CDN Management URL for CDN specific calls. |
18 | func (c *RsConnection) manage(p swift.RequestOpts) (resp *http.Response, headers swift.Headers, err error) { | |
19 | func (c *RsConnection) manage(ctx context.Context, p swift.RequestOpts) (resp *http.Response, headers swift.Headers, err error) { | |
19 | 20 | p.OnReAuth = func() (string, error) { |
20 | 21 | if c.cdnUrl == "" { |
21 | 22 | c.cdnUrl = c.Auth.CdnUrl() |
31 | 32 | return nil, nil, err |
32 | 33 | } |
33 | 34 | } |
34 | return c.Connection.Call(c.cdnUrl, p) | |
35 | return c.Connection.Call(ctx, c.cdnUrl, p) | |
35 | 36 | } |
36 | 37 | |
37 | 38 | // ContainerCDNEnable enables a container for public CDN usage. |
39 | 40 | // Change the default TTL of 259200 seconds (72 hours) by passing in an integer value. |
40 | 41 | // |
41 | 42 | // This method can be called again to change the TTL. |
42 | func (c *RsConnection) ContainerCDNEnable(container string, ttl int) (swift.Headers, error) { | |
43 | func (c *RsConnection) ContainerCDNEnable(ctx context.Context, container string, ttl int) (swift.Headers, error) { | |
43 | 44 | h := swift.Headers{"X-CDN-Enabled": "true"} |
44 | 45 | if ttl > 0 { |
45 | 46 | h["X-TTL"] = strconv.Itoa(ttl) |
46 | 47 | } |
47 | 48 | |
48 | _, headers, err := c.manage(swift.RequestOpts{ | |
49 | _, headers, err := c.manage(ctx, swift.RequestOpts{ | |
49 | 50 | Container: container, |
50 | 51 | Operation: "PUT", |
51 | 52 | ErrorMap: swift.ContainerErrorMap, |
56 | 57 | } |
57 | 58 | |
58 | 59 | // ContainerCDNDisable disables CDN access to a container. |
59 | func (c *RsConnection) ContainerCDNDisable(container string) error { | |
60 | func (c *RsConnection) ContainerCDNDisable(ctx context.Context, container string) error { | |
60 | 61 | h := swift.Headers{"X-CDN-Enabled": "false"} |
61 | 62 | |
62 | _, _, err := c.manage(swift.RequestOpts{ | |
63 | _, _, err := c.manage(ctx, swift.RequestOpts{ | |
63 | 64 | Container: container, |
64 | 65 | Operation: "PUT", |
65 | 66 | ErrorMap: swift.ContainerErrorMap, |
70 | 71 | } |
71 | 72 | |
72 | 73 | // ContainerCDNMeta returns the CDN metadata for a container. |
73 | func (c *RsConnection) ContainerCDNMeta(container string) (swift.Headers, error) { | |
74 | _, headers, err := c.manage(swift.RequestOpts{ | |
74 | func (c *RsConnection) ContainerCDNMeta(ctx context.Context, container string) (swift.Headers, error) { | |
75 | _, headers, err := c.manage(ctx, swift.RequestOpts{ | |
75 | 76 | Container: container, |
76 | 77 | Operation: "HEAD", |
77 | 78 | ErrorMap: swift.ContainerErrorMap, |
1 | 1 | package rs_test |
2 | 2 | |
3 | 3 | import ( |
4 | "context" | |
4 | 5 | "os" |
5 | 6 | "testing" |
6 | 7 | |
7 | "github.com/ncw/swift/rs" | |
8 | "github.com/ncw/swift/v2/rs" | |
8 | 9 | ) |
9 | 10 | |
10 | 11 | var ( |
31 | 32 | c.UserName = UserName |
32 | 33 | c.ApiKey = ApiKey |
33 | 34 | c.AuthUrl = AuthUrl |
34 | err := c.Authenticate() | |
35 | err := c.Authenticate(context.Background()) | |
35 | 36 | if err != nil { |
36 | 37 | t.Fatal("Auth failed", err) |
37 | 38 | } |
42 | 43 | |
43 | 44 | // Setup |
44 | 45 | func TestContainerCreate(t *testing.T) { |
45 | err := c.ContainerCreate(CONTAINER, nil) | |
46 | err := c.ContainerCreate(context.Background(), CONTAINER, nil) | |
46 | 47 | if err != nil { |
47 | 48 | t.Fatal(err) |
48 | 49 | } |
49 | 50 | } |
50 | 51 | |
51 | 52 | func TestCDNEnable(t *testing.T) { |
52 | headers, err := c.ContainerCDNEnable(CONTAINER, 0) | |
53 | headers, err := c.ContainerCDNEnable(context.Background(), CONTAINER, 0) | |
53 | 54 | if err != nil { |
54 | 55 | t.Error(err) |
55 | 56 | } |
63 | 64 | c2.UserName = c.UserName |
64 | 65 | c2.ApiKey = c.ApiKey |
65 | 66 | c2.AuthUrl = c.AuthUrl |
66 | _, err := c2.ContainerCDNEnable(CONTAINER, 0) | |
67 | _, err := c2.ContainerCDNEnable(context.Background(), CONTAINER, 0) | |
67 | 68 | if err != nil { |
68 | 69 | t.Fatalf("Failed to reauthenticate: %v", err) |
69 | 70 | } |
70 | 71 | } |
71 | 72 | |
72 | 73 | func TestCDNMeta(t *testing.T) { |
73 | headers, err := c.ContainerCDNMeta(CONTAINER) | |
74 | headers, err := c.ContainerCDNMeta(context.Background(), CONTAINER) | |
74 | 75 | if err != nil { |
75 | 76 | t.Error(err) |
76 | 77 | } |
80 | 81 | } |
81 | 82 | |
82 | 83 | func TestCDNDisable(t *testing.T) { |
83 | err := c.ContainerCDNDisable(CONTAINER) // files stick in CDN until TTL expires | |
84 | err := c.ContainerCDNDisable(context.Background(), CONTAINER) // files stick in CDN until TTL expires | |
84 | 85 | if err != nil { |
85 | 86 | t.Error(err) |
86 | 87 | } |
88 | 89 | |
89 | 90 | // Teardown |
90 | 91 | func TestContainerDelete(t *testing.T) { |
91 | err := c.ContainerDelete(CONTAINER) | |
92 | err := c.ContainerDelete(context.Background(), CONTAINER) | |
92 | 93 | if err != nil { |
93 | 94 | t.Fatal(err) |
94 | 95 | } |
1 | 1 | |
2 | 2 | import ( |
3 | 3 | "bytes" |
4 | "context" | |
4 | 5 | "encoding/json" |
5 | 6 | "errors" |
6 | 7 | "fmt" |
36 | 37 | // an object which satisfies io.Writer, io.Seeker, io.Closer and |
37 | 38 | // io.ReaderFrom. The flags are as passed to the largeObjectCreate |
38 | 39 | // method. |
39 | func (c *Connection) StaticLargeObjectCreateFile(opts *LargeObjectOpts) (LargeObjectFile, error) { | |
40 | info, err := c.cachedQueryInfo() | |
40 | func (c *Connection) StaticLargeObjectCreateFile(ctx context.Context, opts *LargeObjectOpts) (LargeObjectFile, error) { | |
41 | info, err := c.cachedQueryInfo(ctx) | |
41 | 42 | if err != nil || !info.SupportsSLO() { |
42 | 43 | return nil, SLONotSupported |
43 | 44 | } |
45 | 46 | if realMinChunkSize > opts.MinChunkSize { |
46 | 47 | opts.MinChunkSize = realMinChunkSize |
47 | 48 | } |
48 | lo, err := c.largeObjectCreate(opts) | |
49 | lo, err := c.largeObjectCreate(ctx, opts) | |
49 | 50 | if err != nil { |
50 | 51 | return nil, err |
51 | 52 | } |
57 | 58 | // StaticLargeObjectCreate creates or truncates an existing static |
58 | 59 | // large object returning a writeable object. This sets opts.Flags to |
59 | 60 | // an appropriate value before calling StaticLargeObjectCreateFile |
60 | func (c *Connection) StaticLargeObjectCreate(opts *LargeObjectOpts) (LargeObjectFile, error) { | |
61 | func (c *Connection) StaticLargeObjectCreate(ctx context.Context, opts *LargeObjectOpts) (LargeObjectFile, error) { | |
61 | 62 | opts.Flags = os.O_TRUNC | os.O_CREATE |
62 | return c.StaticLargeObjectCreateFile(opts) | |
63 | return c.StaticLargeObjectCreateFile(ctx, opts) | |
63 | 64 | } |
64 | 65 | |
65 | 66 | // StaticLargeObjectDelete deletes a static large object and all of its segments. |
66 | func (c *Connection) StaticLargeObjectDelete(container string, path string) error { | |
67 | info, err := c.cachedQueryInfo() | |
67 | func (c *Connection) StaticLargeObjectDelete(ctx context.Context, container string, path string) error { | |
68 | info, err := c.cachedQueryInfo(ctx) | |
68 | 69 | if err != nil || !info.SupportsSLO() { |
69 | 70 | return SLONotSupported |
70 | 71 | } |
71 | return c.LargeObjectDelete(container, path) | |
72 | return c.LargeObjectDelete(ctx, container, path) | |
72 | 73 | } |
73 | 74 | |
74 | 75 | // StaticLargeObjectMove moves a static large object from srcContainer, srcObjectName to dstContainer, dstObjectName |
75 | func (c *Connection) StaticLargeObjectMove(srcContainer string, srcObjectName string, dstContainer string, dstObjectName string) error { | |
76 | swiftInfo, err := c.cachedQueryInfo() | |
76 | func (c *Connection) StaticLargeObjectMove(ctx context.Context, srcContainer string, srcObjectName string, dstContainer string, dstObjectName string) error { | |
77 | swiftInfo, err := c.cachedQueryInfo(ctx) | |
77 | 78 | if err != nil || !swiftInfo.SupportsSLO() { |
78 | 79 | return SLONotSupported |
79 | 80 | } |
80 | info, headers, err := c.Object(srcContainer, srcObjectName) | |
81 | info, headers, err := c.Object(ctx, srcContainer, srcObjectName) | |
81 | 82 | if err != nil { |
82 | 83 | return err |
83 | 84 | } |
84 | 85 | |
85 | container, segments, err := c.getAllSegments(srcContainer, srcObjectName, headers) | |
86 | container, segments, err := c.getAllSegments(ctx, srcContainer, srcObjectName, headers) | |
86 | 87 | if err != nil { |
87 | 88 | return err |
88 | 89 | } |
90 | 91 | //copy only metadata during move (other headers might not be safe for copying) |
91 | 92 | headers = headers.ObjectMetadata().ObjectHeaders() |
92 | 93 | |
93 | if err := c.createSLOManifest(dstContainer, dstObjectName, info.ContentType, container, segments, headers); err != nil { | |
94 | if err := c.createSLOManifest(ctx, dstContainer, dstObjectName, info.ContentType, container, segments, headers); err != nil { | |
94 | 95 | return err |
95 | 96 | } |
96 | 97 | |
97 | if err := c.ObjectDelete(srcContainer, srcObjectName); err != nil { | |
98 | if err := c.ObjectDelete(ctx, srcContainer, srcObjectName); err != nil { | |
98 | 99 | return err |
99 | 100 | } |
100 | 101 | |
102 | 103 | } |
103 | 104 | |
104 | 105 | // createSLOManifest creates a static large object manifest |
105 | func (c *Connection) createSLOManifest(container string, path string, contentType string, segmentContainer string, segments []Object, h Headers) error { | |
106 | func (c *Connection) createSLOManifest(ctx context.Context, container string, path string, contentType string, segmentContainer string, segments []Object, h Headers) error { | |
106 | 107 | sloSegments := make([]swiftSegment, len(segments)) |
107 | 108 | for i, segment := range segments { |
108 | 109 | sloSegments[i].Path = fmt.Sprintf("%s/%s", segmentContainer, segment.Name) |
117 | 118 | |
118 | 119 | values := url.Values{} |
119 | 120 | values.Set("multipart-manifest", "put") |
120 | if _, err := c.objectPut(container, path, bytes.NewBuffer(content), false, "", contentType, h, values); err != nil { | |
121 | if _, err := c.objectPut(ctx, container, path, bytes.NewBuffer(content), false, "", contentType, h, values); err != nil { | |
121 | 122 | return err |
122 | 123 | } |
123 | 124 | |
125 | 126 | } |
126 | 127 | |
127 | 128 | func (file *StaticLargeObjectCreateFile) Close() error { |
128 | return file.Flush() | |
129 | return file.CloseWithContext(context.Background()) | |
129 | 130 | } |
130 | 131 | |
131 | func (file *StaticLargeObjectCreateFile) Flush() error { | |
132 | if err := file.conn.createSLOManifest(file.container, file.objectName, file.contentType, file.segmentContainer, file.segments, file.headers); err != nil { | |
132 | func (file *StaticLargeObjectCreateFile) CloseWithContext(ctx context.Context) error { | |
133 | return file.Flush(ctx) | |
134 | } | |
135 | ||
136 | func (file *StaticLargeObjectCreateFile) Flush(ctx context.Context) error { | |
137 | if err := file.conn.createSLOManifest(ctx, file.container, file.objectName, file.contentType, file.segmentContainer, file.segments, file.headers); err != nil { | |
133 | 138 | return err |
134 | 139 | } |
135 | return file.conn.waitForSegmentsToShowUp(file.container, file.objectName, file.Size()) | |
140 | return file.conn.waitForSegmentsToShowUp(ctx, file.container, file.objectName, file.Size()) | |
136 | 141 | } |
137 | 142 | |
138 | func (c *Connection) getAllSLOSegments(container, path string) (string, []Object, error) { | |
143 | func (c *Connection) getAllSLOSegments(ctx context.Context, container, path string) (string, []Object, error) { | |
139 | 144 | var ( |
140 | 145 | segmentList []swiftSegment |
141 | 146 | segments []Object |
146 | 151 | values := url.Values{} |
147 | 152 | values.Set("multipart-manifest", "get") |
148 | 153 | |
149 | file, _, err := c.objectOpen(container, path, true, nil, values) | |
154 | file, _, err := c.objectOpen(ctx, container, path, true, nil, values) | |
150 | 155 | if err != nil { |
151 | 156 | return "", nil, err |
152 | 157 | } |
2 | 2 | import ( |
3 | 3 | "bufio" |
4 | 4 | "bytes" |
5 | "context" | |
5 | 6 | "crypto/hmac" |
6 | 7 | "crypto/md5" |
7 | 8 | "crypto/sha1" |
71 | 72 | // import ( |
72 | 73 | // "appengine/urlfetch" |
73 | 74 | // "fmt" |
74 | // "github.com/ncw/swift" | |
75 | // "github.com/ncw/swift/v2" | |
75 | 76 | // ) |
76 | 77 | // |
77 | 78 | // func handler(w http.ResponseWriter, r *http.Request) { |
456 | 457 | // |
457 | 458 | // If you don't call it before calling one of the connection methods |
458 | 459 | // then it will be called for you on the first access. |
459 | func (c *Connection) Authenticate() (err error) { | |
460 | func (c *Connection) Authenticate(ctx context.Context) (err error) { | |
460 | 461 | if c.authLock == nil { |
461 | 462 | c.authLock = &sync.Mutex{} |
462 | 463 | } |
463 | 464 | c.authLock.Lock() |
464 | 465 | defer c.authLock.Unlock() |
465 | return c.authenticate() | |
466 | return c.authenticate(ctx) | |
466 | 467 | } |
467 | 468 | |
468 | 469 | // Internal implementation of Authenticate |
469 | 470 | // |
470 | 471 | // Call with authLock held |
471 | func (c *Connection) authenticate() (err error) { | |
472 | func (c *Connection) authenticate(ctx context.Context) (err error) { | |
472 | 473 | c.setDefaults() |
473 | 474 | |
474 | 475 | // Flush the keepalives connection - if we are |
485 | 486 | retries := 1 |
486 | 487 | again: |
487 | 488 | var req *http.Request |
488 | req, err = c.Auth.Request(c) | |
489 | req, err = c.Auth.Request(ctx, c) | |
489 | 490 | if err != nil { |
490 | 491 | return |
491 | 492 | } |
513 | 514 | } |
514 | 515 | return |
515 | 516 | } |
516 | err = c.Auth.Response(resp) | |
517 | err = c.Auth.Response(ctx, resp) | |
517 | 518 | if err != nil { |
518 | 519 | return |
519 | 520 | } |
540 | 541 | // Get an authToken and url |
541 | 542 | // |
542 | 543 | // The Url may be updated if it needed to authenticate using the OnReAuth function |
543 | func (c *Connection) getUrlAndAuthToken(targetUrlIn string, OnReAuth func() (string, error)) (targetUrlOut, authToken string, err error) { | |
544 | func (c *Connection) getUrlAndAuthToken(ctx context.Context, targetUrlIn string, OnReAuth func() (string, error)) (targetUrlOut, authToken string, err error) { | |
544 | 545 | c.authLock.Lock() |
545 | 546 | defer c.authLock.Unlock() |
546 | 547 | targetUrlOut = targetUrlIn |
547 | 548 | if !c.authenticated() { |
548 | err = c.authenticate() | |
549 | err = c.authenticate(ctx) | |
549 | 550 | if err != nil { |
550 | 551 | return |
551 | 552 | } |
628 | 629 | } |
629 | 630 | |
630 | 631 | // Discover Swift configuration by doing a request against /info |
631 | func (c *Connection) QueryInfo() (infos SwiftInfo, err error) { | |
632 | func (c *Connection) QueryInfo(ctx context.Context) (infos SwiftInfo, err error) { | |
632 | 633 | infoUrl, err := url.Parse(c.StorageUrl) |
633 | 634 | if err != nil { |
634 | 635 | return nil, err |
635 | 636 | } |
636 | 637 | infoUrl.Path = path.Join(infoUrl.Path, "..", "..", "info") |
637 | resp, err := c.client.Get(infoUrl.String()) | |
638 | req, err := http.NewRequestWithContext(ctx, http.MethodGet, infoUrl.String(), nil) | |
639 | if err != nil { | |
640 | return nil, err | |
641 | } | |
642 | resp, err := c.client.Do(req) | |
638 | 643 | if err == nil { |
639 | 644 | if resp.StatusCode != http.StatusOK { |
640 | 645 | drainAndClose(resp.Body, nil) |
651 | 656 | return nil, err |
652 | 657 | } |
653 | 658 | |
654 | func (c *Connection) cachedQueryInfo() (infos SwiftInfo, err error) { | |
659 | func (c *Connection) cachedQueryInfo(ctx context.Context) (infos SwiftInfo, err error) { | |
655 | 660 | c.authLock.Lock() |
656 | 661 | infos = c.swiftInfo |
657 | 662 | c.authLock.Unlock() |
658 | 663 | if infos == nil { |
659 | infos, err = c.QueryInfo() | |
664 | infos, err = c.QueryInfo(ctx) | |
660 | 665 | if err != nil { |
661 | 666 | return |
662 | 667 | } |
699 | 704 | // receives a 401 error which means the token has expired |
700 | 705 | // |
701 | 706 | // This method is exported so extensions can call it. |
702 | func (c *Connection) Call(targetUrl string, p RequestOpts) (resp *http.Response, headers Headers, err error) { | |
707 | func (c *Connection) Call(ctx context.Context, targetUrl string, p RequestOpts) (resp *http.Response, headers Headers, err error) { | |
703 | 708 | c.authLock.Lock() |
704 | 709 | c.setDefaults() |
705 | 710 | c.authLock.Unlock() |
710 | 715 | var req *http.Request |
711 | 716 | for { |
712 | 717 | var authToken string |
713 | if targetUrl, authToken, err = c.getUrlAndAuthToken(targetUrl, p.OnReAuth); err != nil { | |
718 | if targetUrl, authToken, err = c.getUrlAndAuthToken(ctx, targetUrl, p.OnReAuth); err != nil { | |
714 | 719 | return //authentication failure |
715 | 720 | } |
716 | 721 | var URL *url.URL |
733 | 738 | if reader != nil { |
734 | 739 | reader = newWatchdogReader(reader, c.Timeout, timer) |
735 | 740 | } |
736 | req, err = http.NewRequest(p.Operation, URL.String(), reader) | |
741 | req, err = http.NewRequestWithContext(ctx, p.Operation, URL.String(), reader) | |
737 | 742 | if err != nil { |
738 | 743 | return |
739 | 744 | } |
808 | 813 | // |
809 | 814 | // This will Authenticate if necessary, and re-authenticate if it |
810 | 815 | // receives a 401 error which means the token has expired |
811 | func (c *Connection) storage(p RequestOpts) (resp *http.Response, headers Headers, err error) { | |
816 | func (c *Connection) storage(ctx context.Context, p RequestOpts) (resp *http.Response, headers Headers, err error) { | |
812 | 817 | p.OnReAuth = func() (string, error) { |
813 | 818 | return c.StorageUrl, nil |
814 | 819 | } |
815 | 820 | c.authLock.Lock() |
816 | 821 | url := c.StorageUrl |
817 | 822 | c.authLock.Unlock() |
818 | return c.Call(url, p) | |
823 | return c.Call(ctx, url, p) | |
819 | 824 | } |
820 | 825 | |
821 | 826 | // readLines reads the response into an array of strings. |
886 | 891 | } |
887 | 892 | |
888 | 893 | // ContainerNames returns a slice of names of containers in this account. |
889 | func (c *Connection) ContainerNames(opts *ContainersOpts) ([]string, error) { | |
894 | func (c *Connection) ContainerNames(ctx context.Context, opts *ContainersOpts) ([]string, error) { | |
890 | 895 | v, h := opts.parse() |
891 | resp, _, err := c.storage(RequestOpts{ | |
896 | resp, _, err := c.storage(ctx, RequestOpts{ | |
892 | 897 | Operation: "GET", |
893 | 898 | Parameters: v, |
894 | 899 | ErrorMap: ContainerErrorMap, |
910 | 915 | |
911 | 916 | // Containers returns a slice of structures with full information as |
912 | 917 | // described in Container. |
913 | func (c *Connection) Containers(opts *ContainersOpts) ([]Container, error) { | |
918 | func (c *Connection) Containers(ctx context.Context, opts *ContainersOpts) ([]Container, error) { | |
914 | 919 | v, h := opts.parse() |
915 | 920 | v.Set("format", "json") |
916 | resp, _, err := c.storage(RequestOpts{ | |
921 | resp, _, err := c.storage(ctx, RequestOpts{ | |
917 | 922 | Operation: "GET", |
918 | 923 | Parameters: v, |
919 | 924 | ErrorMap: ContainerErrorMap, |
946 | 951 | // It calls Containers multiple times using the Marker parameter |
947 | 952 | // |
948 | 953 | // It has a default Limit parameter but you may pass in your own |
949 | func (c *Connection) ContainersAll(opts *ContainersOpts) ([]Container, error) { | |
954 | func (c *Connection) ContainersAll(ctx context.Context, opts *ContainersOpts) ([]Container, error) { | |
950 | 955 | opts = containersAllOpts(opts) |
951 | 956 | containers := make([]Container, 0) |
952 | 957 | for { |
953 | newContainers, err := c.Containers(opts) | |
958 | newContainers, err := c.Containers(ctx, opts) | |
954 | 959 | if err != nil { |
955 | 960 | return nil, err |
956 | 961 | } |
968 | 973 | // It calls ContainerNames multiple times using the Marker parameter |
969 | 974 | // |
970 | 975 | // It has a default Limit parameter but you may pass in your own |
971 | func (c *Connection) ContainerNamesAll(opts *ContainersOpts) ([]string, error) { | |
976 | func (c *Connection) ContainerNamesAll(ctx context.Context, opts *ContainersOpts) ([]string, error) { | |
972 | 977 | opts = containersAllOpts(opts) |
973 | 978 | containers := make([]string, 0) |
974 | 979 | for { |
975 | newContainers, err := c.ContainerNames(opts) | |
980 | newContainers, err := c.ContainerNames(ctx, opts) | |
976 | 981 | if err != nil { |
977 | 982 | return nil, err |
978 | 983 | } |
1028 | 1033 | } |
1029 | 1034 | |
1030 | 1035 | // ObjectNames returns a slice of names of objects in a given container. |
1031 | func (c *Connection) ObjectNames(container string, opts *ObjectsOpts) ([]string, error) { | |
1036 | func (c *Connection) ObjectNames(ctx context.Context, container string, opts *ObjectsOpts) ([]string, error) { | |
1032 | 1037 | v, h := opts.parse() |
1033 | resp, _, err := c.storage(RequestOpts{ | |
1038 | resp, _, err := c.storage(ctx, RequestOpts{ | |
1034 | 1039 | Container: container, |
1035 | 1040 | Operation: "GET", |
1036 | 1041 | Parameters: v, |
1064 | 1069 | // with ContentType 'application/directory'. These are not real |
1065 | 1070 | // objects but represent directories of objects which haven't had an |
1066 | 1071 | // object created for them. |
1067 | func (c *Connection) Objects(container string, opts *ObjectsOpts) ([]Object, error) { | |
1072 | func (c *Connection) Objects(ctx context.Context, container string, opts *ObjectsOpts) ([]Object, error) { | |
1068 | 1073 | v, h := opts.parse() |
1069 | 1074 | v.Set("format", "json") |
1070 | resp, _, err := c.storage(RequestOpts{ | |
1075 | resp, _, err := c.storage(ctx, RequestOpts{ | |
1071 | 1076 | Container: container, |
1072 | 1077 | Operation: "GET", |
1073 | 1078 | Parameters: v, |
1129 | 1134 | |
1130 | 1135 | // A closure defined by the caller to iterate through all objects |
1131 | 1136 | // |
1132 | // Call Objects or ObjectNames from here with the *ObjectOpts passed in | |
1137 | // Call Objects or ObjectNames from here with the context.Context and *ObjectOpts passed in | |
1133 | 1138 | // |
1134 | 1139 | // Do whatever is required with the results then return them |
1135 | type ObjectsWalkFn func(*ObjectsOpts) (interface{}, error) | |
1140 | type ObjectsWalkFn func(context.Context, *ObjectsOpts) (interface{}, error) | |
1136 | 1141 | |
1137 | 1142 | // ObjectsWalk is uses to iterate through all the objects in chunks as |
1138 | 1143 | // returned by Objects or ObjectNames using the Marker and Limit |
1144 | 1149 | // Errors will be returned from this function |
1145 | 1150 | // |
1146 | 1151 | // It has a default Limit parameter but you may pass in your own |
1147 | func (c *Connection) ObjectsWalk(container string, opts *ObjectsOpts, walkFn ObjectsWalkFn) error { | |
1152 | func (c *Connection) ObjectsWalk(ctx context.Context, container string, opts *ObjectsOpts, walkFn ObjectsWalkFn) error { | |
1148 | 1153 | opts = objectsAllOpts(opts, allObjectsChanLimit) |
1149 | 1154 | for { |
1150 | objects, err := walkFn(opts) | |
1155 | objects, err := walkFn(ctx, opts) | |
1151 | 1156 | if err != nil { |
1152 | 1157 | return err |
1153 | 1158 | } |
1178 | 1183 | // ObjectsAll is like Objects but it returns an unlimited number of Objects in a slice |
1179 | 1184 | // |
1180 | 1185 | // It calls Objects multiple times using the Marker parameter |
1181 | func (c *Connection) ObjectsAll(container string, opts *ObjectsOpts) ([]Object, error) { | |
1186 | func (c *Connection) ObjectsAll(ctx context.Context, container string, opts *ObjectsOpts) ([]Object, error) { | |
1182 | 1187 | objects := make([]Object, 0) |
1183 | err := c.ObjectsWalk(container, opts, func(opts *ObjectsOpts) (interface{}, error) { | |
1184 | newObjects, err := c.Objects(container, opts) | |
1188 | err := c.ObjectsWalk(ctx, container, opts, func(ctx context.Context, opts *ObjectsOpts) (interface{}, error) { | |
1189 | newObjects, err := c.Objects(ctx, container, opts) | |
1185 | 1190 | if err == nil { |
1186 | 1191 | objects = append(objects, newObjects...) |
1187 | 1192 | } |
1196 | 1201 | // reset unless KeepMarker is set |
1197 | 1202 | // |
1198 | 1203 | // It has a default Limit parameter but you may pass in your own |
1199 | func (c *Connection) ObjectNamesAll(container string, opts *ObjectsOpts) ([]string, error) { | |
1204 | func (c *Connection) ObjectNamesAll(ctx context.Context, container string, opts *ObjectsOpts) ([]string, error) { | |
1200 | 1205 | objects := make([]string, 0) |
1201 | err := c.ObjectsWalk(container, opts, func(opts *ObjectsOpts) (interface{}, error) { | |
1202 | newObjects, err := c.ObjectNames(container, opts) | |
1206 | err := c.ObjectsWalk(ctx, container, opts, func(ctx context.Context, opts *ObjectsOpts) (interface{}, error) { | |
1207 | newObjects, err := c.ObjectNames(ctx, container, opts) | |
1203 | 1208 | if err == nil { |
1204 | 1209 | objects = append(objects, newObjects...) |
1205 | 1210 | } |
1226 | 1231 | } |
1227 | 1232 | |
1228 | 1233 | // Account returns info about the account in an Account struct. |
1229 | func (c *Connection) Account() (info Account, headers Headers, err error) { | |
1234 | func (c *Connection) Account(ctx context.Context) (info Account, headers Headers, err error) { | |
1230 | 1235 | var resp *http.Response |
1231 | resp, headers, err = c.storage(RequestOpts{ | |
1236 | resp, headers, err = c.storage(ctx, RequestOpts{ | |
1232 | 1237 | Operation: "HEAD", |
1233 | 1238 | ErrorMap: ContainerErrorMap, |
1234 | 1239 | NoResponse: true, |
1261 | 1266 | // Add or update keys by mentioning them in the Headers. |
1262 | 1267 | // |
1263 | 1268 | // Remove keys by setting them to an empty string. |
1264 | func (c *Connection) AccountUpdate(h Headers) error { | |
1265 | _, _, err := c.storage(RequestOpts{ | |
1269 | func (c *Connection) AccountUpdate(ctx context.Context, h Headers) error { | |
1270 | _, _, err := c.storage(ctx, RequestOpts{ | |
1266 | 1271 | Operation: "POST", |
1267 | 1272 | ErrorMap: ContainerErrorMap, |
1268 | 1273 | NoResponse: true, |
1276 | 1281 | // If you don't want to add Headers just pass in nil |
1277 | 1282 | // |
1278 | 1283 | // No error is returned if it already exists but the metadata if any will be updated. |
1279 | func (c *Connection) ContainerCreate(container string, h Headers) error { | |
1280 | _, _, err := c.storage(RequestOpts{ | |
1284 | func (c *Connection) ContainerCreate(ctx context.Context, container string, h Headers) error { | |
1285 | _, _, err := c.storage(ctx, RequestOpts{ | |
1281 | 1286 | Container: container, |
1282 | 1287 | Operation: "PUT", |
1283 | 1288 | ErrorMap: ContainerErrorMap, |
1290 | 1295 | // ContainerDelete deletes a container. |
1291 | 1296 | // |
1292 | 1297 | // May return ContainerDoesNotExist or ContainerNotEmpty |
1293 | func (c *Connection) ContainerDelete(container string) error { | |
1294 | _, _, err := c.storage(RequestOpts{ | |
1298 | func (c *Connection) ContainerDelete(ctx context.Context, container string) error { | |
1299 | _, _, err := c.storage(ctx, RequestOpts{ | |
1295 | 1300 | Container: container, |
1296 | 1301 | Operation: "DELETE", |
1297 | 1302 | ErrorMap: ContainerErrorMap, |
1302 | 1307 | |
1303 | 1308 | // Container returns info about a single container including any |
1304 | 1309 | // metadata in the headers. |
1305 | func (c *Connection) Container(container string) (info Container, headers Headers, err error) { | |
1310 | func (c *Connection) Container(ctx context.Context, container string) (info Container, headers Headers, err error) { | |
1306 | 1311 | var resp *http.Response |
1307 | resp, headers, err = c.storage(RequestOpts{ | |
1312 | resp, headers, err = c.storage(ctx, RequestOpts{ | |
1308 | 1313 | Container: container, |
1309 | 1314 | Operation: "HEAD", |
1310 | 1315 | ErrorMap: ContainerErrorMap, |
1331 | 1336 | // Remove keys by setting them to an empty string. |
1332 | 1337 | // |
1333 | 1338 | // Container metadata can only be read with Container() not with Containers(). |
1334 | func (c *Connection) ContainerUpdate(container string, h Headers) error { | |
1335 | _, _, err := c.storage(RequestOpts{ | |
1339 | func (c *Connection) ContainerUpdate(ctx context.Context, container string, h Headers) error { | |
1340 | _, _, err := c.storage(ctx, RequestOpts{ | |
1336 | 1341 | Container: container, |
1337 | 1342 | Operation: "POST", |
1338 | 1343 | ErrorMap: ContainerErrorMap, |
1468 | 1473 | // |
1469 | 1474 | // If contentType is set it will be used, otherwise one will be |
1470 | 1475 | // guessed from objectName using mime.TypeByExtension |
1471 | func (c *Connection) ObjectCreate(container string, objectName string, checkHash bool, Hash string, contentType string, h Headers) (file *ObjectCreateFile, err error) { | |
1476 | func (c *Connection) ObjectCreate(ctx context.Context, container string, objectName string, checkHash bool, Hash string, contentType string, h Headers) (file *ObjectCreateFile, err error) { | |
1472 | 1477 | extraHeaders := objectPutHeaders(objectName, &checkHash, Hash, contentType, h) |
1473 | 1478 | pipeReader, pipeWriter := io.Pipe() |
1474 | 1479 | file = &ObjectCreateFile{ |
1489 | 1494 | NoResponse: true, |
1490 | 1495 | ErrorMap: objectErrorMap, |
1491 | 1496 | } |
1492 | file.resp, file.headers, file.err = c.storage(opts) | |
1497 | file.resp, file.headers, file.err = c.storage(ctx, opts) | |
1493 | 1498 | // Signal finished |
1494 | 1499 | pipeReader.Close() |
1495 | 1500 | close(file.done) |
1497 | 1502 | return |
1498 | 1503 | } |
1499 | 1504 | |
1500 | func (c *Connection) ObjectSymlinkCreate(container string, symlink string, targetAccount string, targetContainer string, targetObject string, targetEtag string) (headers Headers, err error) { | |
1505 | func (c *Connection) ObjectSymlinkCreate(ctx context.Context, container string, symlink string, targetAccount string, targetContainer string, targetObject string, targetEtag string) (headers Headers, err error) { | |
1501 | 1506 | |
1502 | 1507 | EMPTY_MD5 := "d41d8cd98f00b204e9800998ecf8427e" |
1503 | 1508 | symHeaders := Headers{} |
1509 | 1514 | symHeaders["X-Symlink-Target-Etag"] = targetEtag |
1510 | 1515 | } |
1511 | 1516 | symHeaders["X-Symlink-Target"] = fmt.Sprintf("%s/%s", targetContainer, targetObject) |
1512 | _, err = c.ObjectPut(container, symlink, contents, true, EMPTY_MD5, "application/symlink", symHeaders) | |
1513 | return | |
1514 | } | |
1515 | ||
1516 | func (c *Connection) objectPut(container string, objectName string, contents io.Reader, checkHash bool, Hash string, contentType string, h Headers, parameters url.Values) (headers Headers, err error) { | |
1517 | _, err = c.ObjectPut(ctx, container, symlink, contents, true, EMPTY_MD5, "application/symlink", symHeaders) | |
1518 | return | |
1519 | } | |
1520 | ||
1521 | func (c *Connection) objectPut(ctx context.Context, container string, objectName string, contents io.Reader, checkHash bool, Hash string, contentType string, h Headers, parameters url.Values) (headers Headers, err error) { | |
1517 | 1522 | extraHeaders := objectPutHeaders(objectName, &checkHash, Hash, contentType, h) |
1518 | 1523 | hash := md5.New() |
1519 | 1524 | var body io.Reader = contents |
1520 | 1525 | if checkHash { |
1521 | 1526 | body = io.TeeReader(contents, hash) |
1522 | 1527 | } |
1523 | _, headers, err = c.storage(RequestOpts{ | |
1528 | _, headers, err = c.storage(ctx, RequestOpts{ | |
1524 | 1529 | Container: container, |
1525 | 1530 | ObjectName: objectName, |
1526 | 1531 | Operation: "PUT", |
1565 | 1570 | // |
1566 | 1571 | // If contentType is set it will be used, otherwise one will be |
1567 | 1572 | // guessed from objectName using mime.TypeByExtension |
1568 | func (c *Connection) ObjectPut(container string, objectName string, contents io.Reader, checkHash bool, Hash string, contentType string, h Headers) (headers Headers, err error) { | |
1569 | return c.objectPut(container, objectName, contents, checkHash, Hash, contentType, h, nil) | |
1573 | func (c *Connection) ObjectPut(ctx context.Context, container string, objectName string, contents io.Reader, checkHash bool, Hash string, contentType string, h Headers) (headers Headers, err error) { | |
1574 | return c.objectPut(ctx, container, objectName, contents, checkHash, Hash, contentType, h, nil) | |
1570 | 1575 | } |
1571 | 1576 | |
1572 | 1577 | // ObjectPutBytes creates an object from a []byte in a container. |
1573 | 1578 | // |
1574 | 1579 | // This is a simplified interface which checks the MD5. |
1575 | func (c *Connection) ObjectPutBytes(container string, objectName string, contents []byte, contentType string) (err error) { | |
1580 | func (c *Connection) ObjectPutBytes(ctx context.Context, container string, objectName string, contents []byte, contentType string) (err error) { | |
1576 | 1581 | buf := bytes.NewBuffer(contents) |
1577 | 1582 | h := Headers{"Content-Length": strconv.Itoa(len(contents))} |
1578 | _, err = c.ObjectPut(container, objectName, buf, true, "", contentType, h) | |
1583 | _, err = c.ObjectPut(ctx, container, objectName, buf, true, "", contentType, h) | |
1579 | 1584 | return |
1580 | 1585 | } |
1581 | 1586 | |
1582 | 1587 | // ObjectPutString creates an object from a string in a container. |
1583 | 1588 | // |
1584 | 1589 | // This is a simplified interface which checks the MD5 |
1585 | func (c *Connection) ObjectPutString(container string, objectName string, contents string, contentType string) (err error) { | |
1590 | func (c *Connection) ObjectPutString(ctx context.Context, container string, objectName string, contents string, contentType string) (err error) { | |
1586 | 1591 | buf := strings.NewReader(contents) |
1587 | 1592 | h := Headers{"Content-Length": strconv.Itoa(len(contents))} |
1588 | _, err = c.ObjectPut(container, objectName, buf, true, "", contentType, h) | |
1593 | _, err = c.ObjectPut(ctx, container, objectName, buf, true, "", contentType, h) | |
1589 | 1594 | return |
1590 | 1595 | } |
1591 | 1596 | |
1635 | 1640 | // unlike os.File |
1636 | 1641 | // |
1637 | 1642 | // Seek(0, 1) will return the current file pointer. |
1638 | func (file *ObjectOpenFile) Seek(offset int64, whence int) (newPos int64, err error) { | |
1643 | func (file *ObjectOpenFile) Seek(ctx context.Context, offset int64, whence int) (newPos int64, err error) { | |
1639 | 1644 | file.overSeeked = false |
1640 | 1645 | switch whence { |
1641 | 1646 | case 0: // relative to start |
1673 | 1678 | } else { |
1674 | 1679 | delete(file.headers, "Range") |
1675 | 1680 | } |
1676 | newFile, _, err := file.connection.ObjectOpen(file.container, file.objectName, false, file.headers) | |
1681 | newFile, _, err := file.connection.ObjectOpen(ctx, file.container, file.objectName, false, file.headers) | |
1677 | 1682 | if err != nil { |
1678 | 1683 | return |
1679 | 1684 | } |
1687 | 1692 | |
1688 | 1693 | // Length gets the objects content length either from a cached copy or |
1689 | 1694 | // from the server. |
1690 | func (file *ObjectOpenFile) Length() (int64, error) { | |
1695 | func (file *ObjectOpenFile) Length(ctx context.Context) (int64, error) { | |
1691 | 1696 | if !file.lengthOk { |
1692 | info, _, err := file.connection.Object(file.container, file.objectName) | |
1697 | info, _, err := file.connection.Object(ctx, file.container, file.objectName) | |
1693 | 1698 | file.length = info.Bytes |
1694 | 1699 | file.lengthOk = (err == nil) |
1695 | 1700 | return file.length, err |
1726 | 1731 | return |
1727 | 1732 | } |
1728 | 1733 | |
1729 | // Check it satisfies the interfaces | |
1730 | var _ io.ReadCloser = &ObjectOpenFile{} | |
1731 | var _ io.Seeker = &ObjectOpenFile{} | |
1732 | ||
1733 | func (c *Connection) objectOpenBase(container string, objectName string, checkHash bool, h Headers, parameters url.Values) (file *ObjectOpenFile, headers Headers, err error) { | |
1734 | func (c *Connection) objectOpenBase(ctx context.Context, container string, objectName string, checkHash bool, h Headers, parameters url.Values) (file *ObjectOpenFile, headers Headers, err error) { | |
1734 | 1735 | var resp *http.Response |
1735 | 1736 | opts := RequestOpts{ |
1736 | 1737 | Container: container, |
1740 | 1741 | Headers: h, |
1741 | 1742 | Parameters: parameters, |
1742 | 1743 | } |
1743 | resp, headers, err = c.storage(opts) | |
1744 | resp, headers, err = c.storage(ctx, opts) | |
1744 | 1745 | if err != nil { |
1745 | 1746 | return |
1746 | 1747 | } |
1770 | 1771 | return |
1771 | 1772 | } |
1772 | 1773 | |
1773 | func (c *Connection) objectOpen(container string, objectName string, checkHash bool, h Headers, parameters url.Values) (file *ObjectOpenFile, headers Headers, err error) { | |
1774 | func (c *Connection) objectOpen(ctx context.Context, container string, objectName string, checkHash bool, h Headers, parameters url.Values) (file *ObjectOpenFile, headers Headers, err error) { | |
1774 | 1775 | err = withLORetry(0, func() (Headers, int64, error) { |
1775 | file, headers, err = c.objectOpenBase(container, objectName, checkHash, h, parameters) | |
1776 | file, headers, err = c.objectOpenBase(ctx, container, objectName, checkHash, h, parameters) | |
1776 | 1777 | if err != nil { |
1777 | 1778 | return headers, 0, err |
1778 | 1779 | } |
1804 | 1805 | // you will need to download everything in the manifest separately. |
1805 | 1806 | // |
1806 | 1807 | // headers["Content-Type"] will give the content type if desired. |
1807 | func (c *Connection) ObjectOpen(container string, objectName string, checkHash bool, h Headers) (file *ObjectOpenFile, headers Headers, err error) { | |
1808 | return c.objectOpen(container, objectName, checkHash, h, nil) | |
1808 | func (c *Connection) ObjectOpen(ctx context.Context, container string, objectName string, checkHash bool, h Headers) (file *ObjectOpenFile, headers Headers, err error) { | |
1809 | return c.objectOpen(ctx, container, objectName, checkHash, h, nil) | |
1809 | 1810 | } |
1810 | 1811 | |
1811 | 1812 | // ObjectGet gets the object into the io.Writer contents. |
1817 | 1818 | // server. If it is wrong then it will return ObjectCorrupted. |
1818 | 1819 | // |
1819 | 1820 | // headers["Content-Type"] will give the content type if desired. |
1820 | func (c *Connection) ObjectGet(container string, objectName string, contents io.Writer, checkHash bool, h Headers) (headers Headers, err error) { | |
1821 | file, headers, err := c.ObjectOpen(container, objectName, checkHash, h) | |
1821 | func (c *Connection) ObjectGet(ctx context.Context, container string, objectName string, contents io.Writer, checkHash bool, h Headers) (headers Headers, err error) { | |
1822 | file, headers, err := c.ObjectOpen(ctx, container, objectName, checkHash, h) | |
1822 | 1823 | if err != nil { |
1823 | 1824 | return |
1824 | 1825 | } |
1830 | 1831 | // ObjectGetBytes returns an object as a []byte. |
1831 | 1832 | // |
1832 | 1833 | // This is a simplified interface which checks the MD5 |
1833 | func (c *Connection) ObjectGetBytes(container string, objectName string) (contents []byte, err error) { | |
1834 | func (c *Connection) ObjectGetBytes(ctx context.Context, container string, objectName string) (contents []byte, err error) { | |
1834 | 1835 | var buf bytes.Buffer |
1835 | _, err = c.ObjectGet(container, objectName, &buf, true, nil) | |
1836 | _, err = c.ObjectGet(ctx, container, objectName, &buf, true, nil) | |
1836 | 1837 | contents = buf.Bytes() |
1837 | 1838 | return |
1838 | 1839 | } |
1840 | 1841 | // ObjectGetString returns an object as a string. |
1841 | 1842 | // |
1842 | 1843 | // This is a simplified interface which checks the MD5 |
1843 | func (c *Connection) ObjectGetString(container string, objectName string) (contents string, err error) { | |
1844 | func (c *Connection) ObjectGetString(ctx context.Context, container string, objectName string) (contents string, err error) { | |
1844 | 1845 | var buf bytes.Buffer |
1845 | _, err = c.ObjectGet(container, objectName, &buf, true, nil) | |
1846 | _, err = c.ObjectGet(ctx, container, objectName, &buf, true, nil) | |
1846 | 1847 | contents = buf.String() |
1847 | 1848 | return |
1848 | 1849 | } |
1850 | 1851 | // ObjectDelete deletes the object. |
1851 | 1852 | // |
1852 | 1853 | // May return ObjectNotFound if the object isn't found |
1853 | func (c *Connection) ObjectDelete(container string, objectName string) error { | |
1854 | _, _, err := c.storage(RequestOpts{ | |
1854 | func (c *Connection) ObjectDelete(ctx context.Context, container string, objectName string) error { | |
1855 | _, _, err := c.storage(ctx, RequestOpts{ | |
1855 | 1856 | Container: container, |
1856 | 1857 | ObjectName: objectName, |
1857 | 1858 | Operation: "DELETE", |
1908 | 1909 | Headers Headers // Response HTTP headers. |
1909 | 1910 | } |
1910 | 1911 | |
1911 | func (c *Connection) doBulkDelete(objects []string, h Headers) (result BulkDeleteResult, err error) { | |
1912 | func (c *Connection) doBulkDelete(ctx context.Context, objects []string, h Headers) (result BulkDeleteResult, err error) { | |
1912 | 1913 | var buffer bytes.Buffer |
1913 | 1914 | for _, s := range objects { |
1914 | 1915 | u := url.URL{Path: s} |
1922 | 1923 | for key, value := range h { |
1923 | 1924 | extraHeaders[key] = value |
1924 | 1925 | } |
1925 | resp, headers, err := c.storage(RequestOpts{ | |
1926 | resp, headers, err := c.storage(ctx, RequestOpts{ | |
1926 | 1927 | Operation: "DELETE", |
1927 | 1928 | Parameters: url.Values{"bulk-delete": []string{"1"}}, |
1928 | 1929 | Headers: extraHeaders, |
1966 | 1967 | // See also: |
1967 | 1968 | // * http://docs.openstack.org/trunk/openstack-object-storage/admin/content/object-storage-bulk-delete.html |
1968 | 1969 | // * http://docs.rackspace.com/files/api/v1/cf-devguide/content/Bulk_Delete-d1e2338.html |
1969 | func (c *Connection) BulkDelete(container string, objectNames []string) (result BulkDeleteResult, err error) { | |
1970 | return c.BulkDeleteHeaders(container, objectNames, nil) | |
1970 | func (c *Connection) BulkDelete(ctx context.Context, container string, objectNames []string) (result BulkDeleteResult, err error) { | |
1971 | return c.BulkDeleteHeaders(ctx, container, objectNames, nil) | |
1971 | 1972 | } |
1972 | 1973 | |
1973 | 1974 | // BulkDeleteHeaders deletes multiple objectNames from container in one operation. |
1978 | 1979 | // See also: |
1979 | 1980 | // * http://docs.openstack.org/trunk/openstack-object-storage/admin/content/object-storage-bulk-delete.html |
1980 | 1981 | // * http://docs.rackspace.com/files/api/v1/cf-devguide/content/Bulk_Delete-d1e2338.html |
1981 | func (c *Connection) BulkDeleteHeaders(container string, objectNames []string, h Headers) (result BulkDeleteResult, err error) { | |
1982 | func (c *Connection) BulkDeleteHeaders(ctx context.Context, container string, objectNames []string, h Headers) (result BulkDeleteResult, err error) { | |
1982 | 1983 | if len(objectNames) == 0 { |
1983 | 1984 | result.Errors = make(map[string]error) |
1984 | 1985 | return |
1987 | 1988 | for i, name := range objectNames { |
1988 | 1989 | fullPaths[i] = fmt.Sprintf("/%s/%s", container, name) |
1989 | 1990 | } |
1990 | return c.doBulkDelete(fullPaths, h) | |
1991 | return c.doBulkDelete(ctx, fullPaths, h) | |
1991 | 1992 | } |
1992 | 1993 | |
1993 | 1994 | // BulkUploadResult stores results of BulkUpload(). |
2020 | 2021 | // See also: |
2021 | 2022 | // * http://docs.openstack.org/trunk/openstack-object-storage/admin/content/object-storage-extract-archive.html |
2022 | 2023 | // * http://docs.rackspace.com/files/api/v1/cf-devguide/content/Extract_Archive-d1e2338.html |
2023 | func (c *Connection) BulkUpload(uploadPath string, dataStream io.Reader, format string, h Headers) (result BulkUploadResult, err error) { | |
2024 | func (c *Connection) BulkUpload(ctx context.Context, uploadPath string, dataStream io.Reader, format string, h Headers) (result BulkUploadResult, err error) { | |
2024 | 2025 | extraHeaders := Headers{"Accept": "application/json"} |
2025 | 2026 | for key, value := range h { |
2026 | 2027 | extraHeaders[key] = value |
2027 | 2028 | } |
2028 | 2029 | // The following code abuses Container parameter intentionally. |
2029 | 2030 | // The best fix might be to rename Container to UploadPath. |
2030 | resp, headers, err := c.storage(RequestOpts{ | |
2031 | resp, headers, err := c.storage(ctx, RequestOpts{ | |
2031 | 2032 | Container: uploadPath, |
2032 | 2033 | Operation: "PUT", |
2033 | 2034 | Parameters: url.Values{"extract-archive": []string{format}}, |
2072 | 2073 | // May return ObjectNotFound. |
2073 | 2074 | // |
2074 | 2075 | // Use headers.ObjectMetadata() to read the metadata in the Headers. |
2075 | func (c *Connection) Object(container string, objectName string) (info Object, headers Headers, err error) { | |
2076 | func (c *Connection) Object(ctx context.Context, container string, objectName string) (info Object, headers Headers, err error) { | |
2076 | 2077 | err = withLORetry(0, func() (Headers, int64, error) { |
2077 | info, headers, err = c.objectBase(container, objectName) | |
2078 | info, headers, err = c.objectBase(ctx, container, objectName) | |
2078 | 2079 | if err != nil { |
2079 | 2080 | return headers, 0, err |
2080 | 2081 | } |
2083 | 2084 | return |
2084 | 2085 | } |
2085 | 2086 | |
2086 | func (c *Connection) objectBase(container string, objectName string) (info Object, headers Headers, err error) { | |
2087 | func (c *Connection) objectBase(ctx context.Context, container string, objectName string) (info Object, headers Headers, err error) { | |
2087 | 2088 | var resp *http.Response |
2088 | resp, headers, err = c.storage(RequestOpts{ | |
2089 | resp, headers, err = c.storage(ctx, RequestOpts{ | |
2089 | 2090 | Container: container, |
2090 | 2091 | ObjectName: objectName, |
2091 | 2092 | Operation: "HEAD", |
2155 | 2156 | // other headers such as Content-Type or CORS headers. |
2156 | 2157 | // |
2157 | 2158 | // May return ObjectNotFound. |
2158 | func (c *Connection) ObjectUpdate(container string, objectName string, h Headers) error { | |
2159 | _, _, err := c.storage(RequestOpts{ | |
2159 | func (c *Connection) ObjectUpdate(ctx context.Context, container string, objectName string, h Headers) error { | |
2160 | _, _, err := c.storage(ctx, RequestOpts{ | |
2160 | 2161 | Container: container, |
2161 | 2162 | ObjectName: objectName, |
2162 | 2163 | Operation: "POST", |
2185 | 2186 | // |
2186 | 2187 | // You can use this to copy an object to itself - this is the only way |
2187 | 2188 | // to update the content type of an object. |
2188 | func (c *Connection) ObjectCopy(srcContainer string, srcObjectName string, dstContainer string, dstObjectName string, h Headers) (headers Headers, err error) { | |
2189 | func (c *Connection) ObjectCopy(ctx context.Context, srcContainer string, srcObjectName string, dstContainer string, dstObjectName string, h Headers) (headers Headers, err error) { | |
2189 | 2190 | // Meta stuff |
2190 | 2191 | extraHeaders := map[string]string{ |
2191 | 2192 | "Destination": urlPathEscape(dstContainer + "/" + dstObjectName), |
2193 | 2194 | for key, value := range h { |
2194 | 2195 | extraHeaders[key] = value |
2195 | 2196 | } |
2196 | _, headers, err = c.storage(RequestOpts{ | |
2197 | _, headers, err = c.storage(ctx, RequestOpts{ | |
2197 | 2198 | Container: srcContainer, |
2198 | 2199 | ObjectName: srcObjectName, |
2199 | 2200 | Operation: "COPY", |
2211 | 2212 | // All metadata is preserved. |
2212 | 2213 | // |
2213 | 2214 | // The destination container must exist before the copy. |
2214 | func (c *Connection) ObjectMove(srcContainer string, srcObjectName string, dstContainer string, dstObjectName string) (err error) { | |
2215 | _, err = c.ObjectCopy(srcContainer, srcObjectName, dstContainer, dstObjectName, nil) | |
2215 | func (c *Connection) ObjectMove(ctx context.Context, srcContainer string, srcObjectName string, dstContainer string, dstObjectName string) (err error) { | |
2216 | _, err = c.ObjectCopy(ctx, srcContainer, srcObjectName, dstContainer, dstObjectName, nil) | |
2216 | 2217 | if err != nil { |
2217 | 2218 | return |
2218 | 2219 | } |
2219 | return c.ObjectDelete(srcContainer, srcObjectName) | |
2220 | return c.ObjectDelete(ctx, srcContainer, srcObjectName) | |
2220 | 2221 | } |
2221 | 2222 | |
2222 | 2223 | // ObjectUpdateContentType updates the content type of an object |
2224 | 2225 | // This is a convenience method which calls ObjectCopy |
2225 | 2226 | // |
2226 | 2227 | // All other metadata is preserved. |
2227 | func (c *Connection) ObjectUpdateContentType(container string, objectName string, contentType string) (err error) { | |
2228 | func (c *Connection) ObjectUpdateContentType(ctx context.Context, container string, objectName string, contentType string) (err error) { | |
2228 | 2229 | h := Headers{"Content-Type": contentType} |
2229 | _, err = c.ObjectCopy(container, objectName, container, objectName, h) | |
2230 | _, err = c.ObjectCopy(ctx, container, objectName, container, objectName, h) | |
2230 | 2231 | return |
2231 | 2232 | } |
2232 | 2233 | |
2238 | 2239 | // |
2239 | 2240 | // If the server doesn't support versioning then it will return |
2240 | 2241 | // Forbidden however it will have created both the containers at that point. |
2241 | func (c *Connection) VersionContainerCreate(current, version string) error { | |
2242 | if err := c.ContainerCreate(version, nil); err != nil { | |
2242 | func (c *Connection) VersionContainerCreate(ctx context.Context, current, version string) error { | |
2243 | if err := c.ContainerCreate(ctx, version, nil); err != nil { | |
2243 | 2244 | return err |
2244 | 2245 | } |
2245 | if err := c.ContainerCreate(current, nil); err != nil { | |
2246 | if err := c.ContainerCreate(ctx, current, nil); err != nil { | |
2246 | 2247 | return err |
2247 | 2248 | } |
2248 | if err := c.VersionEnable(current, version); err != nil { | |
2249 | if err := c.VersionEnable(ctx, current, version); err != nil { | |
2249 | 2250 | return err |
2250 | 2251 | } |
2251 | 2252 | return nil |
2254 | 2255 | // VersionEnable enables versioning on the current container with version as the tracking container. |
2255 | 2256 | // |
2256 | 2257 | // May return Forbidden if this isn't supported by the server |
2257 | func (c *Connection) VersionEnable(current, version string) error { | |
2258 | func (c *Connection) VersionEnable(ctx context.Context, current, version string) error { | |
2258 | 2259 | h := Headers{"X-Versions-Location": version} |
2259 | if err := c.ContainerUpdate(current, h); err != nil { | |
2260 | if err := c.ContainerUpdate(ctx, current, h); err != nil { | |
2260 | 2261 | return err |
2261 | 2262 | } |
2262 | 2263 | // Check to see if the header was set properly |
2263 | _, headers, err := c.Container(current) | |
2264 | _, headers, err := c.Container(ctx, current) | |
2264 | 2265 | if err != nil { |
2265 | 2266 | return err |
2266 | 2267 | } |
2272 | 2273 | } |
2273 | 2274 | |
2274 | 2275 | // VersionDisable disables versioning on the current container. |
2275 | func (c *Connection) VersionDisable(current string) error { | |
2276 | func (c *Connection) VersionDisable(ctx context.Context, current string) error { | |
2276 | 2277 | h := Headers{"X-Versions-Location": ""} |
2277 | if err := c.ContainerUpdate(current, h); err != nil { | |
2278 | if err := c.ContainerUpdate(ctx, current, h); err != nil { | |
2278 | 2279 | return err |
2279 | 2280 | } |
2280 | 2281 | return nil |
2283 | 2284 | // VersionObjectList returns a list of older versions of the object. |
2284 | 2285 | // |
2285 | 2286 | // Objects are returned in the format <length><object_name>/<timestamp> |
2286 | func (c *Connection) VersionObjectList(version, object string) ([]string, error) { | |
2287 | func (c *Connection) VersionObjectList(ctx context.Context, version, object string) ([]string, error) { | |
2287 | 2288 | opts := &ObjectsOpts{ |
2288 | 2289 | // <3-character zero-padded hexadecimal character length><object name>/ |
2289 | 2290 | Prefix: fmt.Sprintf("%03x", len(object)) + object + "/", |
2290 | 2291 | } |
2291 | return c.ObjectNames(version, opts) | |
2292 | } | |
2292 | return c.ObjectNames(ctx, version, opts) | |
2293 | } |
5 | 5 | package swift |
6 | 6 | |
7 | 7 | import ( |
8 | "context" | |
8 | 9 | "fmt" |
9 | 10 | "io" |
10 | 11 | "net" |
315 | 316 | }).Url("/v1.0") |
316 | 317 | defer server.Finished() |
317 | 318 | |
318 | err := c.Authenticate() | |
319 | ctx := context.Background() | |
320 | err := c.Authenticate(ctx) | |
319 | 321 | if err != nil { |
320 | 322 | t.Fatal(err) |
321 | 323 | } |
335 | 337 | server.AddCheck(t).Error(401, "DENIED") |
336 | 338 | defer server.Finished() |
337 | 339 | c.UnAuthenticate() |
338 | err := c.Authenticate() | |
340 | err := c.Authenticate(context.Background()) | |
339 | 341 | if err != AuthorizationFailed { |
340 | 342 | t.Fatal("Expecting AuthorizationFailed", err) |
341 | 343 | } |
350 | 352 | "X-Storage-Url": PROXY_URL, |
351 | 353 | }) |
352 | 354 | defer server.Finished() |
353 | err := c.Authenticate() | |
355 | ctx := context.Background() | |
356 | err := c.Authenticate(ctx) | |
354 | 357 | checkError(t, err, 0, "Response didn't have storage url and auth token") |
355 | 358 | if c.Authenticated() { |
356 | 359 | t.Fatal("Expecting not authenticated") |
359 | 362 | server.AddCheck(t).Out(Headers{ |
360 | 363 | "X-Auth-Token": AUTH_TOKEN, |
361 | 364 | }) |
362 | err = c.Authenticate() | |
365 | err = c.Authenticate(ctx) | |
363 | 366 | checkError(t, err, 0, "Response didn't have storage url and auth token") |
364 | 367 | if c.Authenticated() { |
365 | 368 | t.Fatal("Expecting not authenticated") |
366 | 369 | } |
367 | 370 | |
368 | 371 | server.AddCheck(t) |
369 | err = c.Authenticate() | |
372 | err = c.Authenticate(ctx) | |
370 | 373 | checkError(t, err, 0, "Response didn't have storage url and auth token") |
371 | 374 | if c.Authenticated() { |
372 | 375 | t.Fatal("Expecting not authenticated") |
376 | 379 | "X-Storage-Url": PROXY_URL, |
377 | 380 | "X-Auth-Token": AUTH_TOKEN, |
378 | 381 | }) |
379 | err = c.Authenticate() | |
382 | err = c.Authenticate(ctx) | |
380 | 383 | if err != nil { |
381 | 384 | t.Fatal(err) |
382 | 385 | } |
390 | 393 | "User-Agent": DefaultUserAgent, |
391 | 394 | "X-Auth-Token": AUTH_TOKEN, |
392 | 395 | }).Tx(rx).Url("/proxy") |
393 | containers, err := c.ContainerNames(nil) | |
396 | containers, err := c.ContainerNames(context.Background(), nil) | |
394 | 397 | if err != nil { |
395 | 398 | t.Fatal(err) |
396 | 399 | } |
419 | 422 | "Content-Type": "text/plain", |
420 | 423 | }).Rx("12345") |
421 | 424 | defer server.Finished() |
422 | c.ObjectPutBytes("container", "object", []byte{'1', '2', '3', '4', '5'}, "text/plain") | |
425 | c.ObjectPutBytes(context.Background(), "container", "object", []byte{'1', '2', '3', '4', '5'}, "text/plain") | |
423 | 426 | } |
424 | 427 | |
425 | 428 | func TestInternalObjectPutString(t *testing.T) { |
430 | 433 | "Content-Type": "text/plain", |
431 | 434 | }).Rx("12345") |
432 | 435 | defer server.Finished() |
433 | c.ObjectPutString("container", "object", "12345", "text/plain") | |
436 | c.ObjectPutString(context.Background(), "container", "object", "12345", "text/plain") | |
434 | 437 | } |
435 | 438 | |
436 | 439 | func TestSetFromEnv(t *testing.T) { |
14 | 14 | import ( |
15 | 15 | "archive/tar" |
16 | 16 | "bytes" |
17 | "context" | |
17 | 18 | "crypto/md5" |
18 | 19 | "crypto/rand" |
19 | 20 | "crypto/tls" |
32 | 33 | "testing" |
33 | 34 | "time" |
34 | 35 | |
35 | "github.com/ncw/swift" | |
36 | "github.com/ncw/swift/swifttest" | |
36 | "github.com/ncw/swift/v2" | |
37 | "github.com/ncw/swift/v2/swifttest" | |
37 | 38 | ) |
38 | 39 | |
39 | 40 | var ( |
143 | 144 | } |
144 | 145 | |
145 | 146 | func makeConnectionAuth(t *testing.T) (*swift.Connection, func()) { |
147 | ctx := context.Background() | |
146 | 148 | c, rollback := makeConnection(t) |
147 | err := c.Authenticate() | |
149 | err := c.Authenticate(ctx) | |
148 | 150 | if err != nil { |
149 | 151 | t.Fatal("Auth failed", err) |
150 | 152 | } |
152 | 154 | } |
153 | 155 | |
154 | 156 | func makeConnectionWithContainer(t *testing.T) (*swift.Connection, func()) { |
157 | ctx := context.Background() | |
155 | 158 | c, rollback := makeConnectionAuth(t) |
156 | err := c.ContainerCreate(CONTAINER, m1.ContainerHeaders()) | |
159 | err := c.ContainerCreate(ctx, CONTAINER, m1.ContainerHeaders()) | |
157 | 160 | if err != nil { |
158 | 161 | t.Fatal(err) |
159 | 162 | } |
160 | 163 | return c, func() { |
161 | c.ContainerDelete(CONTAINER) | |
164 | _ = c.ContainerDelete(ctx, CONTAINER) | |
162 | 165 | rollback() |
163 | 166 | } |
164 | 167 | } |
165 | 168 | |
166 | 169 | func makeConnectionWithObject(t *testing.T) (*swift.Connection, func()) { |
170 | ctx := context.Background() | |
167 | 171 | c, rollback := makeConnectionWithContainer(t) |
168 | err := c.ObjectPutString(CONTAINER, OBJECT, CONTENTS, "") | |
172 | err := c.ObjectPutString(ctx, CONTAINER, OBJECT, CONTENTS, "") | |
169 | 173 | if err != nil { |
170 | 174 | t.Fatal(err) |
171 | 175 | } |
172 | 176 | return c, func() { |
173 | c.ObjectDelete(CONTAINER, OBJECT) | |
177 | _ = c.ObjectDelete(ctx, CONTAINER, OBJECT) | |
174 | 178 | rollback() |
175 | 179 | } |
176 | 180 | } |
177 | 181 | |
178 | 182 | func makeConnectionWithObjectHeaders(t *testing.T) (*swift.Connection, func()) { |
183 | ctx := context.Background() | |
179 | 184 | c, rollback := makeConnectionWithObject(t) |
180 | err := c.ObjectUpdate(CONTAINER, OBJECT, m1.ObjectHeaders()) | |
185 | err := c.ObjectUpdate(ctx, CONTAINER, OBJECT, m1.ObjectHeaders()) | |
181 | 186 | if err != nil { |
182 | 187 | t.Fatal(err) |
183 | 188 | } |
185 | 190 | } |
186 | 191 | |
187 | 192 | func makeConnectionWithVersionsContainer(t *testing.T) (*swift.Connection, func()) { |
193 | ctx := context.Background() | |
188 | 194 | c, rollback := makeConnectionAuth(t) |
189 | err := c.VersionContainerCreate(CURRENT_CONTAINER, VERSIONS_CONTAINER) | |
195 | err := c.VersionContainerCreate(ctx, CURRENT_CONTAINER, VERSIONS_CONTAINER) | |
190 | 196 | newRollback := func() { |
191 | c.ContainerDelete(CURRENT_CONTAINER) | |
192 | c.ContainerDelete(VERSIONS_CONTAINER) | |
197 | _ = c.ContainerDelete(ctx, CURRENT_CONTAINER) | |
198 | _ = c.ContainerDelete(ctx, VERSIONS_CONTAINER) | |
193 | 199 | rollback() |
194 | 200 | } |
195 | 201 | if err != nil { |
203 | 209 | } |
204 | 210 | |
205 | 211 | func makeConnectionWithVersionsObject(t *testing.T) (*swift.Connection, func()) { |
212 | ctx := context.Background() | |
206 | 213 | c, rollback := makeConnectionWithVersionsContainer(t) |
207 | if err := c.ObjectPutString(CURRENT_CONTAINER, OBJECT, CONTENTS, ""); err != nil { | |
214 | if err := c.ObjectPutString(ctx, CURRENT_CONTAINER, OBJECT, CONTENTS, ""); err != nil { | |
208 | 215 | t.Fatal(err) |
209 | 216 | } |
210 | 217 | // Version 2 |
211 | if err := c.ObjectPutString(CURRENT_CONTAINER, OBJECT, CONTENTS2, ""); err != nil { | |
218 | if err := c.ObjectPutString(ctx, CURRENT_CONTAINER, OBJECT, CONTENTS2, ""); err != nil { | |
212 | 219 | t.Fatal(err) |
213 | 220 | } |
214 | 221 | // Version 3 |
215 | if err := c.ObjectPutString(CURRENT_CONTAINER, OBJECT, CONTENTS2, ""); err != nil { | |
222 | if err := c.ObjectPutString(ctx, CURRENT_CONTAINER, OBJECT, CONTENTS2, ""); err != nil { | |
216 | 223 | t.Fatal(err) |
217 | 224 | } |
218 | 225 | return c, func() { |
219 | 226 | for i := 0; i < 3; i++ { |
220 | c.ObjectDelete(CURRENT_CONTAINER, OBJECT) | |
227 | _ = c.ObjectDelete(ctx, CURRENT_CONTAINER, OBJECT) | |
221 | 228 | } |
222 | 229 | rollback() |
223 | 230 | } |
224 | 231 | } |
225 | 232 | |
226 | 233 | func makeConnectionWithSegmentsContainer(t *testing.T) (*swift.Connection, func()) { |
234 | ctx := context.Background() | |
227 | 235 | c, rollback := makeConnectionWithContainer(t) |
228 | err := c.ContainerCreate(SEGMENTS_CONTAINER, swift.Headers{}) | |
236 | err := c.ContainerCreate(ctx, SEGMENTS_CONTAINER, swift.Headers{}) | |
229 | 237 | if err != nil { |
230 | 238 | t.Fatal(err) |
231 | 239 | } |
232 | 240 | return c, func() { |
233 | err = c.ContainerDelete(SEGMENTS_CONTAINER) | |
241 | err = c.ContainerDelete(ctx, SEGMENTS_CONTAINER) | |
234 | 242 | if err != nil { |
235 | 243 | t.Fatal(err) |
236 | 244 | } |
239 | 247 | } |
240 | 248 | |
241 | 249 | func makeConnectionWithDLO(t *testing.T) (*swift.Connection, func()) { |
250 | ctx := context.Background() | |
242 | 251 | c, rollback := makeConnectionWithSegmentsContainer(t) |
243 | 252 | opts := swift.LargeObjectOpts{ |
244 | 253 | Container: CONTAINER, |
245 | 254 | ObjectName: OBJECT, |
246 | 255 | ContentType: "image/jpeg", |
247 | 256 | } |
248 | out, err := c.DynamicLargeObjectCreate(&opts) | |
257 | out, err := c.DynamicLargeObjectCreate(ctx, &opts) | |
249 | 258 | if err != nil { |
250 | 259 | t.Fatal(err) |
251 | 260 | } |
255 | 264 | t.Fatal(err) |
256 | 265 | } |
257 | 266 | } |
258 | err = out.Close() | |
267 | err = out.CloseWithContext(ctx) | |
259 | 268 | if err != nil { |
260 | 269 | t.Error(err) |
261 | 270 | } |
262 | 271 | return c, func() { |
263 | c.DynamicLargeObjectDelete(CONTAINER, OBJECT) | |
272 | _ = c.DynamicLargeObjectDelete(ctx, CONTAINER, OBJECT) | |
264 | 273 | rollback() |
265 | 274 | } |
266 | 275 | } |
267 | 276 | |
268 | 277 | func makeConnectionWithSLO(t *testing.T) (*swift.Connection, func()) { |
278 | ctx := context.Background() | |
269 | 279 | c, rollback := makeConnectionWithSegmentsContainer(t) |
270 | 280 | opts := swift.LargeObjectOpts{ |
271 | 281 | Container: CONTAINER, |
272 | 282 | ObjectName: OBJECT, |
273 | 283 | ContentType: "image/jpeg", |
274 | 284 | } |
275 | out, err := c.StaticLargeObjectCreate(&opts) | |
285 | out, err := c.StaticLargeObjectCreate(ctx, &opts) | |
276 | 286 | if err != nil { |
277 | 287 | if err == swift.SLONotSupported { |
278 | 288 | t.Skip("SLO not supported") |
286 | 296 | t.Fatal(err) |
287 | 297 | } |
288 | 298 | } |
289 | err = out.Close() | |
299 | err = out.CloseWithContext(ctx) | |
290 | 300 | if err != nil { |
291 | 301 | t.Error(err) |
292 | 302 | } |
293 | 303 | return c, func() { |
294 | c.StaticLargeObjectDelete(CONTAINER, OBJECT) | |
304 | _ = c.StaticLargeObjectDelete(ctx, CONTAINER, OBJECT) | |
295 | 305 | rollback() |
296 | 306 | } |
297 | 307 | } |
302 | 312 | } |
303 | 313 | |
304 | 314 | func getSwinftInfo(t *testing.T) (info swift.SwiftInfo, err error) { |
315 | ctx := context.Background() | |
305 | 316 | c, rollback := makeConnectionAuth(t) |
306 | 317 | defer rollback() |
307 | return c.QueryInfo() | |
318 | return c.QueryInfo(ctx) | |
308 | 319 | } |
309 | 320 | |
310 | 321 | func TestTransport(t *testing.T) { |
322 | ctx := context.Background() | |
311 | 323 | c, rollback := makeConnection(t) |
312 | 324 | defer rollback() |
313 | 325 | |
325 | 337 | |
326 | 338 | c.Transport = tr |
327 | 339 | |
328 | err := c.Authenticate() | |
340 | err := c.Authenticate(ctx) | |
329 | 341 | if err != nil { |
330 | 342 | t.Fatal("Auth failed", err) |
331 | 343 | } |
336 | 348 | |
337 | 349 | // The following Test functions are run in order - this one must come before the others! |
338 | 350 | func TestV1V2Authenticate(t *testing.T) { |
351 | ctx := context.Background() | |
339 | 352 | if isV3Api() { |
340 | 353 | return |
341 | 354 | } |
342 | 355 | c, rollback := makeConnection(t) |
343 | 356 | defer rollback() |
344 | 357 | |
345 | err := c.Authenticate() | |
358 | err := c.Authenticate(ctx) | |
346 | 359 | if err != nil { |
347 | 360 | t.Fatal("Auth failed", err) |
348 | 361 | } |
352 | 365 | } |
353 | 366 | |
354 | 367 | func TestV3AuthenticateWithDomainNameAndTenantId(t *testing.T) { |
368 | ctx := context.Background() | |
355 | 369 | if !isV3Api() { |
356 | 370 | return |
357 | 371 | } |
364 | 378 | c.TenantId = os.Getenv("SWIFT_TENANT_ID") |
365 | 379 | c.DomainId = "" |
366 | 380 | |
367 | err := c.Authenticate() | |
381 | err := c.Authenticate(ctx) | |
368 | 382 | if err != nil { |
369 | 383 | t.Fatal("Auth failed", err) |
370 | 384 | } |
374 | 388 | } |
375 | 389 | |
376 | 390 | func TestV3TrustWithTrustId(t *testing.T) { |
391 | ctx := context.Background() | |
377 | 392 | if !isV3Api() { |
378 | 393 | return |
379 | 394 | } |
383 | 398 | |
384 | 399 | c.TrustId = os.Getenv("SWIFT_TRUST_ID") |
385 | 400 | |
386 | err := c.Authenticate() | |
401 | err := c.Authenticate(ctx) | |
387 | 402 | if err != nil { |
388 | 403 | t.Fatal("Auth failed", err) |
389 | 404 | } |
393 | 408 | } |
394 | 409 | |
395 | 410 | func TestV3AuthenticateWithDomainIdAndTenantId(t *testing.T) { |
411 | ctx := context.Background() | |
396 | 412 | if !isV3Api() { |
397 | 413 | return |
398 | 414 | } |
405 | 421 | c.TenantId = os.Getenv("SWIFT_TENANT_ID") |
406 | 422 | c.DomainId = os.Getenv("SWIFT_API_DOMAIN_ID") |
407 | 423 | |
408 | err := c.Authenticate() | |
424 | err := c.Authenticate(ctx) | |
409 | 425 | if err != nil { |
410 | 426 | t.Fatal("Auth failed", err) |
411 | 427 | } |
415 | 431 | } |
416 | 432 | |
417 | 433 | func TestV3AuthenticateWithDomainNameAndTenantName(t *testing.T) { |
434 | ctx := context.Background() | |
418 | 435 | if !isV3Api() { |
419 | 436 | return |
420 | 437 | } |
427 | 444 | c.TenantId = "" |
428 | 445 | c.DomainId = "" |
429 | 446 | |
430 | err := c.Authenticate() | |
447 | err := c.Authenticate(ctx) | |
431 | 448 | if err != nil { |
432 | 449 | t.Fatal("Auth failed", err) |
433 | 450 | } |
437 | 454 | } |
438 | 455 | |
439 | 456 | func TestV3AuthenticateWithDomainIdAndTenantName(t *testing.T) { |
457 | ctx := context.Background() | |
440 | 458 | if !isV3Api() { |
441 | 459 | return |
442 | 460 | } |
449 | 467 | c.TenantId = "" |
450 | 468 | c.DomainId = os.Getenv("SWIFT_API_DOMAIN_ID") |
451 | 469 | |
452 | err := c.Authenticate() | |
470 | err := c.Authenticate(ctx) | |
453 | 471 | if err != nil { |
454 | 472 | t.Fatal("Auth failed", err) |
455 | 473 | } |
462 | 480 | // |
463 | 481 | // Run with -race to test |
464 | 482 | func TestAuthenticateRace(t *testing.T) { |
483 | ctx := context.Background() | |
465 | 484 | c, rollback := makeConnection(t) |
466 | 485 | defer rollback() |
467 | 486 | var wg sync.WaitGroup |
469 | 488 | wg.Add(1) |
470 | 489 | go func() { |
471 | 490 | defer wg.Done() |
472 | err := c.Authenticate() | |
491 | err := c.Authenticate(ctx) | |
473 | 492 | if err != nil { |
474 | 493 | t.Fatal("Auth failed", err) |
475 | 494 | } |
483 | 502 | |
484 | 503 | // Test a connection can be serialized and unserialized with JSON |
485 | 504 | func TestSerializeConnectionJson(t *testing.T) { |
505 | ctx := context.Background() | |
486 | 506 | c, rollback := makeConnectionAuth(t) |
487 | 507 | defer rollback() |
488 | 508 | serializedConnection, err := json.Marshal(c) |
497 | 517 | if !c2.Authenticated() { |
498 | 518 | t.Fatal("Should be authenticated") |
499 | 519 | } |
500 | _, _, err = c2.Account() | |
520 | _, _, err = c2.Account(ctx) | |
501 | 521 | if err != nil { |
502 | 522 | t.Fatalf("Failed to use unserialized connection: %v", err) |
503 | 523 | } |
505 | 525 | |
506 | 526 | // Test a connection can be serialized and unserialized with XML |
507 | 527 | func TestSerializeConnectionXml(t *testing.T) { |
528 | ctx := context.Background() | |
508 | 529 | c, rollback := makeConnectionAuth(t) |
509 | 530 | defer rollback() |
510 | 531 | serializedConnection, err := xml.Marshal(c) |
519 | 540 | if !c2.Authenticated() { |
520 | 541 | t.Fatal("Should be authenticated") |
521 | 542 | } |
522 | _, _, err = c2.Account() | |
543 | _, _, err = c2.Account(ctx) | |
523 | 544 | if err != nil { |
524 | 545 | t.Fatalf("Failed to use unserialized connection: %v", err) |
525 | 546 | } |
527 | 548 | |
528 | 549 | // Test the reauthentication logic |
529 | 550 | func TestOnReAuth(t *testing.T) { |
551 | ctx := context.Background() | |
530 | 552 | c, rollback := makeConnectionAuth(t) |
531 | 553 | defer rollback() |
532 | 554 | c.UnAuthenticate() |
533 | _, _, err := c.Account() | |
555 | _, _, err := c.Account(ctx) | |
534 | 556 | if err != nil { |
535 | 557 | t.Fatalf("Failed to reauthenticate: %v", err) |
536 | 558 | } |
537 | 559 | } |
538 | 560 | |
539 | 561 | func TestAccount(t *testing.T) { |
562 | ctx := context.Background() | |
540 | 563 | c, rollback := makeConnectionAuth(t) |
541 | 564 | defer rollback() |
542 | info, headers, err := c.Account() | |
565 | info, headers, err := c.Account(ctx) | |
543 | 566 | if err != nil { |
544 | 567 | t.Fatal(err) |
545 | 568 | } |
571 | 594 | } |
572 | 595 | |
573 | 596 | func TestAccountUpdate(t *testing.T) { |
597 | ctx := context.Background() | |
574 | 598 | c, rollback := makeConnectionAuth(t) |
575 | 599 | defer rollback() |
576 | err := c.AccountUpdate(m1.AccountHeaders()) | |
577 | if err != nil { | |
578 | t.Fatal(err) | |
579 | } | |
580 | ||
581 | _, headers, err := c.Account() | |
600 | err := c.AccountUpdate(ctx, m1.AccountHeaders()) | |
601 | if err != nil { | |
602 | t.Fatal(err) | |
603 | } | |
604 | ||
605 | _, headers, err := c.Account(ctx) | |
582 | 606 | if err != nil { |
583 | 607 | t.Fatal(err) |
584 | 608 | } |
586 | 610 | delete(m, "temp-url-key") // remove X-Account-Meta-Temp-URL-Key if set |
587 | 611 | compareMaps(t, m, map[string]string{"hello": "1", "potato-salad": "2"}) |
588 | 612 | |
589 | err = c.AccountUpdate(m2.AccountHeaders()) | |
590 | if err != nil { | |
591 | t.Fatal(err) | |
592 | } | |
593 | ||
594 | _, headers, err = c.Account() | |
613 | err = c.AccountUpdate(ctx, m2.AccountHeaders()) | |
614 | if err != nil { | |
615 | t.Fatal(err) | |
616 | } | |
617 | ||
618 | _, headers, err = c.Account(ctx) | |
595 | 619 | if err != nil { |
596 | 620 | t.Fatal(err) |
597 | 621 | } |
601 | 625 | } |
602 | 626 | |
603 | 627 | func TestContainerCreate(t *testing.T) { |
628 | ctx := context.Background() | |
604 | 629 | c, rollback := makeConnectionAuth(t) |
605 | 630 | defer rollback() |
606 | err := c.ContainerCreate(CONTAINER, m1.ContainerHeaders()) | |
607 | if err != nil { | |
608 | t.Fatal(err) | |
609 | } | |
610 | err = c.ContainerDelete(CONTAINER) | |
631 | err := c.ContainerCreate(ctx, CONTAINER, m1.ContainerHeaders()) | |
632 | if err != nil { | |
633 | t.Fatal(err) | |
634 | } | |
635 | err = c.ContainerDelete(ctx, CONTAINER) | |
611 | 636 | if err != nil { |
612 | 637 | t.Fatal(err) |
613 | 638 | } |
614 | 639 | } |
615 | 640 | |
616 | 641 | func TestContainer(t *testing.T) { |
642 | ctx := context.Background() | |
617 | 643 | c, rollback := makeConnectionWithContainer(t) |
618 | 644 | defer rollback() |
619 | info, headers, err := c.Container(CONTAINER) | |
645 | info, headers, err := c.Container(ctx, CONTAINER) | |
620 | 646 | if err != nil { |
621 | 647 | t.Fatal(err) |
622 | 648 | } |
633 | 659 | } |
634 | 660 | |
635 | 661 | func TestContainersAll(t *testing.T) { |
662 | ctx := context.Background() | |
636 | 663 | c, rollback := makeConnectionWithContainer(t) |
637 | 664 | defer rollback() |
638 | containers1, err := c.ContainersAll(nil) | |
639 | if err != nil { | |
640 | t.Fatal(err) | |
641 | } | |
642 | containers2, err := c.Containers(nil) | |
665 | containers1, err := c.ContainersAll(ctx, nil) | |
666 | if err != nil { | |
667 | t.Fatal(err) | |
668 | } | |
669 | containers2, err := c.Containers(ctx, nil) | |
643 | 670 | if err != nil { |
644 | 671 | t.Fatal(err) |
645 | 672 | } |
654 | 681 | } |
655 | 682 | |
656 | 683 | func TestContainersAllWithLimit(t *testing.T) { |
684 | ctx := context.Background() | |
657 | 685 | c, rollback := makeConnectionWithContainer(t) |
658 | 686 | defer rollback() |
659 | containers1, err := c.ContainersAll(&swift.ContainersOpts{Limit: 1}) | |
660 | if err != nil { | |
661 | t.Fatal(err) | |
662 | } | |
663 | containers2, err := c.Containers(nil) | |
687 | containers1, err := c.ContainersAll(ctx, &swift.ContainersOpts{Limit: 1}) | |
688 | if err != nil { | |
689 | t.Fatal(err) | |
690 | } | |
691 | containers2, err := c.Containers(ctx, nil) | |
664 | 692 | if err != nil { |
665 | 693 | t.Fatal(err) |
666 | 694 | } |
675 | 703 | } |
676 | 704 | |
677 | 705 | func TestContainerUpdate(t *testing.T) { |
706 | ctx := context.Background() | |
678 | 707 | c, rollback := makeConnectionWithContainer(t) |
679 | 708 | defer rollback() |
680 | err := c.ContainerUpdate(CONTAINER, m2.ContainerHeaders()) | |
681 | if err != nil { | |
682 | t.Fatal(err) | |
683 | } | |
684 | _, headers, err := c.Container(CONTAINER) | |
709 | err := c.ContainerUpdate(ctx, CONTAINER, m2.ContainerHeaders()) | |
710 | if err != nil { | |
711 | t.Fatal(err) | |
712 | } | |
713 | _, headers, err := c.Container(ctx, CONTAINER) | |
685 | 714 | if err != nil { |
686 | 715 | t.Fatal(err) |
687 | 716 | } |
689 | 718 | } |
690 | 719 | |
691 | 720 | func TestContainerNames(t *testing.T) { |
721 | ctx := context.Background() | |
692 | 722 | c, rollback := makeConnectionWithContainer(t) |
693 | 723 | defer rollback() |
694 | containers, err := c.ContainerNames(nil) | |
724 | containers, err := c.ContainerNames(ctx, nil) | |
695 | 725 | if err != nil { |
696 | 726 | t.Fatal(err) |
697 | 727 | } |
708 | 738 | } |
709 | 739 | |
710 | 740 | func TestContainerNamesAll(t *testing.T) { |
741 | ctx := context.Background() | |
711 | 742 | c, rollback := makeConnectionWithContainer(t) |
712 | 743 | defer rollback() |
713 | containers1, err := c.ContainerNamesAll(nil) | |
714 | if err != nil { | |
715 | t.Fatal(err) | |
716 | } | |
717 | containers2, err := c.ContainerNames(nil) | |
744 | containers1, err := c.ContainerNamesAll(ctx, nil) | |
745 | if err != nil { | |
746 | t.Fatal(err) | |
747 | } | |
748 | containers2, err := c.ContainerNames(ctx, nil) | |
718 | 749 | if err != nil { |
719 | 750 | t.Fatal(err) |
720 | 751 | } |
729 | 760 | } |
730 | 761 | |
731 | 762 | func TestContainerNamesAllWithLimit(t *testing.T) { |
763 | ctx := context.Background() | |
732 | 764 | c, rollback := makeConnectionWithContainer(t) |
733 | 765 | defer rollback() |
734 | containers1, err := c.ContainerNamesAll(&swift.ContainersOpts{Limit: 1}) | |
735 | if err != nil { | |
736 | t.Fatal(err) | |
737 | } | |
738 | containers2, err := c.ContainerNames(nil) | |
766 | containers1, err := c.ContainerNamesAll(ctx, &swift.ContainersOpts{Limit: 1}) | |
767 | if err != nil { | |
768 | t.Fatal(err) | |
769 | } | |
770 | containers2, err := c.ContainerNames(ctx, nil) | |
739 | 771 | if err != nil { |
740 | 772 | t.Fatal(err) |
741 | 773 | } |
750 | 782 | } |
751 | 783 | |
752 | 784 | func TestObjectPutString(t *testing.T) { |
785 | ctx := context.Background() | |
753 | 786 | c, rollback := makeConnectionWithContainer(t) |
754 | 787 | defer rollback() |
755 | err := c.ObjectPutString(CONTAINER, OBJECT, CONTENTS, "") | |
788 | err := c.ObjectPutString(ctx, CONTAINER, OBJECT, CONTENTS, "") | |
756 | 789 | if err != nil { |
757 | 790 | t.Fatal(err) |
758 | 791 | } |
759 | 792 | defer func() { |
760 | err = c.ObjectDelete(CONTAINER, OBJECT) | |
793 | err = c.ObjectDelete(ctx, CONTAINER, OBJECT) | |
761 | 794 | if err != nil { |
762 | 795 | t.Fatal(err) |
763 | 796 | } |
764 | 797 | }() |
765 | 798 | |
766 | info, _, err := c.Object(CONTAINER, OBJECT) | |
799 | info, _, err := c.Object(ctx, CONTAINER, OBJECT) | |
767 | 800 | if err != nil { |
768 | 801 | t.Error(err) |
769 | 802 | } |
779 | 812 | } |
780 | 813 | |
781 | 814 | func TestObjectPut(t *testing.T) { |
815 | ctx := context.Background() | |
782 | 816 | c, rollback := makeConnectionWithContainer(t) |
783 | 817 | defer rollback() |
784 | 818 | |
787 | 821 | // Set content size incorrectly - should produce an error |
788 | 822 | headers["Content-Length"] = strconv.FormatInt(CONTENT_SIZE-1, 10) |
789 | 823 | contents := bytes.NewBufferString(CONTENTS) |
790 | h, err := c.ObjectPut(CONTAINER, OBJECT, contents, true, CONTENT_MD5, "text/plain", headers) | |
824 | h, err := c.ObjectPut(ctx, CONTAINER, OBJECT, contents, true, CONTENT_MD5, "text/plain", headers) | |
791 | 825 | if err == nil { |
792 | 826 | t.Fatal("Expecting error but didn't get one") |
793 | 827 | } |
795 | 829 | // Now set content size correctly |
796 | 830 | contents = bytes.NewBufferString(CONTENTS) |
797 | 831 | headers["Content-Length"] = strconv.FormatInt(CONTENT_SIZE, 10) |
798 | h, err = c.ObjectPut(CONTAINER, OBJECT, contents, true, CONTENT_MD5, "text/plain", headers) | |
832 | h, err = c.ObjectPut(ctx, CONTAINER, OBJECT, contents, true, CONTENT_MD5, "text/plain", headers) | |
799 | 833 | if err != nil { |
800 | 834 | t.Fatal(err) |
801 | 835 | } |
802 | 836 | defer func() { |
803 | err = c.ObjectDelete(CONTAINER, OBJECT) | |
837 | err = c.ObjectDelete(ctx, CONTAINER, OBJECT) | |
804 | 838 | if err != nil { |
805 | 839 | t.Fatal(err) |
806 | 840 | } |
811 | 845 | } |
812 | 846 | |
813 | 847 | // Fetch object info and compare |
814 | info, _, err := c.Object(CONTAINER, OBJECT) | |
848 | info, _, err := c.Object(ctx, CONTAINER, OBJECT) | |
815 | 849 | if err != nil { |
816 | 850 | t.Error(err) |
817 | 851 | } |
827 | 861 | } |
828 | 862 | |
829 | 863 | func TestObjectPutWithReauth(t *testing.T) { |
864 | ctx := context.Background() | |
830 | 865 | if !swift.IS_AT_LEAST_GO_16 { |
831 | 866 | return |
832 | 867 | } |
837 | 872 | c.AuthToken = "expiredtoken" |
838 | 873 | |
839 | 874 | r := strings.NewReader(CONTENTS) |
840 | _, err := c.ObjectPut(CONTAINER, OBJECT, r, true, "", "text/plain", nil) | |
841 | if err != nil { | |
842 | t.Fatal(err) | |
843 | } | |
844 | ||
845 | info, _, err := c.Object(CONTAINER, OBJECT) | |
875 | _, err := c.ObjectPut(ctx, CONTAINER, OBJECT, r, true, "", "text/plain", nil) | |
876 | if err != nil { | |
877 | t.Fatal(err) | |
878 | } | |
879 | ||
880 | info, _, err := c.Object(ctx, CONTAINER, OBJECT) | |
846 | 881 | if err != nil { |
847 | 882 | t.Error(err) |
848 | 883 | } |
858 | 893 | } |
859 | 894 | |
860 | 895 | func TestObjectPutStringWithReauth(t *testing.T) { |
896 | ctx := context.Background() | |
861 | 897 | if !swift.IS_AT_LEAST_GO_16 { |
862 | 898 | return |
863 | 899 | } |
867 | 903 | // Simulate that our auth token expired |
868 | 904 | c.AuthToken = "expiredtoken" |
869 | 905 | |
870 | err := c.ObjectPutString(CONTAINER, OBJECT, CONTENTS, "") | |
871 | if err != nil { | |
872 | t.Fatal(err) | |
873 | } | |
874 | ||
875 | info, _, err := c.Object(CONTAINER, OBJECT) | |
906 | err := c.ObjectPutString(ctx, CONTAINER, OBJECT, CONTENTS, "") | |
907 | if err != nil { | |
908 | t.Fatal(err) | |
909 | } | |
910 | ||
911 | info, _, err := c.Object(ctx, CONTAINER, OBJECT) | |
876 | 912 | if err != nil { |
877 | 913 | t.Error(err) |
878 | 914 | } |
888 | 924 | } |
889 | 925 | |
890 | 926 | func TestObjectEmpty(t *testing.T) { |
927 | ctx := context.Background() | |
891 | 928 | c, rollback := makeConnectionWithContainer(t) |
892 | 929 | defer rollback() |
893 | err := c.ObjectPutString(CONTAINER, EMPTYOBJECT, "", "") | |
930 | err := c.ObjectPutString(ctx, CONTAINER, EMPTYOBJECT, "", "") | |
894 | 931 | if err != nil { |
895 | 932 | t.Fatal(err) |
896 | 933 | } |
897 | 934 | defer func() { |
898 | err = c.ObjectDelete(CONTAINER, EMPTYOBJECT) | |
935 | err = c.ObjectDelete(ctx, CONTAINER, EMPTYOBJECT) | |
899 | 936 | if err != nil { |
900 | 937 | t.Error(err) |
901 | 938 | } |
902 | 939 | }() |
903 | 940 | |
904 | info, _, err := c.Object(CONTAINER, EMPTYOBJECT) | |
941 | info, _, err := c.Object(ctx, CONTAINER, EMPTYOBJECT) | |
905 | 942 | if err != nil { |
906 | 943 | t.Error(err) |
907 | 944 | } |
917 | 954 | } |
918 | 955 | |
919 | 956 | func TestSymlinkObject(t *testing.T) { |
957 | ctx := context.Background() | |
920 | 958 | info, err := getSwinftInfo(t) |
921 | 959 | if err != nil { |
922 | 960 | t.Fatal(err) |
930 | 968 | defer rollback() |
931 | 969 | |
932 | 970 | // write target objects |
933 | err = c.ObjectPutBytes(CONTAINER, OBJECT, []byte(CONTENTS), "text/potato") | |
971 | err = c.ObjectPutBytes(ctx, CONTAINER, OBJECT, []byte(CONTENTS), "text/potato") | |
934 | 972 | if err != nil { |
935 | 973 | t.Fatal(err) |
936 | 974 | } |
937 | 975 | defer func() { |
938 | err = c.ObjectDelete(CONTAINER, OBJECT) | |
976 | err = c.ObjectDelete(ctx, CONTAINER, OBJECT) | |
939 | 977 | if err != nil { |
940 | 978 | t.Error(err) |
941 | 979 | } |
942 | 980 | }() |
943 | 981 | |
944 | 982 | // test dynamic link |
945 | _, err = c.ObjectSymlinkCreate(CONTAINER, SYMLINK_OBJECT, "", CONTAINER, OBJECT, "") | |
983 | _, err = c.ObjectSymlinkCreate(ctx, CONTAINER, SYMLINK_OBJECT, "", CONTAINER, OBJECT, "") | |
946 | 984 | if err != nil { |
947 | 985 | t.Fatal(err) |
948 | 986 | } |
949 | 987 | defer func() { |
950 | err = c.ObjectDelete(CONTAINER, SYMLINK_OBJECT) | |
988 | err = c.ObjectDelete(ctx, CONTAINER, SYMLINK_OBJECT) | |
951 | 989 | if err != nil { |
952 | 990 | t.Error(err) |
953 | 991 | } |
954 | 992 | }() |
955 | 993 | |
956 | md, _, err := c.Object(CONTAINER, SYMLINK_OBJECT) | |
994 | md, _, err := c.Object(ctx, CONTAINER, SYMLINK_OBJECT) | |
957 | 995 | if err != nil { |
958 | 996 | t.Error(err) |
959 | 997 | } |
970 | 1008 | } |
971 | 1009 | |
972 | 1010 | func TestStaticSymlinkObject(t *testing.T) { |
1011 | ctx := context.Background() | |
973 | 1012 | info, err := getSwinftInfo(t) |
974 | 1013 | if err != nil { |
975 | 1014 | t.Fatal(err) |
988 | 1027 | defer rollback() |
989 | 1028 | |
990 | 1029 | // write target objects |
991 | err = c.ObjectPutBytes(CONTAINER, OBJECT2, []byte(CONTENTS2), "text/tomato") | |
1030 | err = c.ObjectPutBytes(ctx, CONTAINER, OBJECT2, []byte(CONTENTS2), "text/tomato") | |
992 | 1031 | if err != nil { |
993 | 1032 | t.Fatal(err) |
994 | 1033 | } |
995 | 1034 | defer func() { |
996 | err = c.ObjectDelete(CONTAINER, OBJECT2) | |
1035 | err = c.ObjectDelete(ctx, CONTAINER, OBJECT2) | |
997 | 1036 | if err != nil { |
998 | 1037 | t.Error(err) |
999 | 1038 | } |
1001 | 1040 | |
1002 | 1041 | // test static link |
1003 | 1042 | // first with the wrong target etag |
1004 | _, err = c.ObjectSymlinkCreate(CONTAINER, SYMLINK_OBJECT2, "", CONTAINER, OBJECT2, CONTENT_MD5) | |
1043 | _, err = c.ObjectSymlinkCreate(ctx, CONTAINER, SYMLINK_OBJECT2, "", CONTAINER, OBJECT2, CONTENT_MD5) | |
1005 | 1044 | if err == nil { |
1006 | 1045 | t.Error("Symlink with wrong target etag should have failed") |
1007 | 1046 | } |
1008 | 1047 | |
1009 | _, err = c.ObjectSymlinkCreate(CONTAINER, SYMLINK_OBJECT2, "", CONTAINER, OBJECT2, CONTENT2_MD5) | |
1048 | _, err = c.ObjectSymlinkCreate(ctx, CONTAINER, SYMLINK_OBJECT2, "", CONTAINER, OBJECT2, CONTENT2_MD5) | |
1010 | 1049 | if err != nil { |
1011 | 1050 | t.Fatal(err) |
1012 | 1051 | } |
1013 | 1052 | defer func() { |
1014 | err = c.ObjectDelete(CONTAINER, SYMLINK_OBJECT2) | |
1053 | err = c.ObjectDelete(ctx, CONTAINER, SYMLINK_OBJECT2) | |
1015 | 1054 | if err != nil { |
1016 | 1055 | t.Error(err) |
1017 | 1056 | } |
1018 | 1057 | }() |
1019 | 1058 | |
1020 | md, _, err := c.Object(CONTAINER, SYMLINK_OBJECT2) | |
1059 | md, _, err := c.Object(ctx, CONTAINER, SYMLINK_OBJECT2) | |
1021 | 1060 | if err != nil { |
1022 | 1061 | t.Error(err) |
1023 | 1062 | } |
1033 | 1072 | } |
1034 | 1073 | |
1035 | 1074 | func TestObjectPutBytes(t *testing.T) { |
1075 | ctx := context.Background() | |
1036 | 1076 | c, rollback := makeConnectionWithContainer(t) |
1037 | 1077 | defer rollback() |
1038 | err := c.ObjectPutBytes(CONTAINER, OBJECT, []byte(CONTENTS), "") | |
1078 | err := c.ObjectPutBytes(ctx, CONTAINER, OBJECT, []byte(CONTENTS), "") | |
1039 | 1079 | if err != nil { |
1040 | 1080 | t.Fatal(err) |
1041 | 1081 | } |
1042 | 1082 | defer func() { |
1043 | err = c.ObjectDelete(CONTAINER, OBJECT) | |
1083 | err = c.ObjectDelete(ctx, CONTAINER, OBJECT) | |
1044 | 1084 | if err != nil { |
1045 | 1085 | t.Error(err) |
1046 | 1086 | } |
1047 | 1087 | }() |
1048 | 1088 | |
1049 | info, _, err := c.Object(CONTAINER, OBJECT) | |
1089 | info, _, err := c.Object(ctx, CONTAINER, OBJECT) | |
1050 | 1090 | if err != nil { |
1051 | 1091 | t.Error(err) |
1052 | 1092 | } |
1062 | 1102 | } |
1063 | 1103 | |
1064 | 1104 | func TestObjectPutMimeType(t *testing.T) { |
1105 | ctx := context.Background() | |
1065 | 1106 | c, rollback := makeConnectionWithContainer(t) |
1066 | 1107 | defer rollback() |
1067 | err := c.ObjectPutString(CONTAINER, "test.jpg", CONTENTS, "") | |
1108 | err := c.ObjectPutString(ctx, CONTAINER, "test.jpg", CONTENTS, "") | |
1068 | 1109 | if err != nil { |
1069 | 1110 | t.Fatal(err) |
1070 | 1111 | } |
1071 | 1112 | defer func() { |
1072 | err = c.ObjectDelete(CONTAINER, "test.jpg") | |
1113 | err = c.ObjectDelete(ctx, CONTAINER, "test.jpg") | |
1073 | 1114 | if err != nil { |
1074 | 1115 | t.Error(err) |
1075 | 1116 | } |
1076 | 1117 | }() |
1077 | 1118 | |
1078 | info, _, err := c.Object(CONTAINER, "test.jpg") | |
1119 | info, _, err := c.Object(ctx, CONTAINER, "test.jpg") | |
1079 | 1120 | if err != nil { |
1080 | 1121 | t.Error(err) |
1081 | 1122 | } |
1085 | 1126 | } |
1086 | 1127 | |
1087 | 1128 | func TestObjectCreate(t *testing.T) { |
1129 | ctx := context.Background() | |
1088 | 1130 | c, rollback := makeConnectionWithContainer(t) |
1089 | 1131 | defer rollback() |
1090 | out, err := c.ObjectCreate(CONTAINER, OBJECT2, true, "", "", nil) | |
1132 | out, err := c.ObjectCreate(ctx, CONTAINER, OBJECT2, true, "", "", nil) | |
1091 | 1133 | if err != nil { |
1092 | 1134 | t.Fatal(err) |
1093 | 1135 | } |
1094 | 1136 | defer func() { |
1095 | err = c.ObjectDelete(CONTAINER, OBJECT2) | |
1137 | err = c.ObjectDelete(ctx, CONTAINER, OBJECT2) | |
1096 | 1138 | if err != nil { |
1097 | 1139 | t.Error(err) |
1098 | 1140 | } |
1101 | 1143 | hash := md5.New() |
1102 | 1144 | out2 := io.MultiWriter(out, buf, hash) |
1103 | 1145 | for i := 0; i < 100; i++ { |
1104 | fmt.Fprintf(out2, "%d %s\n", i, CONTENTS) | |
1146 | _, _ = fmt.Fprintf(out2, "%d %s\n", i, CONTENTS) | |
1105 | 1147 | } |
1106 | 1148 | // Ensure Headers fails if called prematurely |
1107 | 1149 | _, err = out.Headers() |
1113 | 1155 | t.Error(err) |
1114 | 1156 | } |
1115 | 1157 | expected := buf.String() |
1116 | contents, err := c.ObjectGetString(CONTAINER, OBJECT2) | |
1158 | contents, err := c.ObjectGetString(ctx, CONTAINER, OBJECT2) | |
1117 | 1159 | if err != nil { |
1118 | 1160 | t.Error(err) |
1119 | 1161 | } |
1137 | 1179 | } |
1138 | 1180 | |
1139 | 1181 | // Now with hash instead |
1140 | out, err = c.ObjectCreate(CONTAINER, OBJECT2, false, fmt.Sprintf("%x", hash.Sum(nil)), "", nil) | |
1182 | out, err = c.ObjectCreate(ctx, CONTAINER, OBJECT2, false, fmt.Sprintf("%x", hash.Sum(nil)), "", nil) | |
1141 | 1183 | if err != nil { |
1142 | 1184 | t.Fatal(err) |
1143 | 1185 | } |
1149 | 1191 | if err != nil { |
1150 | 1192 | t.Error(err) |
1151 | 1193 | } |
1152 | contents, err = c.ObjectGetString(CONTAINER, OBJECT2) | |
1194 | contents, err = c.ObjectGetString(ctx, CONTAINER, OBJECT2) | |
1153 | 1195 | if err != nil { |
1154 | 1196 | t.Error(err) |
1155 | 1197 | } |
1158 | 1200 | } |
1159 | 1201 | |
1160 | 1202 | // Now with bad hash |
1161 | out, err = c.ObjectCreate(CONTAINER, OBJECT2, false, CONTENT_MD5, "", nil) | |
1203 | out, err = c.ObjectCreate(ctx, CONTAINER, OBJECT2, false, CONTENT_MD5, "", nil) | |
1162 | 1204 | if err != nil { |
1163 | 1205 | t.Fatal(err) |
1164 | 1206 | } |
1165 | 1207 | // FIXME: work around bug which produces 503 not 422 for empty corrupted files |
1166 | fmt.Fprintf(out, "Sausage") | |
1208 | _, _ = fmt.Fprintf(out, "Sausage") | |
1167 | 1209 | err = out.Close() |
1168 | 1210 | if err != swift.ObjectCorrupted { |
1169 | 1211 | t.Error("Expecting object corrupted not", err) |
1171 | 1213 | } |
1172 | 1214 | |
1173 | 1215 | func TestObjectCreateAbort(t *testing.T) { |
1216 | ctx := context.Background() | |
1174 | 1217 | c, rollback := makeConnectionWithContainer(t) |
1175 | 1218 | defer rollback() |
1176 | 1219 | |
1177 | out, err := c.ObjectCreate(CONTAINER, OBJECT2, true, "", "", nil) | |
1220 | out, err := c.ObjectCreate(ctx, CONTAINER, OBJECT2, true, "", "", nil) | |
1178 | 1221 | if err != nil { |
1179 | 1222 | t.Fatal(err) |
1180 | 1223 | } |
1181 | 1224 | defer func() { |
1182 | _ = c.ObjectDelete(CONTAINER, OBJECT2) // Ignore error | |
1225 | _ = c.ObjectDelete(ctx, CONTAINER, OBJECT2) // Ignore error | |
1183 | 1226 | }() |
1184 | 1227 | |
1185 | 1228 | expectedContents := "foo" |
1194 | 1237 | t.Errorf("Unexpected error %#v", err) |
1195 | 1238 | } |
1196 | 1239 | |
1197 | _, err = c.ObjectGetString(CONTAINER, OBJECT2) | |
1240 | _, err = c.ObjectGetString(ctx, CONTAINER, OBJECT2) | |
1198 | 1241 | if err != swift.ObjectNotFound { |
1199 | 1242 | t.Errorf("Unexpected error: %#v", err) |
1200 | 1243 | } |
1201 | 1244 | } |
1202 | 1245 | |
1203 | 1246 | func TestObjectGetString(t *testing.T) { |
1247 | ctx := context.Background() | |
1204 | 1248 | c, rollback := makeConnectionWithObject(t) |
1205 | 1249 | defer rollback() |
1206 | contents, err := c.ObjectGetString(CONTAINER, OBJECT) | |
1250 | contents, err := c.ObjectGetString(ctx, CONTAINER, OBJECT) | |
1207 | 1251 | if err != nil { |
1208 | 1252 | t.Fatal(err) |
1209 | 1253 | } |
1213 | 1257 | } |
1214 | 1258 | |
1215 | 1259 | func TestObjectGetBytes(t *testing.T) { |
1260 | ctx := context.Background() | |
1216 | 1261 | c, rollback := makeConnectionWithObject(t) |
1217 | 1262 | defer rollback() |
1218 | contents, err := c.ObjectGetBytes(CONTAINER, OBJECT) | |
1263 | contents, err := c.ObjectGetBytes(ctx, CONTAINER, OBJECT) | |
1219 | 1264 | if err != nil { |
1220 | 1265 | t.Fatal(err) |
1221 | 1266 | } |
1225 | 1270 | } |
1226 | 1271 | |
1227 | 1272 | func TestObjectOpen(t *testing.T) { |
1273 | ctx := context.Background() | |
1228 | 1274 | c, rollback := makeConnectionWithObject(t) |
1229 | 1275 | defer rollback() |
1230 | file, _, err := c.ObjectOpen(CONTAINER, OBJECT, true, nil) | |
1276 | file, _, err := c.ObjectOpen(ctx, CONTAINER, OBJECT, true, nil) | |
1231 | 1277 | if err != nil { |
1232 | 1278 | t.Fatal(err) |
1233 | 1279 | } |
1249 | 1295 | } |
1250 | 1296 | |
1251 | 1297 | func TestObjectOpenPartial(t *testing.T) { |
1298 | ctx := context.Background() | |
1252 | 1299 | c, rollback := makeConnectionWithObject(t) |
1253 | 1300 | defer rollback() |
1254 | file, _, err := c.ObjectOpen(CONTAINER, OBJECT, true, nil) | |
1301 | file, _, err := c.ObjectOpen(ctx, CONTAINER, OBJECT, true, nil) | |
1255 | 1302 | if err != nil { |
1256 | 1303 | t.Fatal(err) |
1257 | 1304 | } |
1273 | 1320 | } |
1274 | 1321 | |
1275 | 1322 | func TestObjectOpenLength(t *testing.T) { |
1323 | ctx := context.Background() | |
1276 | 1324 | c, rollback := makeConnectionWithObject(t) |
1277 | 1325 | defer rollback() |
1278 | file, _, err := c.ObjectOpen(CONTAINER, OBJECT, true, nil) | |
1326 | file, _, err := c.ObjectOpen(ctx, CONTAINER, OBJECT, true, nil) | |
1279 | 1327 | if err != nil { |
1280 | 1328 | t.Fatal(err) |
1281 | 1329 | } |
1282 | 1330 | // FIXME ideally this would check both branches of the Length() code |
1283 | n, err := file.Length() | |
1331 | n, err := file.Length(ctx) | |
1284 | 1332 | if err != nil { |
1285 | 1333 | t.Fatal(err) |
1286 | 1334 | } |
1294 | 1342 | } |
1295 | 1343 | |
1296 | 1344 | func TestObjectOpenNotModified(t *testing.T) { |
1345 | ctx := context.Background() | |
1297 | 1346 | c, rollback := makeConnectionWithObject(t) |
1298 | 1347 | defer rollback() |
1299 | _, _, err := c.ObjectOpen(CONTAINER, OBJECT, true, swift.Headers{ | |
1348 | _, _, err := c.ObjectOpen(ctx, CONTAINER, OBJECT, true, swift.Headers{ | |
1300 | 1349 | "If-None-Match": CONTENT_MD5, |
1301 | 1350 | }) |
1302 | 1351 | if err != swift.NotModified { |
1305 | 1354 | } |
1306 | 1355 | |
1307 | 1356 | func TestObjectOpenSeek(t *testing.T) { |
1357 | ctx := context.Background() | |
1308 | 1358 | c, rollback := makeConnectionWithObject(t) |
1309 | 1359 | defer rollback() |
1310 | 1360 | |
1330 | 1380 | {2, -4, 1}, |
1331 | 1381 | } |
1332 | 1382 | |
1333 | file, _, err := c.ObjectOpen(CONTAINER, OBJECT, true, nil) | |
1383 | file, _, err := c.ObjectOpen(ctx, CONTAINER, OBJECT, true, nil) | |
1334 | 1384 | if err != nil { |
1335 | 1385 | t.Fatal(err) |
1336 | 1386 | } |
1338 | 1388 | for _, p := range plan { |
1339 | 1389 | if p.whence >= 0 { |
1340 | 1390 | var result int64 |
1341 | result, err = file.Seek(p.offset, p.whence) | |
1391 | result, err = file.Seek(ctx, p.offset, p.whence) | |
1342 | 1392 | if err != nil { |
1343 | 1393 | t.Fatal(err, p) |
1344 | 1394 | } |
1371 | 1421 | |
1372 | 1422 | // Test seeking to the end to find the file size |
1373 | 1423 | func TestObjectOpenSeekEnd(t *testing.T) { |
1424 | ctx := context.Background() | |
1374 | 1425 | c, rollback := makeConnectionWithObject(t) |
1375 | 1426 | defer rollback() |
1376 | file, _, err := c.ObjectOpen(CONTAINER, OBJECT, true, nil) | |
1377 | if err != nil { | |
1378 | t.Fatal(err) | |
1379 | } | |
1380 | n, err := file.Seek(0, 2) // seek to end | |
1427 | file, _, err := c.ObjectOpen(ctx, CONTAINER, OBJECT, true, nil) | |
1428 | if err != nil { | |
1429 | t.Fatal(err) | |
1430 | } | |
1431 | n, err := file.Seek(ctx, 0, 2) // seek to end | |
1381 | 1432 | if err != nil { |
1382 | 1433 | t.Fatal(err) |
1383 | 1434 | } |
1396 | 1447 | } |
1397 | 1448 | |
1398 | 1449 | // Now seek back to start and check we can read the file |
1399 | n, err = file.Seek(0, 0) // seek to start | |
1450 | n, err = file.Seek(ctx, 0, 0) // seek to start | |
1400 | 1451 | if err != nil { |
1401 | 1452 | t.Fatal(err) |
1402 | 1453 | } |
1415 | 1466 | } |
1416 | 1467 | |
1417 | 1468 | func TestObjectUpdate(t *testing.T) { |
1469 | ctx := context.Background() | |
1418 | 1470 | c, rollback := makeConnectionWithObject(t) |
1419 | 1471 | defer rollback() |
1420 | err := c.ObjectUpdate(CONTAINER, OBJECT, m1.ObjectHeaders()) | |
1472 | err := c.ObjectUpdate(ctx, CONTAINER, OBJECT, m1.ObjectHeaders()) | |
1421 | 1473 | if err != nil { |
1422 | 1474 | t.Fatal(err) |
1423 | 1475 | } |
1431 | 1483 | } |
1432 | 1484 | |
1433 | 1485 | func TestObject(t *testing.T) { |
1486 | ctx := context.Background() | |
1434 | 1487 | c, rollback := makeConnectionWithObjectHeaders(t) |
1435 | 1488 | defer rollback() |
1436 | object, headers, err := c.Object(CONTAINER, OBJECT) | |
1489 | object, headers, err := c.Object(ctx, CONTAINER, OBJECT) | |
1437 | 1490 | if err != nil { |
1438 | 1491 | t.Fatal(err) |
1439 | 1492 | } |
1445 | 1498 | } |
1446 | 1499 | |
1447 | 1500 | func TestObjectUpdate2(t *testing.T) { |
1501 | ctx := context.Background() | |
1448 | 1502 | c, rollback := makeConnectionWithObjectHeaders(t) |
1449 | 1503 | defer rollback() |
1450 | err := c.ObjectUpdate(CONTAINER, OBJECT, m2.ObjectHeaders()) | |
1451 | if err != nil { | |
1452 | t.Fatal(err) | |
1453 | } | |
1454 | _, headers, err := c.Object(CONTAINER, OBJECT) | |
1504 | err := c.ObjectUpdate(ctx, CONTAINER, OBJECT, m2.ObjectHeaders()) | |
1505 | if err != nil { | |
1506 | t.Fatal(err) | |
1507 | } | |
1508 | _, headers, err := c.Object(ctx, CONTAINER, OBJECT) | |
1455 | 1509 | if err != nil { |
1456 | 1510 | t.Fatal(err) |
1457 | 1511 | } |
1459 | 1513 | } |
1460 | 1514 | |
1461 | 1515 | func TestContainers(t *testing.T) { |
1516 | ctx := context.Background() | |
1462 | 1517 | c, rollback := makeConnectionWithObjectHeaders(t) |
1463 | 1518 | defer rollback() |
1464 | containers, err := c.Containers(nil) | |
1519 | containers, err := c.Containers(ctx, nil) | |
1465 | 1520 | if err != nil { |
1466 | 1521 | t.Fatal(err) |
1467 | 1522 | } |
1487 | 1542 | } |
1488 | 1543 | |
1489 | 1544 | func TestObjectNames(t *testing.T) { |
1545 | ctx := context.Background() | |
1490 | 1546 | c, rollback := makeConnectionWithObjectHeaders(t) |
1491 | 1547 | defer rollback() |
1492 | objects, err := c.ObjectNames(CONTAINER, nil) | |
1548 | objects, err := c.ObjectNames(ctx, CONTAINER, nil) | |
1493 | 1549 | if err != nil { |
1494 | 1550 | t.Fatal(err) |
1495 | 1551 | } |
1499 | 1555 | } |
1500 | 1556 | |
1501 | 1557 | func TestObjectNamesAll(t *testing.T) { |
1558 | ctx := context.Background() | |
1502 | 1559 | c, rollback := makeConnectionWithObjectHeaders(t) |
1503 | 1560 | defer rollback() |
1504 | objects, err := c.ObjectNamesAll(CONTAINER, nil) | |
1561 | objects, err := c.ObjectNamesAll(ctx, CONTAINER, nil) | |
1505 | 1562 | if err != nil { |
1506 | 1563 | t.Fatal(err) |
1507 | 1564 | } |
1511 | 1568 | } |
1512 | 1569 | |
1513 | 1570 | func TestObjectNamesAllWithLimit(t *testing.T) { |
1571 | ctx := context.Background() | |
1514 | 1572 | c, rollback := makeConnectionWithObjectHeaders(t) |
1515 | 1573 | defer rollback() |
1516 | objects, err := c.ObjectNamesAll(CONTAINER, &swift.ObjectsOpts{Limit: 1}) | |
1574 | objects, err := c.ObjectNamesAll(ctx, CONTAINER, &swift.ObjectsOpts{Limit: 1}) | |
1517 | 1575 | if err != nil { |
1518 | 1576 | t.Fatal(err) |
1519 | 1577 | } |
1523 | 1581 | } |
1524 | 1582 | |
1525 | 1583 | func TestObjectsWalk(t *testing.T) { |
1584 | ctx := context.Background() | |
1526 | 1585 | c, rollback := makeConnectionWithObjectHeaders(t) |
1527 | 1586 | defer rollback() |
1528 | 1587 | objects := make([]string, 0) |
1529 | err := c.ObjectsWalk(container, nil, func(opts *swift.ObjectsOpts) (interface{}, error) { | |
1530 | newObjects, err := c.ObjectNames(CONTAINER, opts) | |
1588 | err := c.ObjectsWalk(ctx, container, nil, func(ctx context.Context, opts *swift.ObjectsOpts) (interface{}, error) { | |
1589 | newObjects, err := c.ObjectNames(ctx, CONTAINER, opts) | |
1531 | 1590 | if err == nil { |
1532 | 1591 | objects = append(objects, newObjects...) |
1533 | 1592 | } |
1542 | 1601 | } |
1543 | 1602 | |
1544 | 1603 | func TestObjects(t *testing.T) { |
1604 | ctx := context.Background() | |
1545 | 1605 | c, rollback := makeConnectionWithObjectHeaders(t) |
1546 | 1606 | defer rollback() |
1547 | objects, err := c.Objects(CONTAINER, &swift.ObjectsOpts{Delimiter: '/'}) | |
1607 | objects, err := c.Objects(ctx, CONTAINER, &swift.ObjectsOpts{Delimiter: '/'}) | |
1548 | 1608 | if err != nil { |
1549 | 1609 | t.Fatal(err) |
1550 | 1610 | } |
1559 | 1619 | } |
1560 | 1620 | |
1561 | 1621 | func TestObjectsDirectory(t *testing.T) { |
1622 | ctx := context.Background() | |
1562 | 1623 | c, rollback := makeConnectionWithObjectHeaders(t) |
1563 | 1624 | defer rollback() |
1564 | err := c.ObjectPutString(CONTAINER, "directory", "", "application/directory") | |
1565 | if err != nil { | |
1566 | t.Fatal(err) | |
1567 | } | |
1568 | defer c.ObjectDelete(CONTAINER, "directory") | |
1625 | err := c.ObjectPutString(ctx, CONTAINER, "directory", "", "application/directory") | |
1626 | if err != nil { | |
1627 | t.Fatal(err) | |
1628 | } | |
1629 | defer c.ObjectDelete(ctx, CONTAINER, "directory") | |
1569 | 1630 | |
1570 | 1631 | // Look for the directory object and check we aren't confusing |
1571 | 1632 | // it with a pseudo directory object |
1572 | objects, err := c.Objects(CONTAINER, &swift.ObjectsOpts{Delimiter: '/'}) | |
1633 | objects, err := c.Objects(ctx, CONTAINER, &swift.ObjectsOpts{Delimiter: '/'}) | |
1573 | 1634 | if err != nil { |
1574 | 1635 | t.Fatal(err) |
1575 | 1636 | } |
1593 | 1654 | } |
1594 | 1655 | |
1595 | 1656 | func TestObjectsPseudoDirectory(t *testing.T) { |
1657 | ctx := context.Background() | |
1596 | 1658 | c, rollback := makeConnectionWithObjectHeaders(t) |
1597 | 1659 | defer rollback() |
1598 | err := c.ObjectPutString(CONTAINER, "directory/puppy.jpg", "cute puppy", "") | |
1599 | if err != nil { | |
1600 | t.Fatal(err) | |
1601 | } | |
1602 | defer c.ObjectDelete(CONTAINER, "directory/puppy.jpg") | |
1660 | err := c.ObjectPutString(ctx, CONTAINER, "directory/puppy.jpg", "cute puppy", "") | |
1661 | if err != nil { | |
1662 | t.Fatal(err) | |
1663 | } | |
1664 | defer c.ObjectDelete(ctx, CONTAINER, "directory/puppy.jpg") | |
1603 | 1665 | |
1604 | 1666 | // Look for the pseudo directory |
1605 | objects, err := c.Objects(CONTAINER, &swift.ObjectsOpts{Delimiter: '/'}) | |
1667 | objects, err := c.Objects(ctx, CONTAINER, &swift.ObjectsOpts{Delimiter: '/'}) | |
1606 | 1668 | if err != nil { |
1607 | 1669 | t.Fatal(err) |
1608 | 1670 | } |
1624 | 1686 | } |
1625 | 1687 | |
1626 | 1688 | // Look in the pseudo directory now |
1627 | objects, err = c.Objects(CONTAINER, &swift.ObjectsOpts{Delimiter: '/', Path: "directory/"}) | |
1689 | objects, err = c.Objects(ctx, CONTAINER, &swift.ObjectsOpts{Delimiter: '/', Path: "directory/"}) | |
1628 | 1690 | if err != nil { |
1629 | 1691 | t.Fatal(err) |
1630 | 1692 | } |
1639 | 1701 | } |
1640 | 1702 | |
1641 | 1703 | func TestObjectsAll(t *testing.T) { |
1704 | ctx := context.Background() | |
1642 | 1705 | c, rollback := makeConnectionWithObjectHeaders(t) |
1643 | 1706 | defer rollback() |
1644 | objects, err := c.ObjectsAll(CONTAINER, nil) | |
1707 | objects, err := c.ObjectsAll(ctx, CONTAINER, nil) | |
1645 | 1708 | if err != nil { |
1646 | 1709 | t.Fatal(err) |
1647 | 1710 | } |
1651 | 1714 | } |
1652 | 1715 | |
1653 | 1716 | func TestObjectsAllWithLimit(t *testing.T) { |
1717 | ctx := context.Background() | |
1654 | 1718 | c, rollback := makeConnectionWithObjectHeaders(t) |
1655 | 1719 | defer rollback() |
1656 | objects, err := c.ObjectsAll(CONTAINER, &swift.ObjectsOpts{Limit: 1}) | |
1720 | objects, err := c.ObjectsAll(ctx, CONTAINER, &swift.ObjectsOpts{Limit: 1}) | |
1657 | 1721 | if err != nil { |
1658 | 1722 | t.Fatal(err) |
1659 | 1723 | } |
1663 | 1727 | } |
1664 | 1728 | |
1665 | 1729 | func TestObjectNamesWithPath(t *testing.T) { |
1730 | ctx := context.Background() | |
1666 | 1731 | c, rollback := makeConnectionWithObjectHeaders(t) |
1667 | 1732 | defer rollback() |
1668 | objects, err := c.ObjectNames(CONTAINER, &swift.ObjectsOpts{Delimiter: '/', Path: ""}) | |
1733 | objects, err := c.ObjectNames(ctx, CONTAINER, &swift.ObjectsOpts{Delimiter: '/', Path: ""}) | |
1669 | 1734 | if err != nil { |
1670 | 1735 | t.Fatal(err) |
1671 | 1736 | } |
1673 | 1738 | t.Error("Bad listing with path", objects) |
1674 | 1739 | } |
1675 | 1740 | // fmt.Println(objects) |
1676 | objects, err = c.ObjectNames(CONTAINER, &swift.ObjectsOpts{Delimiter: '/', Path: "Downloads/"}) | |
1741 | objects, err = c.ObjectNames(ctx, CONTAINER, &swift.ObjectsOpts{Delimiter: '/', Path: "Downloads/"}) | |
1677 | 1742 | if err != nil { |
1678 | 1743 | t.Fatal(err) |
1679 | 1744 | } |
1683 | 1748 | } |
1684 | 1749 | |
1685 | 1750 | func TestObjectCopy(t *testing.T) { |
1751 | ctx := context.Background() | |
1686 | 1752 | c, rollback := makeConnectionWithObjectHeaders(t) |
1687 | 1753 | defer rollback() |
1688 | _, err := c.ObjectCopy(CONTAINER, OBJECT, CONTAINER, OBJECT2, nil) | |
1689 | if err != nil { | |
1690 | t.Fatal(err) | |
1691 | } | |
1692 | err = c.ObjectDelete(CONTAINER, OBJECT2) | |
1754 | _, err := c.ObjectCopy(ctx, CONTAINER, OBJECT, CONTAINER, OBJECT2, nil) | |
1755 | if err != nil { | |
1756 | t.Fatal(err) | |
1757 | } | |
1758 | err = c.ObjectDelete(ctx, CONTAINER, OBJECT2) | |
1693 | 1759 | if err != nil { |
1694 | 1760 | t.Fatal(err) |
1695 | 1761 | } |
1696 | 1762 | } |
1697 | 1763 | |
1698 | 1764 | func TestObjectCopyDifficultName(t *testing.T) { |
1765 | ctx := context.Background() | |
1699 | 1766 | c, rollback := makeConnectionWithObjectHeaders(t) |
1700 | 1767 | defer rollback() |
1701 | 1768 | const dest = OBJECT + "?param %30%31%32 £100" |
1702 | _, err := c.ObjectCopy(CONTAINER, OBJECT, CONTAINER, dest, nil) | |
1703 | if err != nil { | |
1704 | t.Fatal(err) | |
1705 | } | |
1706 | err = c.ObjectDelete(CONTAINER, dest) | |
1769 | _, err := c.ObjectCopy(ctx, CONTAINER, OBJECT, CONTAINER, dest, nil) | |
1770 | if err != nil { | |
1771 | t.Fatal(err) | |
1772 | } | |
1773 | err = c.ObjectDelete(ctx, CONTAINER, dest) | |
1707 | 1774 | if err != nil { |
1708 | 1775 | t.Fatal(err) |
1709 | 1776 | } |
1710 | 1777 | } |
1711 | 1778 | |
1712 | 1779 | func TestObjectCopyWithMetadata(t *testing.T) { |
1780 | ctx := context.Background() | |
1713 | 1781 | c, rollback := makeConnectionWithObjectHeaders(t) |
1714 | 1782 | defer rollback() |
1715 | 1783 | m := swift.Metadata{} |
1717 | 1785 | m["hello"] = "9" |
1718 | 1786 | h := m.ObjectHeaders() |
1719 | 1787 | h["Content-Type"] = "image/jpeg" |
1720 | _, err := c.ObjectCopy(CONTAINER, OBJECT, CONTAINER, OBJECT2, h) | |
1788 | _, err := c.ObjectCopy(ctx, CONTAINER, OBJECT, CONTAINER, OBJECT2, h) | |
1721 | 1789 | if err != nil { |
1722 | 1790 | t.Fatal(err) |
1723 | 1791 | } |
1724 | 1792 | defer func() { |
1725 | err = c.ObjectDelete(CONTAINER, OBJECT2) | |
1793 | err = c.ObjectDelete(ctx, CONTAINER, OBJECT2) | |
1726 | 1794 | if err != nil { |
1727 | 1795 | t.Fatal(err) |
1728 | 1796 | } |
1729 | 1797 | }() |
1730 | 1798 | // Re-read the metadata to see if it is correct |
1731 | _, headers, err := c.Object(CONTAINER, OBJECT2) | |
1799 | _, headers, err := c.Object(ctx, CONTAINER, OBJECT2) | |
1732 | 1800 | if err != nil { |
1733 | 1801 | t.Fatal(err) |
1734 | 1802 | } |
1739 | 1807 | } |
1740 | 1808 | |
1741 | 1809 | func TestObjectMove(t *testing.T) { |
1810 | ctx := context.Background() | |
1742 | 1811 | c, rollback := makeConnectionWithObjectHeaders(t) |
1743 | 1812 | defer rollback() |
1744 | err := c.ObjectMove(CONTAINER, OBJECT, CONTAINER, OBJECT2) | |
1813 | err := c.ObjectMove(ctx, CONTAINER, OBJECT, CONTAINER, OBJECT2) | |
1745 | 1814 | if err != nil { |
1746 | 1815 | t.Fatal(err) |
1747 | 1816 | } |
1748 | 1817 | testExistenceAfterDelete(t, c, CONTAINER, OBJECT) |
1749 | _, _, err = c.Object(CONTAINER, OBJECT2) | |
1750 | if err != nil { | |
1751 | t.Fatal(err) | |
1752 | } | |
1753 | ||
1754 | err = c.ObjectMove(CONTAINER, OBJECT2, CONTAINER, OBJECT) | |
1818 | _, _, err = c.Object(ctx, CONTAINER, OBJECT2) | |
1819 | if err != nil { | |
1820 | t.Fatal(err) | |
1821 | } | |
1822 | ||
1823 | err = c.ObjectMove(ctx, CONTAINER, OBJECT2, CONTAINER, OBJECT) | |
1755 | 1824 | if err != nil { |
1756 | 1825 | t.Fatal(err) |
1757 | 1826 | } |
1758 | 1827 | testExistenceAfterDelete(t, c, CONTAINER, OBJECT2) |
1759 | _, headers, err := c.Object(CONTAINER, OBJECT) | |
1828 | _, headers, err := c.Object(ctx, CONTAINER, OBJECT) | |
1760 | 1829 | if err != nil { |
1761 | 1830 | t.Fatal(err) |
1762 | 1831 | } |
1764 | 1833 | } |
1765 | 1834 | |
1766 | 1835 | func TestObjectUpdateContentType(t *testing.T) { |
1836 | ctx := context.Background() | |
1767 | 1837 | c, rollback := makeConnectionWithObjectHeaders(t) |
1768 | 1838 | defer rollback() |
1769 | err := c.ObjectUpdateContentType(CONTAINER, OBJECT, "text/potato") | |
1839 | err := c.ObjectUpdateContentType(ctx, CONTAINER, OBJECT, "text/potato") | |
1770 | 1840 | if err != nil { |
1771 | 1841 | t.Fatal(err) |
1772 | 1842 | } |
1773 | 1843 | // Re-read the metadata to see if it is correct |
1774 | _, headers, err := c.Object(CONTAINER, OBJECT) | |
1844 | _, headers, err := c.Object(ctx, CONTAINER, OBJECT) | |
1775 | 1845 | if err != nil { |
1776 | 1846 | t.Fatal(err) |
1777 | 1847 | } |
1782 | 1852 | } |
1783 | 1853 | |
1784 | 1854 | func TestVersionContainerCreate(t *testing.T) { |
1855 | ctx := context.Background() | |
1785 | 1856 | c, rollback := makeConnectionAuth(t) |
1786 | 1857 | defer rollback() |
1787 | err := c.VersionContainerCreate(CURRENT_CONTAINER, VERSIONS_CONTAINER) | |
1858 | err := c.VersionContainerCreate(ctx, CURRENT_CONTAINER, VERSIONS_CONTAINER) | |
1788 | 1859 | defer func() { |
1789 | c.ContainerDelete(CURRENT_CONTAINER) | |
1790 | c.ContainerDelete(VERSIONS_CONTAINER) | |
1860 | _ = c.ContainerDelete(ctx, CURRENT_CONTAINER) | |
1861 | _ = c.ContainerDelete(ctx, VERSIONS_CONTAINER) | |
1791 | 1862 | }() |
1792 | 1863 | if err != nil { |
1793 | 1864 | if err == swift.Forbidden { |
1799 | 1870 | } |
1800 | 1871 | |
1801 | 1872 | func TestVersionObjectAdd(t *testing.T) { |
1873 | ctx := context.Background() | |
1802 | 1874 | c, rollback := makeConnectionWithVersionsContainer(t) |
1803 | 1875 | defer rollback() |
1804 | 1876 | if skipVersionTests { |
1806 | 1878 | return |
1807 | 1879 | } |
1808 | 1880 | // Version 1 |
1809 | if err := c.ObjectPutString(CURRENT_CONTAINER, OBJECT, CONTENTS, ""); err != nil { | |
1881 | if err := c.ObjectPutString(ctx, CURRENT_CONTAINER, OBJECT, CONTENTS, ""); err != nil { | |
1810 | 1882 | t.Fatal(err) |
1811 | 1883 | } |
1812 | 1884 | defer func() { |
1813 | err := c.ObjectDelete(CURRENT_CONTAINER, OBJECT) | |
1885 | err := c.ObjectDelete(ctx, CURRENT_CONTAINER, OBJECT) | |
1814 | 1886 | if err != nil { |
1815 | 1887 | t.Fatal(err) |
1816 | 1888 | } |
1817 | 1889 | }() |
1818 | if contents, err := c.ObjectGetString(CURRENT_CONTAINER, OBJECT); err != nil { | |
1890 | if contents, err := c.ObjectGetString(ctx, CURRENT_CONTAINER, OBJECT); err != nil { | |
1819 | 1891 | t.Fatal(err) |
1820 | 1892 | } else if contents != CONTENTS { |
1821 | 1893 | t.Error("Contents wrong") |
1822 | 1894 | } |
1823 | 1895 | |
1824 | 1896 | // Version 2 |
1825 | if err := c.ObjectPutString(CURRENT_CONTAINER, OBJECT, CONTENTS2, ""); err != nil { | |
1897 | if err := c.ObjectPutString(ctx, CURRENT_CONTAINER, OBJECT, CONTENTS2, ""); err != nil { | |
1826 | 1898 | t.Fatal(err) |
1827 | 1899 | } |
1828 | 1900 | defer func() { |
1829 | err := c.ObjectDelete(CURRENT_CONTAINER, OBJECT) | |
1901 | err := c.ObjectDelete(ctx, CURRENT_CONTAINER, OBJECT) | |
1830 | 1902 | if err != nil { |
1831 | 1903 | t.Fatal(err) |
1832 | 1904 | } |
1833 | 1905 | }() |
1834 | if contents, err := c.ObjectGetString(CURRENT_CONTAINER, OBJECT); err != nil { | |
1906 | if contents, err := c.ObjectGetString(ctx, CURRENT_CONTAINER, OBJECT); err != nil { | |
1835 | 1907 | t.Fatal(err) |
1836 | 1908 | } else if contents != CONTENTS2 { |
1837 | 1909 | t.Error("Contents wrong") |
1838 | 1910 | } |
1839 | 1911 | |
1840 | 1912 | // Version 3 |
1841 | if err := c.ObjectPutString(CURRENT_CONTAINER, OBJECT, CONTENTS2, ""); err != nil { | |
1913 | if err := c.ObjectPutString(ctx, CURRENT_CONTAINER, OBJECT, CONTENTS2, ""); err != nil { | |
1842 | 1914 | t.Fatal(err) |
1843 | 1915 | } |
1844 | 1916 | defer func() { |
1845 | err := c.ObjectDelete(CURRENT_CONTAINER, OBJECT) | |
1917 | err := c.ObjectDelete(ctx, CURRENT_CONTAINER, OBJECT) | |
1846 | 1918 | if err != nil { |
1847 | 1919 | t.Fatal(err) |
1848 | 1920 | } |
1850 | 1922 | } |
1851 | 1923 | |
1852 | 1924 | func TestVersionObjectList(t *testing.T) { |
1925 | ctx := context.Background() | |
1853 | 1926 | c, rollback := makeConnectionWithVersionsObject(t) |
1854 | 1927 | defer rollback() |
1855 | 1928 | if skipVersionTests { |
1856 | 1929 | t.Log("Server doesn't support Versions - skipping test") |
1857 | 1930 | return |
1858 | 1931 | } |
1859 | list, err := c.VersionObjectList(VERSIONS_CONTAINER, OBJECT) | |
1932 | list, err := c.VersionObjectList(ctx, VERSIONS_CONTAINER, OBJECT) | |
1860 | 1933 | if err != nil { |
1861 | 1934 | t.Fatal(err) |
1862 | 1935 | } |
1867 | 1940 | } |
1868 | 1941 | |
1869 | 1942 | func TestVersionObjectDelete(t *testing.T) { |
1943 | ctx := context.Background() | |
1870 | 1944 | c, rollback := makeConnectionWithVersionsObject(t) |
1871 | 1945 | defer rollback() |
1872 | 1946 | if skipVersionTests { |
1874 | 1948 | return |
1875 | 1949 | } |
1876 | 1950 | // Delete Version 3 |
1877 | if err := c.ObjectDelete(CURRENT_CONTAINER, OBJECT); err != nil { | |
1951 | if err := c.ObjectDelete(ctx, CURRENT_CONTAINER, OBJECT); err != nil { | |
1878 | 1952 | t.Fatal(err) |
1879 | 1953 | } |
1880 | 1954 | |
1881 | 1955 | // Delete Version 2 |
1882 | if err := c.ObjectDelete(CURRENT_CONTAINER, OBJECT); err != nil { | |
1956 | if err := c.ObjectDelete(ctx, CURRENT_CONTAINER, OBJECT); err != nil { | |
1883 | 1957 | t.Fatal(err) |
1884 | 1958 | } |
1885 | 1959 | |
1886 | 1960 | // Contents should be reverted to Version 1 |
1887 | if contents, err := c.ObjectGetString(CURRENT_CONTAINER, OBJECT); err != nil { | |
1961 | if contents, err := c.ObjectGetString(ctx, CURRENT_CONTAINER, OBJECT); err != nil { | |
1888 | 1962 | t.Fatal(err) |
1889 | 1963 | } else if contents != CONTENTS { |
1890 | 1964 | t.Error("Contents wrong") |
1892 | 1966 | } |
1893 | 1967 | |
1894 | 1968 | func TestVersionDeleteContent(t *testing.T) { |
1969 | ctx := context.Background() | |
1895 | 1970 | c, rollback := makeConnectionWithVersionsObject(t) |
1896 | 1971 | defer rollback() |
1897 | 1972 | if skipVersionTests { |
1899 | 1974 | return |
1900 | 1975 | } |
1901 | 1976 | // Delete Version 3 |
1902 | if err := c.ObjectDelete(CURRENT_CONTAINER, OBJECT); err != nil { | |
1977 | if err := c.ObjectDelete(ctx, CURRENT_CONTAINER, OBJECT); err != nil { | |
1903 | 1978 | t.Fatal(err) |
1904 | 1979 | } |
1905 | 1980 | // Delete Version 2 |
1906 | if err := c.ObjectDelete(CURRENT_CONTAINER, OBJECT); err != nil { | |
1981 | if err := c.ObjectDelete(ctx, CURRENT_CONTAINER, OBJECT); err != nil { | |
1907 | 1982 | t.Fatal(err) |
1908 | 1983 | } |
1909 | 1984 | // Delete Version 1 |
1910 | if err := c.ObjectDelete(CURRENT_CONTAINER, OBJECT); err != nil { | |
1911 | t.Fatal(err) | |
1912 | } | |
1913 | if err := c.ObjectDelete(CURRENT_CONTAINER, OBJECT); err != swift.ObjectNotFound { | |
1985 | if err := c.ObjectDelete(ctx, CURRENT_CONTAINER, OBJECT); err != nil { | |
1986 | t.Fatal(err) | |
1987 | } | |
1988 | if err := c.ObjectDelete(ctx, CURRENT_CONTAINER, OBJECT); err != swift.ObjectNotFound { | |
1914 | 1989 | t.Fatalf("Expecting Object not found error, got: %v", err) |
1915 | 1990 | } |
1916 | 1991 | } |
1918 | 1993 | // Check for non existence after delete |
1919 | 1994 | // May have to do it a few times to wait for swift to be consistent. |
1920 | 1995 | func testExistenceAfterDelete(t *testing.T, c *swift.Connection, container, object string) { |
1996 | ctx := context.Background() | |
1921 | 1997 | for i := 10; i <= 0; i-- { |
1922 | _, _, err := c.Object(container, object) | |
1998 | _, _, err := c.Object(ctx, container, object) | |
1923 | 1999 | if err == swift.ObjectNotFound { |
1924 | 2000 | break |
1925 | 2001 | } |
1931 | 2007 | } |
1932 | 2008 | |
1933 | 2009 | func TestObjectDelete(t *testing.T) { |
2010 | ctx := context.Background() | |
1934 | 2011 | c, rollback := makeConnectionWithObject(t) |
1935 | 2012 | defer rollback() |
1936 | err := c.ObjectDelete(CONTAINER, OBJECT) | |
2013 | err := c.ObjectDelete(ctx, CONTAINER, OBJECT) | |
1937 | 2014 | if err != nil { |
1938 | 2015 | t.Fatal(err) |
1939 | 2016 | } |
1940 | 2017 | testExistenceAfterDelete(t, c, CONTAINER, OBJECT) |
1941 | err = c.ObjectDelete(CONTAINER, OBJECT) | |
2018 | err = c.ObjectDelete(ctx, CONTAINER, OBJECT) | |
1942 | 2019 | if err != swift.ObjectNotFound { |
1943 | 2020 | t.Fatal("Expecting Object not found", err) |
1944 | 2021 | } |
1945 | 2022 | } |
1946 | 2023 | |
1947 | 2024 | func TestBulkDelete(t *testing.T) { |
2025 | ctx := context.Background() | |
1948 | 2026 | c, rollback := makeConnectionWithContainer(t) |
1949 | 2027 | defer rollback() |
1950 | result, err := c.BulkDelete(CONTAINER, []string{OBJECT}) | |
2028 | result, err := c.BulkDelete(ctx, CONTAINER, []string{OBJECT}) | |
1951 | 2029 | if err == swift.Forbidden { |
1952 | 2030 | t.Log("Server doesn't support BulkDelete - skipping test") |
1953 | 2031 | return |
1961 | 2039 | if result.NumberDeleted != 0 { |
1962 | 2040 | t.Error("Expected 0, actual:", result.NumberDeleted) |
1963 | 2041 | } |
1964 | err = c.ObjectPutString(CONTAINER, OBJECT, CONTENTS, "") | |
1965 | if err != nil { | |
1966 | t.Fatal(err) | |
1967 | } | |
1968 | result, err = c.BulkDelete(CONTAINER, []string{OBJECT2, OBJECT}) | |
2042 | err = c.ObjectPutString(ctx, CONTAINER, OBJECT, CONTENTS, "") | |
2043 | if err != nil { | |
2044 | t.Fatal(err) | |
2045 | } | |
2046 | result, err = c.BulkDelete(ctx, CONTAINER, []string{OBJECT2, OBJECT}) | |
1969 | 2047 | if err != nil { |
1970 | 2048 | t.Fatal(err) |
1971 | 2049 | } |
1979 | 2057 | } |
1980 | 2058 | |
1981 | 2059 | func TestBulkUpload(t *testing.T) { |
2060 | ctx := context.Background() | |
1982 | 2061 | c, rollback := makeConnectionWithContainer(t) |
1983 | 2062 | defer rollback() |
1984 | 2063 | buffer := new(bytes.Buffer) |
2003 | 2082 | t.Fatal(err) |
2004 | 2083 | } |
2005 | 2084 | |
2006 | result, err := c.BulkUpload(CONTAINER, buffer, swift.UploadTar, nil) | |
2085 | result, err := c.BulkUpload(ctx, CONTAINER, buffer, swift.UploadTar, nil) | |
2007 | 2086 | if err == swift.Forbidden { |
2008 | 2087 | t.Log("Server doesn't support BulkUpload - skipping test") |
2009 | 2088 | return |
2012 | 2091 | t.Fatal(err) |
2013 | 2092 | } |
2014 | 2093 | defer func() { |
2015 | err = c.ObjectDelete(CONTAINER, OBJECT) | |
2016 | if err != nil { | |
2017 | t.Fatal(err) | |
2018 | } | |
2019 | err = c.ObjectDelete(CONTAINER, OBJECT2) | |
2094 | err = c.ObjectDelete(ctx, CONTAINER, OBJECT) | |
2095 | if err != nil { | |
2096 | t.Fatal(err) | |
2097 | } | |
2098 | err = c.ObjectDelete(ctx, CONTAINER, OBJECT2) | |
2020 | 2099 | if err != nil { |
2021 | 2100 | t.Fatal(err) |
2022 | 2101 | } |
2026 | 2105 | } |
2027 | 2106 | t.Log("Errors:", result.Errors) |
2028 | 2107 | |
2029 | _, _, err = c.Object(CONTAINER, OBJECT) | |
2108 | _, _, err = c.Object(ctx, CONTAINER, OBJECT) | |
2030 | 2109 | if err != nil { |
2031 | 2110 | t.Error("Expecting object to be found") |
2032 | 2111 | } |
2033 | _, _, err = c.Object(CONTAINER, OBJECT2) | |
2112 | _, _, err = c.Object(ctx, CONTAINER, OBJECT2) | |
2034 | 2113 | if err != nil { |
2035 | 2114 | t.Error("Expecting object to be found") |
2036 | 2115 | } |
2037 | 2116 | } |
2038 | 2117 | |
2039 | 2118 | func TestObjectDifficultName(t *testing.T) { |
2119 | ctx := context.Background() | |
2040 | 2120 | c, rollback := makeConnectionWithContainer(t) |
2041 | 2121 | defer rollback() |
2042 | 2122 | const name = `hello? sausage/êé/Hello, 世界/ " ' @ < > & ?/` |
2043 | err := c.ObjectPutString(CONTAINER, name, CONTENTS, "") | |
2123 | err := c.ObjectPutString(ctx, CONTAINER, name, CONTENTS, "") | |
2044 | 2124 | if err != nil { |
2045 | 2125 | t.Fatal(err) |
2046 | 2126 | } |
2047 | 2127 | defer func() { |
2048 | err = c.ObjectDelete(CONTAINER, name) | |
2128 | err = c.ObjectDelete(ctx, CONTAINER, name) | |
2049 | 2129 | if err != nil { |
2050 | 2130 | t.Fatal(err) |
2051 | 2131 | } |
2052 | 2132 | }() |
2053 | objects, err := c.ObjectNamesAll(CONTAINER, nil) | |
2133 | objects, err := c.ObjectNamesAll(ctx, CONTAINER, nil) | |
2054 | 2134 | if err != nil { |
2055 | 2135 | t.Error(err) |
2056 | 2136 | } |
2067 | 2147 | } |
2068 | 2148 | |
2069 | 2149 | func TestTempUrl(t *testing.T) { |
2150 | ctx := context.Background() | |
2070 | 2151 | c, rollback := makeConnectionWithContainer(t) |
2071 | 2152 | defer rollback() |
2072 | err := c.ObjectPutBytes(CONTAINER, OBJECT, []byte(CONTENTS), "") | |
2153 | err := c.ObjectPutBytes(ctx, CONTAINER, OBJECT, []byte(CONTENTS), "") | |
2073 | 2154 | if err != nil { |
2074 | 2155 | t.Fatal(err) |
2075 | 2156 | } |
2076 | 2157 | defer func() { |
2077 | err = c.ObjectDelete(CONTAINER, OBJECT) | |
2158 | err = c.ObjectDelete(ctx, CONTAINER, OBJECT) | |
2078 | 2159 | if err != nil { |
2079 | 2160 | t.Fatal(err) |
2080 | 2161 | } |
2082 | 2163 | |
2083 | 2164 | m := swift.Metadata{} |
2084 | 2165 | m["temp-url-key"] = SECRET_KEY |
2085 | err = c.AccountUpdate(m.AccountHeaders()) | |
2166 | err = c.AccountUpdate(ctx, m.AccountHeaders()) | |
2086 | 2167 | if err != nil { |
2087 | 2168 | t.Fatal(err) |
2088 | 2169 | } |
2116 | 2197 | } |
2117 | 2198 | |
2118 | 2199 | func TestQueryInfo(t *testing.T) { |
2200 | ctx := context.Background() | |
2119 | 2201 | c, rollback := makeConnectionAuth(t) |
2120 | 2202 | defer rollback() |
2121 | infos, err := c.QueryInfo() | |
2203 | infos, err := c.QueryInfo(ctx) | |
2122 | 2204 | if err != nil { |
2123 | 2205 | t.Log("Server doesn't support querying info") |
2124 | 2206 | return |
2129 | 2211 | } |
2130 | 2212 | |
2131 | 2213 | func TestDLOCreate(t *testing.T) { |
2214 | ctx := context.Background() | |
2132 | 2215 | c, rollback := makeConnectionWithSegmentsContainer(t) |
2133 | 2216 | defer rollback() |
2134 | 2217 | |
2137 | 2220 | ObjectName: OBJECT, |
2138 | 2221 | ContentType: "image/jpeg", |
2139 | 2222 | } |
2140 | out, err := c.DynamicLargeObjectCreate(&opts) | |
2223 | out, err := c.DynamicLargeObjectCreate(ctx, &opts) | |
2141 | 2224 | if err != nil { |
2142 | 2225 | t.Fatal(err) |
2143 | 2226 | } |
2144 | 2227 | defer func() { |
2145 | err = c.DynamicLargeObjectDelete(CONTAINER, OBJECT) | |
2228 | err = c.DynamicLargeObjectDelete(ctx, CONTAINER, OBJECT) | |
2146 | 2229 | if err != nil { |
2147 | 2230 | t.Fatal(err) |
2148 | 2231 | } |
2156 | 2239 | t.Fatal(err) |
2157 | 2240 | } |
2158 | 2241 | } |
2159 | err = out.Close() | |
2242 | err = out.CloseWithContext(ctx) | |
2160 | 2243 | if err != nil { |
2161 | 2244 | t.Error(err) |
2162 | 2245 | } |
2163 | 2246 | expected := buf.String() |
2164 | contents, err := c.ObjectGetString(CONTAINER, OBJECT) | |
2247 | contents, err := c.ObjectGetString(ctx, CONTAINER, OBJECT) | |
2165 | 2248 | if err != nil { |
2166 | 2249 | t.Error(err) |
2167 | 2250 | } |
2168 | 2251 | if contents != expected { |
2169 | 2252 | t.Errorf("Contents wrong, expected %q, got: %q", expected, contents) |
2170 | 2253 | } |
2171 | info, _, err := c.Object(CONTAINER, OBJECT) | |
2254 | info, _, err := c.Object(ctx, CONTAINER, OBJECT) | |
2172 | 2255 | if err != nil { |
2173 | 2256 | t.Fatal(err) |
2174 | 2257 | } |
2181 | 2264 | } |
2182 | 2265 | |
2183 | 2266 | func TestDLOInsert(t *testing.T) { |
2267 | ctx := context.Background() | |
2184 | 2268 | c, rollback := makeConnectionWithDLO(t) |
2185 | 2269 | defer rollback() |
2186 | 2270 | opts := swift.LargeObjectOpts{ |
2189 | 2273 | CheckHash: true, |
2190 | 2274 | ContentType: "image/jpeg", |
2191 | 2275 | } |
2192 | out, err := c.DynamicLargeObjectCreateFile(&opts) | |
2276 | out, err := c.DynamicLargeObjectCreateFile(ctx, &opts) | |
2193 | 2277 | if err != nil { |
2194 | 2278 | t.Fatal(err) |
2195 | 2279 | } |
2200 | 2284 | if err != nil { |
2201 | 2285 | t.Fatal(err) |
2202 | 2286 | } |
2203 | fmt.Fprintf(buf, "\n%d %s\n", 1, CONTENTS) | |
2204 | err = out.Close() | |
2287 | _, _ = fmt.Fprintf(buf, "\n%d %s\n", 1, CONTENTS) | |
2288 | err = out.CloseWithContext(ctx) | |
2205 | 2289 | if err != nil { |
2206 | 2290 | t.Error(err) |
2207 | 2291 | } |
2208 | 2292 | expected := buf.String() |
2209 | contents, err := c.ObjectGetString(CONTAINER, OBJECT) | |
2293 | contents, err := c.ObjectGetString(ctx, CONTAINER, OBJECT) | |
2210 | 2294 | if err != nil { |
2211 | 2295 | t.Error(err) |
2212 | 2296 | } |
2216 | 2300 | } |
2217 | 2301 | |
2218 | 2302 | func TestDLOAppend(t *testing.T) { |
2303 | ctx := context.Background() | |
2219 | 2304 | c, rollback := makeConnectionWithDLO(t) |
2220 | 2305 | defer rollback() |
2221 | 2306 | opts := swift.LargeObjectOpts{ |
2225 | 2310 | CheckHash: true, |
2226 | 2311 | ContentType: "image/jpeg", |
2227 | 2312 | } |
2228 | out, err := c.DynamicLargeObjectCreateFile(&opts) | |
2229 | if err != nil { | |
2230 | t.Fatal(err) | |
2231 | } | |
2232 | ||
2233 | contents, err := c.ObjectGetString(CONTAINER, OBJECT) | |
2313 | out, err := c.DynamicLargeObjectCreateFile(ctx, &opts) | |
2314 | if err != nil { | |
2315 | t.Fatal(err) | |
2316 | } | |
2317 | ||
2318 | contents, err := c.ObjectGetString(ctx, CONTAINER, OBJECT) | |
2234 | 2319 | buf := bytes.NewBuffer([]byte(contents)) |
2235 | 2320 | multi := io.MultiWriter(buf, out) |
2236 | 2321 | for i := 0; i < 2; i++ { |
2239 | 2324 | t.Fatal(err) |
2240 | 2325 | } |
2241 | 2326 | } |
2242 | err = out.Close() | |
2327 | err = out.CloseWithContext(ctx) | |
2243 | 2328 | if err != nil { |
2244 | 2329 | t.Error(err) |
2245 | 2330 | } |
2246 | 2331 | expected := buf.String() |
2247 | contents, err = c.ObjectGetString(CONTAINER, OBJECT) | |
2332 | contents, err = c.ObjectGetString(ctx, CONTAINER, OBJECT) | |
2248 | 2333 | if err != nil { |
2249 | 2334 | t.Error(err) |
2250 | 2335 | } |
2254 | 2339 | } |
2255 | 2340 | |
2256 | 2341 | func TestDLOTruncate(t *testing.T) { |
2342 | ctx := context.Background() | |
2257 | 2343 | c, rollback := makeConnectionWithDLO(t) |
2258 | 2344 | defer rollback() |
2259 | 2345 | opts := swift.LargeObjectOpts{ |
2263 | 2349 | CheckHash: true, |
2264 | 2350 | ContentType: "image/jpeg", |
2265 | 2351 | } |
2266 | out, err := c.DynamicLargeObjectCreateFile(&opts) | |
2352 | out, err := c.DynamicLargeObjectCreateFile(ctx, &opts) | |
2267 | 2353 | if err != nil { |
2268 | 2354 | t.Fatal(err) |
2269 | 2355 | } |
2274 | 2360 | if err != nil { |
2275 | 2361 | t.Fatal(err) |
2276 | 2362 | } |
2277 | err = out.Close() | |
2363 | err = out.CloseWithContext(ctx) | |
2278 | 2364 | if err != nil { |
2279 | 2365 | t.Error(err) |
2280 | 2366 | } |
2281 | 2367 | expected := buf.String() |
2282 | contents, err := c.ObjectGetString(CONTAINER, OBJECT) | |
2368 | contents, err := c.ObjectGetString(ctx, CONTAINER, OBJECT) | |
2283 | 2369 | if err != nil { |
2284 | 2370 | t.Error(err) |
2285 | 2371 | } |
2289 | 2375 | } |
2290 | 2376 | |
2291 | 2377 | func TestDLOMove(t *testing.T) { |
2378 | ctx := context.Background() | |
2292 | 2379 | c, rollback := makeConnectionWithDLO(t) |
2293 | 2380 | defer rollback() |
2294 | contents, err := c.ObjectGetString(CONTAINER, OBJECT) | |
2295 | if err != nil { | |
2296 | t.Fatal(err) | |
2297 | } | |
2298 | ||
2299 | err = c.DynamicLargeObjectMove(CONTAINER, OBJECT, CONTAINER, OBJECT2) | |
2381 | contents, err := c.ObjectGetString(ctx, CONTAINER, OBJECT) | |
2382 | if err != nil { | |
2383 | t.Fatal(err) | |
2384 | } | |
2385 | ||
2386 | err = c.DynamicLargeObjectMove(ctx, CONTAINER, OBJECT, CONTAINER, OBJECT2) | |
2300 | 2387 | if err != nil { |
2301 | 2388 | t.Fatal(err) |
2302 | 2389 | } |
2303 | 2390 | defer func() { |
2304 | err = c.DynamicLargeObjectDelete(CONTAINER, OBJECT2) | |
2391 | err = c.DynamicLargeObjectDelete(ctx, CONTAINER, OBJECT2) | |
2305 | 2392 | if err != nil { |
2306 | 2393 | t.Fatal(err) |
2307 | 2394 | } |
2308 | 2395 | }() |
2309 | 2396 | |
2310 | contents2, err := c.ObjectGetString(CONTAINER, OBJECT2) | |
2397 | contents2, err := c.ObjectGetString(ctx, CONTAINER, OBJECT2) | |
2311 | 2398 | if err != nil { |
2312 | 2399 | t.Fatal(err) |
2313 | 2400 | } |
2318 | 2405 | } |
2319 | 2406 | |
2320 | 2407 | func TestDLONoSegmentContainer(t *testing.T) { |
2408 | ctx := context.Background() | |
2321 | 2409 | c, rollback := makeConnectionWithDLO(t) |
2322 | 2410 | defer rollback() |
2323 | 2411 | opts := swift.LargeObjectOpts{ |
2326 | 2414 | ContentType: "image/jpeg", |
2327 | 2415 | SegmentContainer: CONTAINER, |
2328 | 2416 | } |
2329 | out, err := c.DynamicLargeObjectCreate(&opts) | |
2417 | out, err := c.DynamicLargeObjectCreate(ctx, &opts) | |
2330 | 2418 | if err != nil { |
2331 | 2419 | t.Fatal(err) |
2332 | 2420 | } |
2339 | 2427 | t.Fatal(err) |
2340 | 2428 | } |
2341 | 2429 | } |
2342 | err = out.Close() | |
2430 | err = out.CloseWithContext(ctx) | |
2343 | 2431 | if err != nil { |
2344 | 2432 | t.Error(err) |
2345 | 2433 | } |
2346 | 2434 | expected := buf.String() |
2347 | contents, err := c.ObjectGetString(CONTAINER, OBJECT) | |
2435 | contents, err := c.ObjectGetString(ctx, CONTAINER, OBJECT) | |
2348 | 2436 | if err != nil { |
2349 | 2437 | t.Error(err) |
2350 | 2438 | } |
2354 | 2442 | } |
2355 | 2443 | |
2356 | 2444 | func TestDLOCreateMissingSegmentsInList(t *testing.T) { |
2445 | ctx := context.Background() | |
2357 | 2446 | c, rollback := makeConnectionWithContainer(t) |
2358 | 2447 | defer rollback() |
2359 | 2448 | |
2368 | 2457 | w.Header().Set(k, v[0]) |
2369 | 2458 | } |
2370 | 2459 | w.WriteHeader(recorder.Code) |
2371 | w.Write([]byte("null\n")) | |
2460 | _, _ = w.Write([]byte("null\n")) | |
2372 | 2461 | }) |
2373 | 2462 | defer srv.UnsetOverride(listURL) |
2374 | 2463 | |
2375 | 2464 | headers := swift.Headers{} |
2376 | err := c.ContainerCreate(SEGMENTS_CONTAINER, headers) | |
2465 | err := c.ContainerCreate(ctx, SEGMENTS_CONTAINER, headers) | |
2377 | 2466 | if err != nil { |
2378 | 2467 | t.Fatal(err) |
2379 | 2468 | } |
2380 | 2469 | defer func() { |
2381 | err = c.ContainerDelete(SEGMENTS_CONTAINER) | |
2470 | err = c.ContainerDelete(ctx, SEGMENTS_CONTAINER) | |
2382 | 2471 | if err != nil { |
2383 | 2472 | t.Fatal(err) |
2384 | 2473 | } |
2389 | 2478 | ObjectName: OBJECT, |
2390 | 2479 | ContentType: "image/jpeg", |
2391 | 2480 | } |
2392 | out, err := c.DynamicLargeObjectCreate(&opts) | |
2481 | out, err := c.DynamicLargeObjectCreate(ctx, &opts) | |
2393 | 2482 | if err != nil { |
2394 | 2483 | t.Fatal(err) |
2395 | 2484 | } |
2396 | 2485 | defer func() { |
2397 | err = c.DynamicLargeObjectDelete(CONTAINER, OBJECT) | |
2486 | err = c.DynamicLargeObjectDelete(ctx, CONTAINER, OBJECT) | |
2398 | 2487 | if err != nil { |
2399 | 2488 | t.Fatal(err) |
2400 | 2489 | } |
2408 | 2497 | t.Fatal(err) |
2409 | 2498 | } |
2410 | 2499 | } |
2411 | err = out.Close() | |
2500 | err = out.CloseWithContext(ctx) | |
2412 | 2501 | if err != nil { |
2413 | 2502 | t.Error(err) |
2414 | 2503 | } |
2415 | 2504 | expected := buf.String() |
2416 | contents, err := c.ObjectGetString(CONTAINER, OBJECT) | |
2505 | contents, err := c.ObjectGetString(ctx, CONTAINER, OBJECT) | |
2417 | 2506 | if err != nil { |
2418 | 2507 | t.Error(err) |
2419 | 2508 | } |
2423 | 2512 | } |
2424 | 2513 | |
2425 | 2514 | func TestDLOCreateIncorrectSize(t *testing.T) { |
2515 | ctx := context.Background() | |
2426 | 2516 | c, rollback := makeConnectionWithContainer(t) |
2427 | 2517 | defer rollback() |
2428 | 2518 | |
2445 | 2535 | } |
2446 | 2536 | } |
2447 | 2537 | w.WriteHeader(recorder.Code) |
2448 | w.Write(recorder.Body.Bytes()) | |
2538 | _, _ = w.Write(recorder.Body.Bytes()) | |
2449 | 2539 | }) |
2450 | 2540 | defer srv.UnsetOverride(listURL) |
2451 | 2541 | |
2452 | 2542 | headers := swift.Headers{} |
2453 | err := c.ContainerCreate(SEGMENTS_CONTAINER, headers) | |
2543 | err := c.ContainerCreate(ctx, SEGMENTS_CONTAINER, headers) | |
2454 | 2544 | if err != nil { |
2455 | 2545 | t.Fatal(err) |
2456 | 2546 | } |
2457 | 2547 | defer func() { |
2458 | err = c.ContainerDelete(SEGMENTS_CONTAINER) | |
2548 | err = c.ContainerDelete(ctx, SEGMENTS_CONTAINER) | |
2459 | 2549 | if err != nil { |
2460 | 2550 | t.Fatal(err) |
2461 | 2551 | } |
2466 | 2556 | ObjectName: OBJECT, |
2467 | 2557 | ContentType: "image/jpeg", |
2468 | 2558 | } |
2469 | out, err := c.DynamicLargeObjectCreate(&opts) | |
2559 | out, err := c.DynamicLargeObjectCreate(ctx, &opts) | |
2470 | 2560 | if err != nil { |
2471 | 2561 | t.Fatal(err) |
2472 | 2562 | } |
2473 | 2563 | defer func() { |
2474 | err = c.DynamicLargeObjectDelete(CONTAINER, OBJECT) | |
2564 | err = c.DynamicLargeObjectDelete(ctx, CONTAINER, OBJECT) | |
2475 | 2565 | if err != nil { |
2476 | 2566 | t.Fatal(err) |
2477 | 2567 | } |
2484 | 2574 | t.Fatal(err) |
2485 | 2575 | } |
2486 | 2576 | } |
2487 | err = out.Close() | |
2577 | err = out.CloseWithContext(ctx) | |
2488 | 2578 | if err != nil { |
2489 | 2579 | t.Error(err) |
2490 | 2580 | } |
2492 | 2582 | t.Errorf("Unexpected HEAD requests count, expected %d, got: %d", expectedHeadCount, headCount) |
2493 | 2583 | } |
2494 | 2584 | expected := buf.String() |
2495 | contents, err := c.ObjectGetString(CONTAINER, OBJECT) | |
2585 | contents, err := c.ObjectGetString(ctx, CONTAINER, OBJECT) | |
2496 | 2586 | if err != nil { |
2497 | 2587 | t.Error(err) |
2498 | 2588 | } |
2502 | 2592 | } |
2503 | 2593 | |
2504 | 2594 | func TestDLOConcurrentWrite(t *testing.T) { |
2595 | ctx := context.Background() | |
2505 | 2596 | c, rollback := makeConnectionWithSegmentsContainer(t) |
2506 | 2597 | defer rollback() |
2507 | 2598 | |
2516 | 2607 | ObjectName: objName, |
2517 | 2608 | ContentType: "image/jpeg", |
2518 | 2609 | } |
2519 | out, err := c.DynamicLargeObjectCreate(&opts) | |
2610 | out, err := c.DynamicLargeObjectCreate(ctx, &opts) | |
2520 | 2611 | if err != nil { |
2521 | 2612 | t.Fatal(err) |
2522 | 2613 | } |
2523 | 2614 | defer func() { |
2524 | err = c.DynamicLargeObjectDelete(CONTAINER, objName) | |
2615 | err = c.DynamicLargeObjectDelete(ctx, CONTAINER, objName) | |
2525 | 2616 | if err != nil { |
2526 | 2617 | t.Fatal(err) |
2527 | 2618 | } |
2543 | 2634 | t.Fatalf("expected to write %d, got: %d", chunkSize, n) |
2544 | 2635 | } |
2545 | 2636 | } |
2546 | err = out.Close() | |
2637 | err = out.CloseWithContext(ctx) | |
2547 | 2638 | if err != nil { |
2548 | 2639 | t.Error(err) |
2549 | 2640 | } |
2550 | 2641 | expected := buf.String() |
2551 | contents, err := c.ObjectGetString(CONTAINER, objName) | |
2642 | contents, err := c.ObjectGetString(ctx, CONTAINER, objName) | |
2552 | 2643 | if err != nil { |
2553 | 2644 | t.Error(err) |
2554 | 2645 | } |
2569 | 2660 | } |
2570 | 2661 | |
2571 | 2662 | func TestDLOSegmentation(t *testing.T) { |
2663 | ctx := context.Background() | |
2572 | 2664 | c, rollback := makeConnectionWithSegmentsContainer(t) |
2573 | 2665 | defer rollback() |
2574 | 2666 | |
2581 | 2673 | } |
2582 | 2674 | |
2583 | 2675 | testSegmentation(t, c, func() swift.LargeObjectFile { |
2584 | out, err := c.DynamicLargeObjectCreate(&opts) | |
2676 | out, err := c.DynamicLargeObjectCreate(ctx, &opts) | |
2585 | 2677 | if err != nil { |
2586 | 2678 | t.Fatal(err) |
2587 | 2679 | } |
2624 | 2716 | } |
2625 | 2717 | |
2626 | 2718 | func TestDLOSegmentationBuffered(t *testing.T) { |
2719 | ctx := context.Background() | |
2627 | 2720 | c, rollback := makeConnectionWithSegmentsContainer(t) |
2628 | 2721 | defer rollback() |
2629 | 2722 | |
2635 | 2728 | } |
2636 | 2729 | |
2637 | 2730 | testSegmentation(t, c, func() swift.LargeObjectFile { |
2638 | out, err := c.DynamicLargeObjectCreate(&opts) | |
2731 | out, err := c.DynamicLargeObjectCreate(ctx, &opts) | |
2639 | 2732 | if err != nil { |
2640 | 2733 | t.Fatal(err) |
2641 | 2734 | } |
2678 | 2771 | } |
2679 | 2772 | |
2680 | 2773 | func TestSLOCreate(t *testing.T) { |
2774 | ctx := context.Background() | |
2681 | 2775 | c, rollback := makeConnectionWithSegmentsContainer(t) |
2682 | 2776 | defer rollback() |
2683 | 2777 | |
2686 | 2780 | ObjectName: OBJECT, |
2687 | 2781 | ContentType: "image/jpeg", |
2688 | 2782 | } |
2689 | out, err := c.StaticLargeObjectCreate(&opts) | |
2783 | out, err := c.StaticLargeObjectCreate(ctx, &opts) | |
2690 | 2784 | if err != nil { |
2691 | 2785 | if err == swift.SLONotSupported { |
2692 | 2786 | t.Skip("SLO not supported") |
2695 | 2789 | t.Fatal(err) |
2696 | 2790 | } |
2697 | 2791 | defer func() { |
2698 | err = c.StaticLargeObjectDelete(CONTAINER, OBJECT) | |
2792 | err = c.StaticLargeObjectDelete(ctx, CONTAINER, OBJECT) | |
2699 | 2793 | if err != nil { |
2700 | 2794 | t.Fatal(err) |
2701 | 2795 | } |
2709 | 2803 | t.Fatal(err) |
2710 | 2804 | } |
2711 | 2805 | } |
2712 | err = out.Close() | |
2806 | err = out.CloseWithContext(ctx) | |
2713 | 2807 | if err != nil { |
2714 | 2808 | t.Error(err) |
2715 | 2809 | } |
2716 | 2810 | expected := buf.String() |
2717 | contents, err := c.ObjectGetString(CONTAINER, OBJECT) | |
2811 | contents, err := c.ObjectGetString(ctx, CONTAINER, OBJECT) | |
2718 | 2812 | if err != nil { |
2719 | 2813 | t.Error(err) |
2720 | 2814 | } |
2721 | 2815 | if contents != expected { |
2722 | 2816 | t.Errorf("Contents wrong, expected %q, got: %q", expected, contents) |
2723 | 2817 | } |
2724 | info, _, err := c.Object(CONTAINER, OBJECT) | |
2818 | info, _, err := c.Object(ctx, CONTAINER, OBJECT) | |
2725 | 2819 | if err != nil { |
2726 | 2820 | t.Fatal(err) |
2727 | 2821 | } |
2734 | 2828 | } |
2735 | 2829 | |
2736 | 2830 | func TestSLOInsert(t *testing.T) { |
2831 | ctx := context.Background() | |
2737 | 2832 | c, rollback := makeConnectionWithSLO(t) |
2738 | 2833 | defer rollback() |
2739 | 2834 | opts := swift.LargeObjectOpts{ |
2741 | 2836 | ObjectName: OBJECT, |
2742 | 2837 | ContentType: "image/jpeg", |
2743 | 2838 | } |
2744 | out, err := c.StaticLargeObjectCreateFile(&opts) | |
2839 | out, err := c.StaticLargeObjectCreateFile(ctx, &opts) | |
2745 | 2840 | if err != nil { |
2746 | 2841 | t.Fatal(err) |
2747 | 2842 | } |
2752 | 2847 | if err != nil { |
2753 | 2848 | t.Fatal(err) |
2754 | 2849 | } |
2755 | fmt.Fprintf(buf, "\n%d %s\n", 1, CONTENTS) | |
2756 | err = out.Close() | |
2850 | _, _ = fmt.Fprintf(buf, "\n%d %s\n", 1, CONTENTS) | |
2851 | err = out.CloseWithContext(ctx) | |
2757 | 2852 | if err != nil { |
2758 | 2853 | t.Error(err) |
2759 | 2854 | } |
2760 | 2855 | expected := buf.String() |
2761 | contents, err := c.ObjectGetString(CONTAINER, OBJECT) | |
2856 | contents, err := c.ObjectGetString(ctx, CONTAINER, OBJECT) | |
2762 | 2857 | if err != nil { |
2763 | 2858 | t.Error(err) |
2764 | 2859 | } |
2768 | 2863 | } |
2769 | 2864 | |
2770 | 2865 | func TestSLOAppend(t *testing.T) { |
2866 | ctx := context.Background() | |
2771 | 2867 | c, rollback := makeConnectionWithSLO(t) |
2772 | 2868 | defer rollback() |
2773 | 2869 | opts := swift.LargeObjectOpts{ |
2777 | 2873 | CheckHash: true, |
2778 | 2874 | ContentType: "image/jpeg", |
2779 | 2875 | } |
2780 | out, err := c.StaticLargeObjectCreateFile(&opts) | |
2781 | if err != nil { | |
2782 | t.Fatal(err) | |
2783 | } | |
2784 | ||
2785 | contents, err := c.ObjectGetString(CONTAINER, OBJECT) | |
2876 | out, err := c.StaticLargeObjectCreateFile(ctx, &opts) | |
2877 | if err != nil { | |
2878 | t.Fatal(err) | |
2879 | } | |
2880 | ||
2881 | contents, err := c.ObjectGetString(ctx, CONTAINER, OBJECT) | |
2786 | 2882 | buf := bytes.NewBuffer([]byte(contents)) |
2787 | 2883 | multi := io.MultiWriter(buf, out) |
2788 | 2884 | for i := 0; i < 2; i++ { |
2791 | 2887 | t.Fatal(err) |
2792 | 2888 | } |
2793 | 2889 | } |
2794 | err = out.Close() | |
2890 | err = out.CloseWithContext(ctx) | |
2795 | 2891 | if err != nil { |
2796 | 2892 | t.Error(err) |
2797 | 2893 | } |
2798 | 2894 | expected := buf.String() |
2799 | contents, err = c.ObjectGetString(CONTAINER, OBJECT) | |
2895 | contents, err = c.ObjectGetString(ctx, CONTAINER, OBJECT) | |
2800 | 2896 | if err != nil { |
2801 | 2897 | t.Error(err) |
2802 | 2898 | } |
2806 | 2902 | } |
2807 | 2903 | |
2808 | 2904 | func TestSLOMove(t *testing.T) { |
2905 | ctx := context.Background() | |
2809 | 2906 | c, rollback := makeConnectionWithSLO(t) |
2810 | 2907 | defer rollback() |
2811 | contents, err := c.ObjectGetString(CONTAINER, OBJECT) | |
2812 | if err != nil { | |
2813 | t.Fatal(err) | |
2814 | } | |
2815 | ||
2816 | err = c.StaticLargeObjectMove(CONTAINER, OBJECT, CONTAINER, OBJECT2) | |
2908 | contents, err := c.ObjectGetString(ctx, CONTAINER, OBJECT) | |
2909 | if err != nil { | |
2910 | t.Fatal(err) | |
2911 | } | |
2912 | ||
2913 | err = c.StaticLargeObjectMove(ctx, CONTAINER, OBJECT, CONTAINER, OBJECT2) | |
2817 | 2914 | if err != nil { |
2818 | 2915 | t.Fatal(err) |
2819 | 2916 | } |
2820 | 2917 | defer func() { |
2821 | err = c.StaticLargeObjectDelete(CONTAINER, OBJECT2) | |
2918 | err = c.StaticLargeObjectDelete(ctx, CONTAINER, OBJECT2) | |
2822 | 2919 | if err != nil { |
2823 | 2920 | t.Fatal(err) |
2824 | 2921 | } |
2825 | 2922 | }() |
2826 | 2923 | |
2827 | contents2, err := c.ObjectGetString(CONTAINER, OBJECT2) | |
2924 | contents2, err := c.ObjectGetString(ctx, CONTAINER, OBJECT2) | |
2828 | 2925 | if err != nil { |
2829 | 2926 | t.Fatal(err) |
2830 | 2927 | } |
2835 | 2932 | } |
2836 | 2933 | |
2837 | 2934 | func TestSLONoSegmentContainer(t *testing.T) { |
2935 | ctx := context.Background() | |
2838 | 2936 | c, rollback := makeConnectionWithSLO(t) |
2839 | 2937 | defer rollback() |
2840 | 2938 | |
2844 | 2942 | ContentType: "image/jpeg", |
2845 | 2943 | SegmentContainer: CONTAINER, |
2846 | 2944 | } |
2847 | out, err := c.StaticLargeObjectCreate(&opts) | |
2945 | out, err := c.StaticLargeObjectCreate(ctx, &opts) | |
2848 | 2946 | if err != nil { |
2849 | 2947 | t.Fatal(err) |
2850 | 2948 | } |
2857 | 2955 | t.Fatal(err) |
2858 | 2956 | } |
2859 | 2957 | } |
2860 | err = out.Close() | |
2958 | err = out.CloseWithContext(ctx) | |
2861 | 2959 | if err != nil { |
2862 | 2960 | t.Error(err) |
2863 | 2961 | } |
2864 | 2962 | expected := buf.String() |
2865 | contents, err := c.ObjectGetString(CONTAINER, OBJECT) | |
2963 | contents, err := c.ObjectGetString(ctx, CONTAINER, OBJECT) | |
2866 | 2964 | if err != nil { |
2867 | 2965 | t.Error(err) |
2868 | 2966 | } |
2870 | 2968 | t.Errorf("Contents wrong, expected %q, got: %q", expected, contents) |
2871 | 2969 | } |
2872 | 2970 | |
2873 | err = c.StaticLargeObjectDelete(CONTAINER, OBJECT) | |
2971 | err = c.StaticLargeObjectDelete(ctx, CONTAINER, OBJECT) | |
2874 | 2972 | if err != nil { |
2875 | 2973 | t.Fatal(err) |
2876 | 2974 | } |
2877 | 2975 | } |
2878 | 2976 | |
2879 | 2977 | func TestSLOMinChunkSize(t *testing.T) { |
2978 | ctx := context.Background() | |
2880 | 2979 | c, rollback := makeConnectionWithSegmentsContainer(t) |
2881 | 2980 | defer rollback() |
2882 | 2981 | if srv == nil { |
2885 | 2984 | } |
2886 | 2985 | |
2887 | 2986 | srv.SetOverride("/info", func(w http.ResponseWriter, r *http.Request, recorder *httptest.ResponseRecorder) { |
2888 | w.Write([]byte(`{"slo": {"min_segment_size": 4}}`)) | |
2987 | _, _ = w.Write([]byte(`{"slo": {"min_segment_size": 4}}`)) | |
2889 | 2988 | }) |
2890 | 2989 | defer srv.UnsetOverride("/info") |
2891 | c.QueryInfo() | |
2990 | _, _ = c.QueryInfo(ctx) | |
2892 | 2991 | |
2893 | 2992 | opts := swift.LargeObjectOpts{ |
2894 | 2993 | Container: CONTAINER, |
2900 | 2999 | } |
2901 | 3000 | |
2902 | 3001 | testSLOSegmentation(t, c, func() swift.LargeObjectFile { |
2903 | out, err := c.StaticLargeObjectCreate(&opts) | |
3002 | out, err := c.StaticLargeObjectCreate(ctx, &opts) | |
2904 | 3003 | if err != nil { |
2905 | 3004 | t.Fatal(err) |
2906 | 3005 | } |
2909 | 3008 | } |
2910 | 3009 | |
2911 | 3010 | func TestSLOSegmentation(t *testing.T) { |
3011 | ctx := context.Background() | |
2912 | 3012 | c, rollback := makeConnectionWithSegmentsContainer(t) |
2913 | 3013 | defer rollback() |
2914 | 3014 | opts := swift.LargeObjectOpts{ |
2920 | 3020 | NoBuffer: true, |
2921 | 3021 | } |
2922 | 3022 | testSLOSegmentation(t, c, func() swift.LargeObjectFile { |
2923 | out, err := c.StaticLargeObjectCreate(&opts) | |
3023 | out, err := c.StaticLargeObjectCreate(ctx, &opts) | |
2924 | 3024 | if err != nil { |
2925 | 3025 | if err == swift.SLONotSupported { |
2926 | 3026 | t.Skip("SLO not supported") |
2932 | 3032 | } |
2933 | 3033 | |
2934 | 3034 | func TestSLOSegmentationBuffered(t *testing.T) { |
3035 | ctx := context.Background() | |
2935 | 3036 | c, rollback := makeConnectionWithSegmentsContainer(t) |
2936 | 3037 | defer rollback() |
2937 | 3038 | opts := swift.LargeObjectOpts{ |
2942 | 3043 | MinChunkSize: 4, |
2943 | 3044 | } |
2944 | 3045 | testSegmentation(t, c, func() swift.LargeObjectFile { |
2945 | out, err := c.StaticLargeObjectCreate(&opts) | |
3046 | out, err := c.StaticLargeObjectCreate(ctx, &opts) | |
2946 | 3047 | if err != nil { |
2947 | 3048 | if err == swift.SLONotSupported { |
2948 | 3049 | t.Skip("SLO not supported") |
3034 | 3135 | } |
3035 | 3136 | |
3036 | 3137 | func testSegmentation(t *testing.T, c *swift.Connection, createObj func() swift.LargeObjectFile, testCases []segmentTest) { |
3138 | ctx := context.Background() | |
3037 | 3139 | var err error |
3038 | 3140 | runTestCase := func(tCase segmentTest) { |
3039 | 3141 | out := createObj() |
3040 | 3142 | defer func() { |
3041 | err = c.LargeObjectDelete(CONTAINER, OBJECT) | |
3143 | err = c.LargeObjectDelete(ctx, CONTAINER, OBJECT) | |
3042 | 3144 | if err != nil { |
3043 | 3145 | t.Fatal(err) |
3044 | 3146 | } |
3055 | 3157 | } |
3056 | 3158 | } |
3057 | 3159 | } |
3058 | err = out.Close() | |
3160 | err = out.CloseWithContext(ctx) | |
3059 | 3161 | if err != nil { |
3060 | 3162 | t.Error(err) |
3061 | 3163 | } |
3062 | contents, err := c.ObjectGetString(CONTAINER, OBJECT) | |
3164 | contents, err := c.ObjectGetString(ctx, CONTAINER, OBJECT) | |
3063 | 3165 | if err != nil { |
3064 | 3166 | t.Error(err) |
3065 | 3167 | } |
3066 | 3168 | if contents != tCase.expectedValue { |
3067 | 3169 | t.Errorf("Contents wrong, expected %q, got: %q", tCase.expectedValue, contents) |
3068 | 3170 | } |
3069 | container, objects, err := c.LargeObjectGetSegments(CONTAINER, OBJECT) | |
3171 | container, objects, err := c.LargeObjectGetSegments(ctx, CONTAINER, OBJECT) | |
3070 | 3172 | if err != nil { |
3071 | 3173 | t.Error(err) |
3072 | 3174 | } |
3073 | 3175 | if container != SEGMENTS_CONTAINER { |
3074 | 3176 | t.Errorf("Segments container wrong, expected %q, got: %q", SEGMENTS_CONTAINER, container) |
3075 | 3177 | } |
3076 | _, headers, err := c.Object(CONTAINER, OBJECT) | |
3178 | _, headers, err := c.Object(ctx, CONTAINER, OBJECT) | |
3077 | 3179 | if err != nil { |
3078 | 3180 | t.Fatal(err) |
3079 | 3181 | } |
3080 | 3182 | if headers.IsLargeObjectSLO() { |
3081 | 3183 | var info swift.SwiftInfo |
3082 | info, err = c.QueryInfo() | |
3184 | info, err = c.QueryInfo(ctx) | |
3083 | 3185 | if err != nil { |
3084 | 3186 | t.Fatal(err) |
3085 | 3187 | } |
3091 | 3193 | var segContents []string |
3092 | 3194 | for _, obj := range objects { |
3093 | 3195 | var value string |
3094 | value, err = c.ObjectGetString(SEGMENTS_CONTAINER, obj.Name) | |
3196 | value, err = c.ObjectGetString(ctx, SEGMENTS_CONTAINER, obj.Name) | |
3095 | 3197 | if err != nil { |
3096 | 3198 | t.Error(err) |
3097 | 3199 | } |
3107 | 3209 | } |
3108 | 3210 | |
3109 | 3211 | func TestContainerDelete(t *testing.T) { |
3212 | ctx := context.Background() | |
3110 | 3213 | c, rollback := makeConnectionWithContainer(t) |
3111 | 3214 | defer rollback() |
3112 | err := c.ContainerDelete(CONTAINER) | |
3113 | if err != nil { | |
3114 | t.Fatal(err) | |
3115 | } | |
3116 | err = c.ContainerDelete(CONTAINER) | |
3215 | err := c.ContainerDelete(ctx, CONTAINER) | |
3216 | if err != nil { | |
3217 | t.Fatal(err) | |
3218 | } | |
3219 | err = c.ContainerDelete(ctx, CONTAINER) | |
3117 | 3220 | if err != swift.ContainerNotFound { |
3118 | 3221 | t.Fatal("Expecting container not found", err) |
3119 | 3222 | } |
3120 | _, _, err = c.Container(CONTAINER) | |
3223 | _, _, err = c.Container(ctx, CONTAINER) | |
3121 | 3224 | if err != swift.ContainerNotFound { |
3122 | 3225 | t.Fatal("Expecting container not found", err) |
3123 | 3226 | } |
3124 | 3227 | } |
3125 | 3228 | |
3126 | 3229 | func TestUnAuthenticate(t *testing.T) { |
3230 | ctx := context.Background() | |
3127 | 3231 | c, rollback := makeConnectionAuth(t) |
3128 | 3232 | defer rollback() |
3129 | 3233 | c.UnAuthenticate() |
3131 | 3235 | t.Fatal("Shouldn't be authenticated") |
3132 | 3236 | } |
3133 | 3237 | // Test re-authenticate |
3134 | err := c.Authenticate() | |
3238 | err := c.Authenticate(ctx) | |
3135 | 3239 | if err != nil { |
3136 | 3240 | t.Fatal("ReAuth failed", err) |
3137 | 3241 | } |