Skip to content

Commit 52ee432

Browse files
committed
Download function added
1 parent 002cbcb commit 52ee432

File tree

4 files changed

+90
-0
lines changed

4 files changed

+90
-0
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,5 +14,6 @@ require (
1414
github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
1515
github.com/jmespath/go-jmespath v0.4.0 // indirect
1616
github.com/russross/blackfriday/v2 v2.1.0 // indirect
17+
golang.org/x/sync v0.6.0
1718
golang.org/x/sys v0.1.0 // indirect
1819
)

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ golang.org/x/net v0.1.0 h1:hZ/3BUoy5aId7sCpA/Tc5lt8DkFgdVS2onTpJsZ/fl0=
4040
golang.org/x/net v0.1.0/go.mod h1:Cx3nUiGt4eDBEyega/BKRp+/AlGL8hYe7U9odMt2Cco=
4141
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
4242
golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
43+
golang.org/x/sync v0.6.0 h1:5BMeUDZ7vkXGfEr1x9B4bRcTH4lpkTkpdh0T/J+qjbQ=
44+
golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
4345
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
4446
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
4547
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

main.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,11 @@ func main() {
9494
Usage: "server-side encryption algorithm, defaults to none",
9595
EnvVar: "PLUGIN_ENCRYPTION",
9696
},
97+
cli.BoolFlag{
98+
Name: "download",
99+
Usage: "switch to download mode, which will fetch `target`'s files from s3 bucket and place them according to `strip-prefix`",
100+
EnvVar: "PLUGIN_DOWNLOAD",
101+
},
97102
cli.BoolFlag{
98103
Name: "dry-run",
99104
Usage: "dry run for debug purposes",
@@ -164,6 +169,7 @@ func run(c *cli.Context) error {
164169
Exclude: c.StringSlice("exclude"),
165170
Encryption: c.String("encryption"),
166171
ContentType: c.Generic("content-type").(*StringMapFlag).Get(),
172+
Download: c.Bool("download"),
167173
ContentEncoding: c.Generic("content-encoding").(*StringMapFlag).Get(),
168174
CacheControl: c.Generic("cache-control").(*StringMapFlag).Get(),
169175
StorageClass: c.String("storage-class"),

plugin.go

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package main
22

33
import (
4+
"io"
45
"mime"
56
"os"
67
"path/filepath"
@@ -16,6 +17,7 @@ import (
1617
"github.com/aws/aws-sdk-go/service/sts"
1718
"github.com/mattn/go-zglob"
1819
log "github.com/sirupsen/logrus"
20+
"golang.org/x/sync/errgroup"
1921
)
2022

2123
// Plugin defines the S3 plugin parameters.
@@ -44,6 +46,9 @@ type Plugin struct {
4446
// sa-east-1
4547
Region string
4648

49+
// if true, plugin is set to download mode, which means `target` from the bucket will be downloaded
50+
Download bool
51+
4752
// Indicates the files ACL, which should be one
4853
// of the following:
4954
// private
@@ -135,6 +140,77 @@ func (p *Plugin) Exec() error {
135140
client = s3.New(sess)
136141
}
137142

143+
if p.Download {
144+
targetDir := strings.TrimPrefix(filepath.ToSlash(p.Target), "/")
145+
log.WithFields(log.Fields{
146+
"bucket": p.Bucket,
147+
"dir": targetDir,
148+
}).Info("Listing S3 directory")
149+
150+
list, err := client.ListObjectsV2(&s3.ListObjectsV2Input{
151+
Bucket: &p.Bucket,
152+
Prefix: &targetDir,
153+
})
154+
if err != nil {
155+
log.WithFields(log.Fields{
156+
"error": err,
157+
"bucket": p.Bucket,
158+
"dir": targetDir,
159+
}).Error("Cannot list S3 directory")
160+
return err
161+
}
162+
163+
g := errgroup.Group{}
164+
165+
for _, item := range list.Contents {
166+
log.WithFields(log.Fields{
167+
"bucket": p.Bucket,
168+
"key": *item.Key,
169+
}).Info("Getting S3 object")
170+
171+
item := item
172+
g.Go(func() error {
173+
obj, err := client.GetObject(&s3.GetObjectInput{
174+
Bucket: &p.Bucket,
175+
Key: item.Key,
176+
})
177+
if err != nil {
178+
log.WithFields(log.Fields{
179+
"error": err,
180+
"bucket": p.Bucket,
181+
"key": *item.Key,
182+
}).Error("Cannot get S3 object")
183+
return err
184+
}
185+
186+
source := resolveSource(targetDir, *item.Key, p.StripPrefix)
187+
188+
f, err := os.Create(source)
189+
if err != nil {
190+
log.WithFields(log.Fields{
191+
"error": err,
192+
"file": source,
193+
}).Error("Problem opening file for writing")
194+
return err
195+
}
196+
defer f.Close()
197+
198+
_, err = io.Copy(f, obj.Body)
199+
if err != nil {
200+
log.WithFields(log.Fields{
201+
"error": err,
202+
"file": source,
203+
}).Error("Failed to write file")
204+
return err
205+
}
206+
207+
return nil
208+
})
209+
}
210+
211+
return g.Wait()
212+
}
213+
138214
// find the bucket
139215
log.WithFields(log.Fields{
140216
"region": p.Region,
@@ -322,6 +398,11 @@ func resolveKey(target, srcPath, stripPrefix string) string {
322398
return key
323399
}
324400

401+
func resolveSource(targetDir, target, stripPrefix string) string {
402+
path := strings.TrimPrefix(strings.TrimPrefix(target, targetDir), "/")
403+
return stripPrefix + path
404+
}
405+
325406
// checks if the source path is a dir
326407
func isDir(source string, matches []string) bool {
327408
stat, err := os.Stat(source)

0 commit comments

Comments
 (0)