diff options
Diffstat (limited to 'vendor/github.com/minio/minio-go/v7/api-putobject-snowball.go')
-rw-r--r-- | vendor/github.com/minio/minio-go/v7/api-putobject-snowball.go | 246 |
1 files changed, 246 insertions, 0 deletions
diff --git a/vendor/github.com/minio/minio-go/v7/api-putobject-snowball.go b/vendor/github.com/minio/minio-go/v7/api-putobject-snowball.go new file mode 100644 index 0000000..eb4da41 --- /dev/null +++ b/vendor/github.com/minio/minio-go/v7/api-putobject-snowball.go | |||
@@ -0,0 +1,246 @@ | |||
1 | /* | ||
2 | * MinIO Go Library for Amazon S3 Compatible Cloud Storage | ||
3 | * Copyright 2021 MinIO, Inc. | ||
4 | * | ||
5 | * Licensed under the Apache License, Version 2.0 (the "License"); | ||
6 | * you may not use this file except in compliance with the License. | ||
7 | * You may obtain a copy of the License at | ||
8 | * | ||
9 | * http://www.apache.org/licenses/LICENSE-2.0 | ||
10 | * | ||
11 | * Unless required by applicable law or agreed to in writing, software | ||
12 | * distributed under the License is distributed on an "AS IS" BASIS, | ||
13 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
14 | * See the License for the specific language governing permissions and | ||
15 | * limitations under the License. | ||
16 | */ | ||
17 | |||
18 | package minio | ||
19 | |||
20 | import ( | ||
21 | "archive/tar" | ||
22 | "bufio" | ||
23 | "bytes" | ||
24 | "context" | ||
25 | "fmt" | ||
26 | "io" | ||
27 | "net/http" | ||
28 | "os" | ||
29 | "strings" | ||
30 | "sync" | ||
31 | "time" | ||
32 | |||
33 | "github.com/klauspost/compress/s2" | ||
34 | ) | ||
35 | |||
36 | // SnowballOptions contains options for PutObjectsSnowball calls. | ||
37 | type SnowballOptions struct { | ||
38 | // Opts is options applied to all objects. | ||
39 | Opts PutObjectOptions | ||
40 | |||
41 | // Processing options: | ||
42 | |||
43 | // InMemory specifies that all objects should be collected in memory | ||
44 | // before they are uploaded. | ||
45 | // If false a temporary file will be created. | ||
46 | InMemory bool | ||
47 | |||
48 | // Compress enabled content compression before upload. | ||
49 | // Compression will typically reduce memory and network usage, | ||
50 | // Compression can safely be enabled with MinIO hosts. | ||
51 | Compress bool | ||
52 | |||
53 | // SkipErrs if enabled will skip any errors while reading the | ||
54 | // object content while creating the snowball archive | ||
55 | SkipErrs bool | ||
56 | } | ||
57 | |||
58 | // SnowballObject contains information about a single object to be added to the snowball. | ||
59 | type SnowballObject struct { | ||
60 | // Key is the destination key, including prefix. | ||
61 | Key string | ||
62 | |||
63 | // Size is the content size of this object. | ||
64 | Size int64 | ||
65 | |||
66 | // Modtime to apply to the object. | ||
67 | // If Modtime is the zero value current time will be used. | ||
68 | ModTime time.Time | ||
69 | |||
70 | // Content of the object. | ||
71 | // Exactly 'Size' number of bytes must be provided. | ||
72 | Content io.Reader | ||
73 | |||
74 | // VersionID of the object; if empty, a new versionID will be generated | ||
75 | VersionID string | ||
76 | |||
77 | // Headers contains more options for this object upload, the same as you | ||
78 | // would include in a regular PutObject operation, such as user metadata | ||
79 | // and content-disposition, expires, .. | ||
80 | Headers http.Header | ||
81 | |||
82 | // Close will be called when an object has finished processing. | ||
83 | // Note that if PutObjectsSnowball returns because of an error, | ||
84 | // objects not consumed from the input will NOT have been closed. | ||
85 | // Leave as nil for no callback. | ||
86 | Close func() | ||
87 | } | ||
88 | |||
89 | type nopReadSeekCloser struct { | ||
90 | io.ReadSeeker | ||
91 | } | ||
92 | |||
93 | func (n nopReadSeekCloser) Close() error { | ||
94 | return nil | ||
95 | } | ||
96 | |||
97 | // This is available as io.ReadSeekCloser from go1.16 | ||
98 | type readSeekCloser interface { | ||
99 | io.Reader | ||
100 | io.Closer | ||
101 | io.Seeker | ||
102 | } | ||
103 | |||
104 | // PutObjectsSnowball will put multiple objects with a single put call. | ||
105 | // A (compressed) TAR file will be created which will contain multiple objects. | ||
106 | // The key for each object will be used for the destination in the specified bucket. | ||
107 | // Total size should be < 5TB. | ||
108 | // This function blocks until 'objs' is closed and the content has been uploaded. | ||
109 | func (c Client) PutObjectsSnowball(ctx context.Context, bucketName string, opts SnowballOptions, objs <-chan SnowballObject) (err error) { | ||
110 | err = opts.Opts.validate() | ||
111 | if err != nil { | ||
112 | return err | ||
113 | } | ||
114 | var tmpWriter io.Writer | ||
115 | var getTmpReader func() (rc readSeekCloser, sz int64, err error) | ||
116 | if opts.InMemory { | ||
117 | b := bytes.NewBuffer(nil) | ||
118 | tmpWriter = b | ||
119 | getTmpReader = func() (readSeekCloser, int64, error) { | ||
120 | return nopReadSeekCloser{bytes.NewReader(b.Bytes())}, int64(b.Len()), nil | ||
121 | } | ||
122 | } else { | ||
123 | f, err := os.CreateTemp("", "s3-putsnowballobjects-*") | ||
124 | if err != nil { | ||
125 | return err | ||
126 | } | ||
127 | name := f.Name() | ||
128 | tmpWriter = f | ||
129 | var once sync.Once | ||
130 | defer once.Do(func() { | ||
131 | f.Close() | ||
132 | }) | ||
133 | defer os.Remove(name) | ||
134 | getTmpReader = func() (readSeekCloser, int64, error) { | ||
135 | once.Do(func() { | ||
136 | f.Close() | ||
137 | }) | ||
138 | f, err := os.Open(name) | ||
139 | if err != nil { | ||
140 | return nil, 0, err | ||
141 | } | ||
142 | st, err := f.Stat() | ||
143 | if err != nil { | ||
144 | return nil, 0, err | ||
145 | } | ||
146 | return f, st.Size(), nil | ||
147 | } | ||
148 | } | ||
149 | flush := func() error { return nil } | ||
150 | if !opts.Compress { | ||
151 | if !opts.InMemory { | ||
152 | // Insert buffer for writes. | ||
153 | buf := bufio.NewWriterSize(tmpWriter, 1<<20) | ||
154 | flush = buf.Flush | ||
155 | tmpWriter = buf | ||
156 | } | ||
157 | } else { | ||
158 | s2c := s2.NewWriter(tmpWriter, s2.WriterBetterCompression()) | ||
159 | flush = s2c.Close | ||
160 | defer s2c.Close() | ||
161 | tmpWriter = s2c | ||
162 | } | ||
163 | t := tar.NewWriter(tmpWriter) | ||
164 | |||
165 | objectLoop: | ||
166 | for { | ||
167 | select { | ||
168 | case <-ctx.Done(): | ||
169 | return ctx.Err() | ||
170 | case obj, ok := <-objs: | ||
171 | if !ok { | ||
172 | break objectLoop | ||
173 | } | ||
174 | |||
175 | closeObj := func() {} | ||
176 | if obj.Close != nil { | ||
177 | closeObj = obj.Close | ||
178 | } | ||
179 | |||
180 | // Trim accidental slash prefix. | ||
181 | obj.Key = strings.TrimPrefix(obj.Key, "/") | ||
182 | header := tar.Header{ | ||
183 | Typeflag: tar.TypeReg, | ||
184 | Name: obj.Key, | ||
185 | Size: obj.Size, | ||
186 | ModTime: obj.ModTime, | ||
187 | Format: tar.FormatPAX, | ||
188 | } | ||
189 | if header.ModTime.IsZero() { | ||
190 | header.ModTime = time.Now().UTC() | ||
191 | } | ||
192 | |||
193 | header.PAXRecords = make(map[string]string) | ||
194 | if obj.VersionID != "" { | ||
195 | header.PAXRecords["minio.versionId"] = obj.VersionID | ||
196 | } | ||
197 | for k, vals := range obj.Headers { | ||
198 | header.PAXRecords["minio.metadata."+k] = strings.Join(vals, ",") | ||
199 | } | ||
200 | |||
201 | if err := t.WriteHeader(&header); err != nil { | ||
202 | closeObj() | ||
203 | return err | ||
204 | } | ||
205 | n, err := io.Copy(t, obj.Content) | ||
206 | if err != nil { | ||
207 | closeObj() | ||
208 | if opts.SkipErrs { | ||
209 | continue | ||
210 | } | ||
211 | return err | ||
212 | } | ||
213 | if n != obj.Size { | ||
214 | closeObj() | ||
215 | if opts.SkipErrs { | ||
216 | continue | ||
217 | } | ||
218 | return io.ErrUnexpectedEOF | ||
219 | } | ||
220 | closeObj() | ||
221 | } | ||
222 | } | ||
223 | // Flush tar | ||
224 | err = t.Flush() | ||
225 | if err != nil { | ||
226 | return err | ||
227 | } | ||
228 | // Flush compression | ||
229 | err = flush() | ||
230 | if err != nil { | ||
231 | return err | ||
232 | } | ||
233 | if opts.Opts.UserMetadata == nil { | ||
234 | opts.Opts.UserMetadata = map[string]string{} | ||
235 | } | ||
236 | opts.Opts.UserMetadata["X-Amz-Meta-Snowball-Auto-Extract"] = "true" | ||
237 | opts.Opts.DisableMultipart = true | ||
238 | rc, sz, err := getTmpReader() | ||
239 | if err != nil { | ||
240 | return err | ||
241 | } | ||
242 | defer rc.Close() | ||
243 | rand := c.random.Uint64() | ||
244 | _, err = c.PutObject(ctx, bucketName, fmt.Sprintf("snowball-upload-%x.tar", rand), rc, sz, opts.Opts) | ||
245 | return err | ||
246 | } | ||