@@ -16,6 +16,7 @@ import (
1616 "github.com/aws/aws-sdk-go/service/s3"
1717 "github.com/aws/aws-sdk-go/service/sts"
1818 "github.com/mattn/go-zglob"
19+ "github.com/pkg/errors"
1920 log "github.com/sirupsen/logrus"
2021)
2122
@@ -101,11 +102,11 @@ type Plugin struct {
101102
102103// Exec runs the plugin
103104func (p * Plugin ) Exec () error {
104- // normalize the target URL
105105 if p .Download {
106106 p .Source = normalizePath (p .Source )
107- } else {
108107 p .Target = normalizePath (p .Target )
108+ } else {
109+ p .Target = strings .TrimPrefix (p .Target , "/" )
109110 }
110111
111112 // create the client
@@ -143,73 +144,12 @@ func (p *Plugin) Exec() error {
143144 client = s3 .New (sess )
144145 }
145146
147+ // If in download mode, call the downloadS3Objects method
146148 if p .Download {
147149 sourceDir := normalizePath (p .Source )
150+ client := p .createS3Client ()
148151
149- log .WithFields (log.Fields {
150- "bucket" : p .Bucket ,
151- "dir" : sourceDir ,
152- }).Info ("Listing S3 directory" )
153-
154- list , err := client .ListObjectsV2 (& s3.ListObjectsV2Input {
155- Bucket : & p .Bucket ,
156- Prefix : & sourceDir ,
157- })
158- if err != nil {
159- log .WithFields (log.Fields {
160- "error" : err ,
161- "bucket" : p .Bucket ,
162- "dir" : sourceDir ,
163- }).Error ("Cannot list S3 directory" )
164- return err
165- }
166-
167- for _ , item := range list .Contents {
168- log .WithFields (log.Fields {
169- "bucket" : p .Bucket ,
170- "key" : * item .Key ,
171- }).Info ("Getting S3 object" )
172-
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- defer obj .Body .Close ()
186-
187- // resolveSource takes a target directory, a target path, and a prefix to strip,
188- // and returns a resolved source path by removing the targetDir from the target
189- // and appending the stripPrefix.
190- target := resolveSource (sourceDir , * item .Key , p .StripPrefix )
191-
192- f , err := os .Create (target )
193- if err != nil {
194- log .WithFields (log.Fields {
195- "error" : err ,
196- "file" : target ,
197- }).Error ("Failed to create file" )
198- return err
199- }
200- defer f .Close ()
201-
202- _ , err = io .Copy (f , obj .Body )
203- if err != nil {
204- log .WithFields (log.Fields {
205- "error" : err ,
206- "file" : target ,
207- }).Error ("Failed to write file" )
208- return err
209- }
210- }
211-
212- return nil
152+ return p .downloadS3Objects (client , sourceDir )
213153 }
214154
215155 // find the bucket
@@ -399,9 +339,9 @@ func resolveKey(target, srcPath, stripPrefix string) string {
399339 return key
400340}
401341
402- func resolveSource (targetDir , target , stripPrefix string ) string {
403- // Remove the leading targetDir from the target path
404- path := strings .TrimPrefix (strings .TrimPrefix (target , targetDir ), "/" )
342+ func resolveSource (sourceDir , source , stripPrefix string ) string {
343+ // Remove the leading sourceDir from the source path
344+ path := strings .TrimPrefix (strings .TrimPrefix (source , sourceDir ), "/" )
405345
406346 // Add the specified stripPrefix to the resulting path
407347 return stripPrefix + path
@@ -429,6 +369,126 @@ func isDir(source string, matches []string) bool {
429369}
430370
431371// normalizePath converts the path to a forward slash format and trims the prefix.
432- func normalizePath (source string ) string {
433- return strings .TrimPrefix (filepath .ToSlash (source ), "/" )
372+ func normalizePath (path string ) string {
373+ return strings .TrimPrefix (filepath .ToSlash (path ), "/" )
374+ }
375+
376+ // downloadS3Object downloads a single object from S3
377+ func (p * Plugin ) downloadS3Object (client * s3.S3 , sourceDir , key , target string ) error {
378+ log .WithFields (log.Fields {
379+ "bucket" : p .Bucket ,
380+ "key" : key ,
381+ }).Info ("Getting S3 object" )
382+
383+ obj , err := client .GetObject (& s3.GetObjectInput {
384+ Bucket : & p .Bucket ,
385+ Key : & key ,
386+ })
387+ if err != nil {
388+ log .WithFields (log.Fields {
389+ "error" : err ,
390+ "bucket" : p .Bucket ,
391+ "key" : key ,
392+ }).Error ("Cannot get S3 object" )
393+ return err
394+ }
395+ defer obj .Body .Close ()
396+
397+ // Create the destination file path
398+ destination := filepath .Join (p .Target , target )
399+ log .Println ("Destination: " , destination )
400+
401+ // Extract the directory from the destination path
402+ dir := filepath .Dir (destination )
403+
404+ // Create the directory and any necessary parent directories
405+ if err := os .MkdirAll (dir , os .ModePerm ); err != nil {
406+ return errors .Wrap (err , "error creating directories" )
407+ }
408+
409+ f , err := os .Create (destination )
410+ if err != nil {
411+ log .WithFields (log.Fields {
412+ "error" : err ,
413+ "file" : destination ,
414+ }).Error ("Failed to create file" )
415+ return err
416+ }
417+ defer f .Close ()
418+
419+ _ , err = io .Copy (f , obj .Body )
420+ if err != nil {
421+ log .WithFields (log.Fields {
422+ "error" : err ,
423+ "file" : destination ,
424+ }).Error ("Failed to write file" )
425+ return err
426+ }
427+
428+ return nil
429+ }
430+
431+ // downloadS3Objects downloads all objects in the specified S3 bucket path
432+ func (p * Plugin ) downloadS3Objects (client * s3.S3 , sourceDir string ) error {
433+ log .WithFields (log.Fields {
434+ "bucket" : p .Bucket ,
435+ "dir" : sourceDir ,
436+ }).Info ("Listing S3 directory" )
437+
438+ list , err := client .ListObjectsV2 (& s3.ListObjectsV2Input {
439+ Bucket : & p .Bucket ,
440+ Prefix : & sourceDir ,
441+ })
442+ if err != nil {
443+ log .WithFields (log.Fields {
444+ "error" : err ,
445+ "bucket" : p .Bucket ,
446+ "dir" : sourceDir ,
447+ }).Error ("Cannot list S3 directory" )
448+ return err
449+ }
450+
451+ for _ , item := range list .Contents {
452+ // resolveSource takes a source directory, a source path, and a prefix to strip,
453+ // and returns a resolved target path by removing the sourceDir from the source
454+ // and appending the stripPrefix.
455+ target := resolveSource (sourceDir , * item .Key , p .StripPrefix )
456+
457+ if err := p .downloadS3Object (client , sourceDir , * item .Key , target ); err != nil {
458+ return err
459+ }
460+ }
461+
462+ return nil
463+ }
464+
465+ // createS3Client creates and returns an S3 client based on the plugin configuration
466+ func (p * Plugin ) createS3Client () * s3.S3 {
467+ conf := & aws.Config {
468+ Region : aws .String (p .Region ),
469+ Endpoint : & p .Endpoint ,
470+ DisableSSL : aws .Bool (strings .HasPrefix (p .Endpoint , "http://" )),
471+ S3ForcePathStyle : aws .Bool (p .PathStyle ),
472+ }
473+
474+ if p .Key != "" && p .Secret != "" {
475+ conf .Credentials = credentials .NewStaticCredentials (p .Key , p .Secret , "" )
476+ } else if p .AssumeRole != "" {
477+ conf .Credentials = assumeRole (p .AssumeRole , p .AssumeRoleSessionName , p .ExternalID )
478+ } else {
479+ log .Warn ("AWS Key and/or Secret not provided (falling back to ec2 instance profile)" )
480+ }
481+
482+ sess , _ := session .NewSession (conf )
483+ client := s3 .New (sess )
484+
485+ if len (p .UserRoleArn ) > 0 {
486+ confRoleArn := aws.Config {
487+ Region : aws .String (p .Region ),
488+ Credentials : stscreds .NewCredentials (sess , p .UserRoleArn ),
489+ }
490+ client = s3 .New (sess , & confRoleArn )
491+ }
492+
493+ return client
434494}
0 commit comments