github.com/etecs-ru/gnomock@v0.13.2/preset/localstack/options_s3.go (about) 1 package localstack 2 3 import ( 4 "fmt" 5 "io/ioutil" 6 "os" 7 "path" 8 "path/filepath" 9 10 "github.com/aws/aws-sdk-go/aws" 11 "github.com/aws/aws-sdk-go/aws/credentials" 12 "github.com/aws/aws-sdk-go/aws/session" 13 "github.com/aws/aws-sdk-go/service/s3" 14 "github.com/etecs-ru/gnomock" 15 ) 16 17 // WithS3Files sets up S3 service running in localstack with the contents of 18 // `path` directory. The first level children of `path` must be directories, 19 // their names will be used to create buckets. Below them, all the files in any 20 // other directories, these files will be uploaded as-is. 21 // 22 // For example, if you put your test files in testdata/my-bucket/dir/, Gnomock 23 // will create "my-bucket" for you, and pull "dir" with all its contents into 24 // this bucket. 25 // 26 // This function does nothing if you don't provide localstack.S3 as one of the 27 // services in WithServices 28 func WithS3Files(path string) Option { 29 return func(p *P) { 30 p.S3Path = path 31 } 32 } 33 34 func (p *P) initS3(c *gnomock.Container) error { 35 if p.S3Path == "" { 36 return nil 37 } 38 39 s3Endpoint := fmt.Sprintf("http://%s/", c.Address(APIPort)) 40 config := &aws.Config{ 41 Region: aws.String("us-east-1"), 42 Endpoint: aws.String(s3Endpoint), 43 S3ForcePathStyle: aws.Bool(true), 44 Credentials: credentials.NewStaticCredentials("a", "b", "c"), 45 } 46 47 sess, err := session.NewSession(config) 48 if err != nil { 49 return fmt.Errorf("can't create s3 session: %w", err) 50 } 51 52 svc := s3.New(sess) 53 54 buckets, err := p.createBuckets(svc) 55 if err != nil { 56 return fmt.Errorf("can't create buckets: %w", err) 57 } 58 59 err = p.uploadFiles(svc, buckets) 60 if err != nil { 61 return err 62 } 63 64 return nil 65 } 66 67 func (p *P) createBuckets(svc *s3.S3) ([]string, error) { 68 files, err := ioutil.ReadDir(p.S3Path) 69 if err != nil { 70 return nil, fmt.Errorf("can't read s3 initial files: %w", err) 71 } 72 73 buckets := []string{} 74 75 // create buckets from top-level folders under `path` 76 for _, f := range files { 77 if !f.IsDir() { 78 continue 79 } 80 81 bucket := f.Name() 82 83 err := p.createBucket(svc, bucket) 84 if err != nil { 85 return nil, fmt.Errorf("can't create bucket '%s': %w", bucket, err) 86 } 87 88 buckets = append(buckets, bucket) 89 } 90 91 return buckets, nil 92 } 93 94 func (p *P) createBucket(svc *s3.S3, bucket string) error { 95 input := &s3.CreateBucketInput{Bucket: aws.String(bucket)} 96 97 _, err := svc.CreateBucket(input) 98 if err != nil { 99 return fmt.Errorf("can't create bucket '%s': %w", bucket, err) 100 } 101 102 return nil 103 } 104 105 func (p *P) uploadFiles(svc *s3.S3, buckets []string) error { 106 for _, bucket := range buckets { 107 bucket := bucket 108 109 err := filepath.Walk( 110 path.Join(p.S3Path, bucket), 111 func(fPath string, file os.FileInfo, err error) error { 112 if err != nil { 113 return fmt.Errorf("error reading input file '%s': %w", fPath, err) 114 } 115 116 if file.IsDir() { 117 return nil 118 } 119 120 err = p.uploadFile(svc, bucket, fPath) 121 if err != nil { 122 return err 123 } 124 125 return nil 126 }, 127 ) 128 if err != nil { 129 return fmt.Errorf("error uploading input dir: %w", err) 130 } 131 } 132 133 return nil 134 } 135 136 func (p *P) uploadFile(svc *s3.S3, bucket, file string) (err error) { 137 inputFile, err := os.Open(file) //nolint:gosec 138 if err != nil { 139 return fmt.Errorf("can't open file '%s': %w", file, err) 140 } 141 142 defer func() { 143 closeErr := inputFile.Close() 144 if err == nil && closeErr != nil { 145 err = closeErr 146 } 147 }() 148 149 localPath := path.Join(p.S3Path, bucket) 150 key := file[len(localPath):] 151 152 input := &s3.PutObjectInput{ 153 Bucket: aws.String(bucket), 154 Key: aws.String(key), 155 Body: inputFile, 156 } 157 158 _, err = svc.PutObject(input) 159 if err != nil { 160 return fmt.Errorf("can't upload file '%s' to bucket '%s': %w", file, bucket, err) 161 } 162 163 return nil 164 }