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  }