Skip to content

Commit fbde487

Browse files
committed
Changes and fixes
1 parent 56cdc0b commit fbde487

File tree

4 files changed

+133
-106
lines changed

4 files changed

+133
-106
lines changed

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ require (
1313
require (
1414
github.com/cpuguy83/go-md2man/v2 v2.0.1 // indirect
1515
github.com/jmespath/go-jmespath v0.4.0 // indirect
16+
github.com/pkg/errors v0.9.1
1617
github.com/russross/blackfriday/v2 v2.1.0 // indirect
1718
golang.org/x/sync v0.6.0
1819
golang.org/x/sys v0.1.0 // indirect

go.sum

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ github.com/joho/godotenv v1.4.0 h1:3l4+N6zfMWnkbPEXKng2o2/MR5mSwTrBih4ZEkkz1lg=
1515
github.com/joho/godotenv v1.4.0/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
1616
github.com/mattn/go-zglob v0.0.4 h1:LQi2iOm0/fGgu80AioIJ/1j9w9Oh+9DZ39J4VAGzHQM=
1717
github.com/mattn/go-zglob v0.0.4/go.mod h1:MxxjyoXXnMxfIpxTK2GAkw1w8glPsQILx3N5wrKakiY=
18+
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
1819
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
1920
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
2021
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=

plugin.go

Lines changed: 131 additions & 71 deletions
Original file line numberDiff line numberDiff line change
@@ -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
103104
func (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
}

plugin_windows_test.go

Lines changed: 0 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -59,41 +59,6 @@ func TestResolveWinKey(t *testing.T) {
5959
}
6060
}
6161

62-
func TestResolveDir(t *testing.T) {
63-
tests := []struct {
64-
input string
65-
expected string
66-
}{
67-
{
68-
input: "example-string",
69-
expected: "example-string",
70-
},
71-
{
72-
input: "/path/to/file",
73-
expected: "path/to/file",
74-
},
75-
{
76-
input: "12345",
77-
expected: "12345",
78-
},
79-
{
80-
input: "/root/directory",
81-
expected: "root/directory",
82-
},
83-
{
84-
input: "no_slash",
85-
expected: "no_slash",
86-
},
87-
}
88-
89-
for _, tc := range tests {
90-
result := resolveDir(tc.input)
91-
if result != tc.expected {
92-
t.Errorf("Expected: %s, Got: %s", tc.expected, result)
93-
}
94-
}
95-
}
96-
9762
func TestNormalizePath(t *testing.T) {
9863
tests := []struct {
9964
input string

0 commit comments

Comments
 (0)