1
1
package downloader
2
2
3
3
import (
4
+ "bytes"
4
5
"crypto/sha256"
5
6
"errors"
6
7
"fmt"
7
8
"io"
8
9
"net/http"
9
10
"os"
11
+ "os/exec"
12
+ "path"
10
13
"path/filepath"
11
14
"strings"
12
15
"time"
@@ -38,6 +41,7 @@ type Result struct {
38
41
39
42
type options struct {
40
43
cacheDir string // default: empty (disables caching)
44
+ decompress bool // default: false (keep compression)
41
45
description string // default: url
42
46
expectedDigest digest.Digest
43
47
}
@@ -73,6 +77,14 @@ func WithDescription(description string) Opt {
73
77
}
74
78
}
75
79
80
+ // WithDecompress decompress the download from the cache.
81
+ func WithDecompress (decompress bool ) Opt {
82
+ return func (o * options ) error {
83
+ o .decompress = decompress
84
+ return nil
85
+ }
86
+ }
87
+
76
88
// WithExpectedDigest is used to validate the downloaded file against the expected digest.
77
89
//
78
90
// The digest is not verified in the following cases:
@@ -142,8 +154,9 @@ func Download(local, remote string, opts ...Opt) (*Result, error) {
142
154
}
143
155
}
144
156
157
+ ext := path .Ext (remote )
145
158
if IsLocal (remote ) {
146
- if err := copyLocal (localPath , remote , o .description , o .expectedDigest ); err != nil {
159
+ if err := copyLocal (localPath , remote , ext , o . decompress , o .description , o .expectedDigest ); err != nil {
147
160
return nil , err
148
161
}
149
162
res := & Result {
@@ -183,11 +196,11 @@ func Download(local, remote string, opts ...Opt) (*Result, error) {
183
196
if o .expectedDigest .String () != shadDigestS {
184
197
return nil , fmt .Errorf ("expected digest %q does not match the cached digest %q" , o .expectedDigest .String (), shadDigestS )
185
198
}
186
- if err := copyLocal (localPath , shadData , "" , "" ); err != nil {
199
+ if err := copyLocal (localPath , shadData , ext , o . decompress , "" , "" ); err != nil {
187
200
return nil , err
188
201
}
189
202
} else {
190
- if err := copyLocal (localPath , shadData , o .description , o .expectedDigest ); err != nil {
203
+ if err := copyLocal (localPath , shadData , ext , o . decompress , o .description , o .expectedDigest ); err != nil {
191
204
return nil , err
192
205
}
193
206
}
@@ -212,7 +225,7 @@ func Download(local, remote string, opts ...Opt) (*Result, error) {
212
225
return nil , err
213
226
}
214
227
// no need to pass the digest to copyLocal(), as we already verified the digest
215
- if err := copyLocal (localPath , shadData , "" , "" ); err != nil {
228
+ if err := copyLocal (localPath , shadData , ext , o . decompress , "" , "" ); err != nil {
216
229
return nil , err
217
230
}
218
231
if shadDigest != "" && o .expectedDigest != "" {
@@ -253,7 +266,7 @@ func canonicalLocalPath(s string) (string, error) {
253
266
return localpathutil .Expand (s )
254
267
}
255
268
256
- func copyLocal (dst , src string , description string , expectedDigest digest.Digest ) error {
269
+ func copyLocal (dst , src , ext string , decompress bool , description string , expectedDigest digest.Digest ) error {
257
270
srcPath , err := canonicalLocalPath (src )
258
271
if err != nil {
259
272
return err
@@ -274,9 +287,60 @@ func copyLocal(dst, src string, description string, expectedDigest digest.Digest
274
287
if description != "" {
275
288
// TODO: progress bar for copy
276
289
}
290
+ if _ , ok := Decompressor (ext ); ok && decompress {
291
+ return decompressLocal (dstPath , srcPath , ext )
292
+ }
277
293
return fs .CopyFile (dstPath , srcPath )
278
294
}
279
295
296
+ func Decompressor (ext string ) ([]string , bool ) {
297
+ var program string
298
+ switch ext {
299
+ case ".gz" :
300
+ program = "gzip"
301
+ case ".bz2" :
302
+ program = "bzip2"
303
+ case ".xz" :
304
+ program = "xz"
305
+ case ".zst" :
306
+ program = "zstd"
307
+ default :
308
+ return nil , false
309
+ }
310
+ // -d --decompress
311
+ return []string {program , "-d" }, true
312
+ }
313
+
314
+ func decompressLocal (dst , src , ext string ) error {
315
+ command , found := Decompressor (ext )
316
+ if ! found {
317
+ return fmt .Errorf ("decompressLocal: unknown extension %s" , ext )
318
+ }
319
+ logrus .Infof ("decompressing %s with %v" , ext , command )
320
+ in , err := os .Open (src )
321
+ if err != nil {
322
+ return err
323
+ }
324
+ defer in .Close ()
325
+ out , err := os .OpenFile (dst , os .O_CREATE | os .O_WRONLY , 0644 )
326
+ if err != nil {
327
+ return err
328
+ }
329
+ defer out .Close ()
330
+ buf := new (bytes.Buffer )
331
+ cmd := exec .Command (command [0 ], command [1 :]... )
332
+ cmd .Stdin = in
333
+ cmd .Stdout = out
334
+ cmd .Stderr = buf
335
+ err = cmd .Run ()
336
+ if err != nil {
337
+ if ee , ok := err .(* exec.ExitError ); ok {
338
+ ee .Stderr = buf .Bytes ()
339
+ }
340
+ }
341
+ return err
342
+ }
343
+
280
344
func validateLocalFileDigest (localPath string , expectedDigest digest.Digest ) error {
281
345
if localPath == "" {
282
346
return fmt .Errorf ("validateLocalFileDigest: got empty localPath" )
0 commit comments