github.com/terraform-modules-krish/terratest@v0.29.0/modules/aws/s3.go (about)

     1  package aws
     2  
     3  import (
     4  	"bytes"
     5  	"strings"
     6  
     7  	"github.com/aws/aws-sdk-go/aws"
     8  	"github.com/aws/aws-sdk-go/service/s3"
     9  	"github.com/aws/aws-sdk-go/service/s3/s3manager"
    10  	"github.com/terraform-modules-krish/terratest/modules/logger"
    11  	"github.com/terraform-modules-krish/terratest/modules/testing"
    12  	"github.com/stretchr/testify/require"
    13  )
    14  
    15  // FindS3BucketWithTag finds the name of the S3 bucket in the given region with the given tag key=value.
    16  func FindS3BucketWithTag(t testing.TestingT, awsRegion string, key string, value string) string {
    17  	bucket, err := FindS3BucketWithTagE(t, awsRegion, key, value)
    18  	require.NoError(t, err)
    19  
    20  	return bucket
    21  }
    22  
    23  // FindS3BucketWithTagE finds the name of the S3 bucket in the given region with the given tag key=value.
    24  func FindS3BucketWithTagE(t testing.TestingT, awsRegion string, key string, value string) (string, error) {
    25  	s3Client, err := NewS3ClientE(t, awsRegion)
    26  	if err != nil {
    27  		return "", err
    28  	}
    29  
    30  	resp, err := s3Client.ListBuckets(&s3.ListBucketsInput{})
    31  	if err != nil {
    32  		return "", err
    33  	}
    34  
    35  	for _, bucket := range resp.Buckets {
    36  		tagResponse, err := s3Client.GetBucketTagging(&s3.GetBucketTaggingInput{Bucket: bucket.Name})
    37  
    38  		if err != nil {
    39  			if !strings.Contains(err.Error(), "AuthorizationHeaderMalformed") &&
    40  				!strings.Contains(err.Error(), "BucketRegionError") &&
    41  				!strings.Contains(err.Error(), "NoSuchTagSet") {
    42  				return "", err
    43  			}
    44  		}
    45  
    46  		for _, tag := range tagResponse.TagSet {
    47  			if *tag.Key == key && *tag.Value == value {
    48  				logger.Logf(t, "Found S3 bucket %s with tag %s=%s", *bucket.Name, key, value)
    49  				return *bucket.Name, nil
    50  			}
    51  		}
    52  	}
    53  
    54  	return "", nil
    55  }
    56  
    57  // GetS3ObjectContents fetches the contents of the object in the given bucket with the given key and return it as a string.
    58  func GetS3ObjectContents(t testing.TestingT, awsRegion string, bucket string, key string) string {
    59  	contents, err := GetS3ObjectContentsE(t, awsRegion, bucket, key)
    60  	require.NoError(t, err)
    61  
    62  	return contents
    63  }
    64  
    65  // GetS3ObjectContentsE fetches the contents of the object in the given bucket with the given key and return it as a string.
    66  func GetS3ObjectContentsE(t testing.TestingT, awsRegion string, bucket string, key string) (string, error) {
    67  	s3Client, err := NewS3ClientE(t, awsRegion)
    68  	if err != nil {
    69  		return "", err
    70  	}
    71  
    72  	res, err := s3Client.GetObject(&s3.GetObjectInput{
    73  		Bucket: &bucket,
    74  		Key:    &key,
    75  	})
    76  
    77  	if err != nil {
    78  		return "", err
    79  	}
    80  
    81  	buf := new(bytes.Buffer)
    82  	_, err = buf.ReadFrom(res.Body)
    83  	if err != nil {
    84  		return "", err
    85  	}
    86  
    87  	contents := buf.String()
    88  	logger.Logf(t, "Read contents from s3://%s/%s", bucket, key)
    89  
    90  	return contents, nil
    91  }
    92  
    93  // CreateS3Bucket creates an S3 bucket in the given region with the given name. Note that S3 bucket names must be globally unique.
    94  func CreateS3Bucket(t testing.TestingT, region string, name string) {
    95  	err := CreateS3BucketE(t, region, name)
    96  	require.NoError(t, err)
    97  }
    98  
    99  // CreateS3BucketE creates an S3 bucket in the given region with the given name. Note that S3 bucket names must be globally unique.
   100  func CreateS3BucketE(t testing.TestingT, region string, name string) error {
   101  	logger.Logf(t, "Creating bucket %s in %s", name, region)
   102  
   103  	s3Client, err := NewS3ClientE(t, region)
   104  	if err != nil {
   105  		return err
   106  	}
   107  
   108  	params := &s3.CreateBucketInput{
   109  		Bucket: aws.String(name),
   110  	}
   111  	_, err = s3Client.CreateBucket(params)
   112  	return err
   113  }
   114  
   115  // PutS3BucketPolicy applies an IAM resource policy to a given S3 bucket to create it's bucket policy
   116  func PutS3BucketPolicy(t testing.TestingT, region string, bucketName string, policyJSONString string) {
   117  	err := PutS3BucketPolicyE(t, region, bucketName, policyJSONString)
   118  	require.NoError(t, err)
   119  }
   120  
   121  // PutS3BucketPolicyE applies an IAM resource policy to a given S3 bucket to create it's bucket policy
   122  func PutS3BucketPolicyE(t testing.TestingT, region string, bucketName string, policyJSONString string) error {
   123  	logger.Logf(t, "Applying bucket policy for bucket %s in %s", bucketName, region)
   124  
   125  	s3Client, err := NewS3ClientE(t, region)
   126  	if err != nil {
   127  		return err
   128  	}
   129  
   130  	input := &s3.PutBucketPolicyInput{
   131  		Bucket: aws.String(bucketName),
   132  		Policy: aws.String(policyJSONString),
   133  	}
   134  
   135  	_, err = s3Client.PutBucketPolicy(input)
   136  	return err
   137  }
   138  
   139  // PutS3BucketVersioning creates an S3 bucket versioning configuration in the given region against the given bucket name, WITHOUT requiring MFA to remove versioning.
   140  func PutS3BucketVersioning(t testing.TestingT, region string, bucketName string) {
   141  	err := PutS3BucketVersioningE(t, region, bucketName)
   142  	require.NoError(t, err)
   143  }
   144  
   145  // PutS3BucketVersioningE creates an S3 bucket versioning configuration in the given region against the given bucket name, WITHOUT requiring MFA to remove versioning.
   146  func PutS3BucketVersioningE(t testing.TestingT, region string, bucketName string) error {
   147  	logger.Logf(t, "Creating bucket versioning configuration for bucket %s in %s", bucketName, region)
   148  
   149  	s3Client, err := NewS3ClientE(t, region)
   150  	if err != nil {
   151  		return err
   152  	}
   153  
   154  	input := &s3.PutBucketVersioningInput{
   155  		Bucket: aws.String(bucketName),
   156  		VersioningConfiguration: &s3.VersioningConfiguration{
   157  			MFADelete: aws.String("Disabled"),
   158  			Status:    aws.String("Enabled"),
   159  		},
   160  	}
   161  
   162  	_, err = s3Client.PutBucketVersioning(input)
   163  	return err
   164  }
   165  
   166  // DeleteS3Bucket destroys the S3 bucket in the given region with the given name.
   167  func DeleteS3Bucket(t testing.TestingT, region string, name string) {
   168  	err := DeleteS3BucketE(t, region, name)
   169  	require.NoError(t, err)
   170  }
   171  
   172  // DeleteS3BucketE destroys the S3 bucket in the given region with the given name.
   173  func DeleteS3BucketE(t testing.TestingT, region string, name string) error {
   174  	logger.Logf(t, "Deleting bucket %s in %s", region, name)
   175  
   176  	s3Client, err := NewS3ClientE(t, region)
   177  	if err != nil {
   178  		return err
   179  	}
   180  
   181  	params := &s3.DeleteBucketInput{
   182  		Bucket: aws.String(name),
   183  	}
   184  	_, err = s3Client.DeleteBucket(params)
   185  	return err
   186  }
   187  
   188  // EmptyS3Bucket removes the contents of an S3 bucket in the given region with the given name.
   189  func EmptyS3Bucket(t testing.TestingT, region string, name string) {
   190  	err := EmptyS3BucketE(t, region, name)
   191  	require.NoError(t, err)
   192  }
   193  
   194  // EmptyS3BucketE removes the contents of an S3 bucket in the given region with the given name.
   195  func EmptyS3BucketE(t testing.TestingT, region string, name string) error {
   196  	logger.Logf(t, "Emptying bucket %s in %s", name, region)
   197  
   198  	s3Client, err := NewS3ClientE(t, region)
   199  	if err != nil {
   200  		return err
   201  	}
   202  
   203  	params := &s3.ListObjectVersionsInput{
   204  		Bucket: aws.String(name),
   205  	}
   206  
   207  	for {
   208  		// Requesting a batch of objects from s3 bucket
   209  		bucketObjects, err := s3Client.ListObjectVersions(params)
   210  		if err != nil {
   211  			return err
   212  		}
   213  
   214  		//Checks if the bucket is already empty
   215  		if len((*bucketObjects).Versions) == 0 {
   216  			logger.Logf(t, "Bucket %s is already empty", name)
   217  			return nil
   218  		}
   219  
   220  		//creating an array of pointers of ObjectIdentifier
   221  		objectsToDelete := make([]*s3.ObjectIdentifier, 0, 1000)
   222  		for _, object := range (*bucketObjects).Versions {
   223  			obj := s3.ObjectIdentifier{
   224  				Key:       object.Key,
   225  				VersionId: object.VersionId,
   226  			}
   227  			objectsToDelete = append(objectsToDelete, &obj)
   228  		}
   229  
   230  		for _, object := range (*bucketObjects).DeleteMarkers {
   231  			obj := s3.ObjectIdentifier{
   232  				Key:       object.Key,
   233  				VersionId: object.VersionId,
   234  			}
   235  			objectsToDelete = append(objectsToDelete, &obj)
   236  		}
   237  
   238  		//Creating JSON payload for bulk delete
   239  		deleteArray := s3.Delete{Objects: objectsToDelete}
   240  		deleteParams := &s3.DeleteObjectsInput{
   241  			Bucket: aws.String(name),
   242  			Delete: &deleteArray,
   243  		}
   244  
   245  		//Running the Bulk delete job (limit 1000)
   246  		_, err = s3Client.DeleteObjects(deleteParams)
   247  		if err != nil {
   248  			return err
   249  		}
   250  
   251  		if *(*bucketObjects).IsTruncated { //if there are more objects in the bucket, IsTruncated = true
   252  			// params.Marker = (*deleteParams).Delete.Objects[len((*deleteParams).Delete.Objects)-1].Key
   253  			params.KeyMarker = bucketObjects.NextKeyMarker
   254  			logger.Logf(t, "Requesting next batch | %s", *(params.KeyMarker))
   255  		} else { //if all objects in the bucket have been cleaned up.
   256  			break
   257  		}
   258  	}
   259  	logger.Logf(t, "Bucket %s is now empty", name)
   260  	return err
   261  }
   262  
   263  // GetS3BucketVersioning fetches the given bucket's versioning configuration status and returns it as a string
   264  func GetS3BucketVersioning(t testing.TestingT, awsRegion string, bucket string) string {
   265  	versioningStatus, err := GetS3BucketVersioningE(t, awsRegion, bucket)
   266  	require.NoError(t, err)
   267  
   268  	return versioningStatus
   269  }
   270  
   271  // GetS3BucketVersioningE fetches the given bucket's versioning configuration status and returns it as a string
   272  func GetS3BucketVersioningE(t testing.TestingT, awsRegion string, bucket string) (string, error) {
   273  	s3Client, err := NewS3ClientE(t, awsRegion)
   274  	if err != nil {
   275  		return "", err
   276  	}
   277  
   278  	res, err := s3Client.GetBucketVersioning(&s3.GetBucketVersioningInput{
   279  		Bucket: &bucket,
   280  	})
   281  	if err != nil {
   282  		return "", err
   283  	}
   284  
   285  	return aws.StringValue(res.Status), nil
   286  }
   287  
   288  // GetS3BucketPolicy fetches the given bucket's resource policy and returns it as a string
   289  func GetS3BucketPolicy(t testing.TestingT, awsRegion string, bucket string) string {
   290  	bucketPolicy, err := GetS3BucketPolicyE(t, awsRegion, bucket)
   291  	require.NoError(t, err)
   292  
   293  	return bucketPolicy
   294  }
   295  
   296  // GetS3BucketPolicyE fetches the given bucket's resource policy and returns it as a string
   297  func GetS3BucketPolicyE(t testing.TestingT, awsRegion string, bucket string) (string, error) {
   298  	s3Client, err := NewS3ClientE(t, awsRegion)
   299  	if err != nil {
   300  		return "", err
   301  	}
   302  
   303  	res, err := s3Client.GetBucketPolicy(&s3.GetBucketPolicyInput{
   304  		Bucket: &bucket,
   305  	})
   306  	if err != nil {
   307  		return "", err
   308  	}
   309  
   310  	return aws.StringValue(res.Policy), nil
   311  }
   312  
   313  // AssertS3BucketExists checks if the given S3 bucket exists in the given region and fail the test if it does not.
   314  func AssertS3BucketExists(t testing.TestingT, region string, name string) {
   315  	err := AssertS3BucketExistsE(t, region, name)
   316  	require.NoError(t, err)
   317  }
   318  
   319  // AssertS3BucketExistsE checks if the given S3 bucket exists in the given region and return an error if it does not.
   320  func AssertS3BucketExistsE(t testing.TestingT, region string, name string) error {
   321  	s3Client, err := NewS3ClientE(t, region)
   322  	if err != nil {
   323  		return err
   324  	}
   325  
   326  	params := &s3.HeadBucketInput{
   327  		Bucket: aws.String(name),
   328  	}
   329  	_, err = s3Client.HeadBucket(params)
   330  	return err
   331  }
   332  
   333  // AssertS3BucketVersioningExists checks if the given S3 bucket has a versioning configuration enabled and returns an error if it does not.
   334  func AssertS3BucketVersioningExists(t testing.TestingT, region string, bucketName string) {
   335  	err := AssertS3BucketVersioningExistsE(t, region, bucketName)
   336  	require.NoError(t, err)
   337  }
   338  
   339  // AssertS3BucketVersioningExistsE checks if the given S3 bucket has a versioning configuration enabled and returns an error if it does not.
   340  func AssertS3BucketVersioningExistsE(t testing.TestingT, region string, bucketName string) error {
   341  	status, err := GetS3BucketVersioningE(t, region, bucketName)
   342  	if err != nil {
   343  		return err
   344  	}
   345  
   346  	if status == "Enabled" {
   347  		return nil
   348  	}
   349  	return NewBucketVersioningNotEnabledError(bucketName, region, status)
   350  }
   351  
   352  // AssertS3BucketPolicyExists checks if the given S3 bucket has a resource policy attached and returns an error if it does not
   353  func AssertS3BucketPolicyExists(t testing.TestingT, region string, bucketName string) {
   354  	err := AssertS3BucketPolicyExistsE(t, region, bucketName)
   355  	require.NoError(t, err)
   356  }
   357  
   358  // AssertS3BucketPolicyExistsE checks if the given S3 bucket has a resource policy attached and returns an error if it does not
   359  func AssertS3BucketPolicyExistsE(t testing.TestingT, region string, bucketName string) error {
   360  	policy, err := GetS3BucketPolicyE(t, region, bucketName)
   361  	if err != nil {
   362  		return err
   363  	}
   364  
   365  	if policy == "" {
   366  		return NewNoBucketPolicyError(bucketName, region, policy)
   367  	}
   368  	return nil
   369  }
   370  
   371  // NewS3Client creates an S3 client.
   372  func NewS3Client(t testing.TestingT, region string) *s3.S3 {
   373  	client, err := NewS3ClientE(t, region)
   374  	require.NoError(t, err)
   375  
   376  	return client
   377  }
   378  
   379  // NewS3ClientE creates an S3 client.
   380  func NewS3ClientE(t testing.TestingT, region string) (*s3.S3, error) {
   381  	sess, err := NewAuthenticatedSession(region)
   382  	if err != nil {
   383  		return nil, err
   384  	}
   385  
   386  	return s3.New(sess), nil
   387  }
   388  
   389  // NewS3Uploader creates an S3 Uploader.
   390  func NewS3Uploader(t testing.TestingT, region string) *s3manager.Uploader {
   391  	uploader, err := NewS3UploaderE(t, region)
   392  	require.NoError(t, err)
   393  	return uploader
   394  }
   395  
   396  // NewS3UploaderE creates an S3 Uploader.
   397  func NewS3UploaderE(t testing.TestingT, region string) (*s3manager.Uploader, error) {
   398  	sess, err := NewAuthenticatedSession(region)
   399  	if err != nil {
   400  		return nil, err
   401  	}
   402  
   403  	return s3manager.NewUploader(sess), nil
   404  }