github.com/mweagle/Sparta@v1.15.0/aws/s3/util.go (about)

     1  package s3
     2  
     3  import (
     4  	"fmt"
     5  	"mime"
     6  	"net/url"
     7  	"os"
     8  	"path"
     9  	"strings"
    10  
    11  	"github.com/aws/aws-sdk-go/aws"
    12  	"github.com/aws/aws-sdk-go/aws/session"
    13  	"github.com/aws/aws-sdk-go/service/s3"
    14  	"github.com/aws/aws-sdk-go/service/s3/s3manager"
    15  	humanize "github.com/dustin/go-humanize"
    16  	"github.com/pkg/errors"
    17  	"github.com/sirupsen/logrus"
    18  )
    19  
    20  // RollbackFunction called in the event of a stack provisioning failure
    21  type RollbackFunction func(logger *logrus.Logger) error
    22  
    23  // CreateS3RollbackFunc creates an S3 rollback function that attempts to delete a previously
    24  // uploaded item. Note that s3ArtifactURL may include a `versionId` query arg
    25  // to denote the specific version to delete.
    26  func CreateS3RollbackFunc(awsSession *session.Session, s3ArtifactURL string) RollbackFunction {
    27  	return func(logger *logrus.Logger) error {
    28  		logger.WithFields(logrus.Fields{
    29  			"URL": s3ArtifactURL,
    30  		}).Info("Deleting S3 object")
    31  		artifactURLParts, artifactURLPartsErr := url.Parse(s3ArtifactURL)
    32  		if nil != artifactURLPartsErr {
    33  			return artifactURLPartsErr
    34  		}
    35  		// Bucket is the first component
    36  		s3Bucket := strings.Split(artifactURLParts.Host, ".")[0]
    37  		s3Client := s3.New(awsSession)
    38  		params := &s3.DeleteObjectInput{
    39  			Bucket: aws.String(s3Bucket),
    40  			Key:    aws.String(artifactURLParts.Path),
    41  		}
    42  		versionID := artifactURLParts.Query().Get("versionId")
    43  		if versionID != "" {
    44  			params.VersionId = aws.String(versionID)
    45  		}
    46  		_, err := s3Client.DeleteObject(params)
    47  		if err != nil {
    48  			logger.WithFields(logrus.Fields{
    49  				"Error": err,
    50  			}).Warn("Failed to delete S3 item during rollback cleanup")
    51  		}
    52  		return err
    53  	}
    54  }
    55  
    56  // UploadLocalFileToS3 takes a local path and uploads the content at localPath
    57  // to the given S3Bucket and KeyPrefix.  The final S3 keyname is the S3KeyPrefix+
    58  // the basename of the localPath.
    59  func UploadLocalFileToS3(localPath string,
    60  	awsSession *session.Session,
    61  	S3Bucket string,
    62  	S3KeyName string,
    63  	logger *logrus.Logger) (string, error) {
    64  
    65  	// Then do the actual work
    66  	/* #nosec */
    67  	reader, err := os.Open(localPath)
    68  	if nil != err {
    69  		return "", fmt.Errorf("failed to open local archive for S3 upload: %s", err.Error())
    70  	}
    71  	uploadInput := &s3manager.UploadInput{
    72  		Bucket:      &S3Bucket,
    73  		Key:         &S3KeyName,
    74  		ContentType: aws.String(mime.TypeByExtension(path.Ext(localPath))),
    75  		Body:        reader,
    76  	}
    77  	// If we can get the current working directory, let's try and strip
    78  	// it from the path just to keep the log statement a bit shorter
    79  	logPath := localPath
    80  	cwd, cwdErr := os.Getwd()
    81  	if cwdErr == nil {
    82  		logPath = strings.TrimPrefix(logPath, cwd)
    83  		if logPath != localPath {
    84  			logPath = fmt.Sprintf(".%s", logPath)
    85  		}
    86  	}
    87  	// Binary size
    88  	stat, err := os.Stat(localPath)
    89  	if err != nil {
    90  		return "", fmt.Errorf("failed to calculate upload size for file: %s", localPath)
    91  	}
    92  	logger.WithFields(logrus.Fields{
    93  		"Path":   logPath,
    94  		"Bucket": S3Bucket,
    95  		"Key":    S3KeyName,
    96  		"Size":   humanize.Bytes(uint64(stat.Size())),
    97  	}).Info("Uploading local file to S3")
    98  
    99  	uploader := s3manager.NewUploader(awsSession)
   100  	result, err := uploader.Upload(uploadInput)
   101  	if nil != err {
   102  		return "", errors.Wrapf(err, "Failed to upload object to S3")
   103  	}
   104  	if result.VersionID != nil {
   105  		logger.WithFields(logrus.Fields{
   106  			"URL":       result.Location,
   107  			"VersionID": string(*result.VersionID),
   108  		}).Debug("S3 upload complete")
   109  	} else {
   110  		logger.WithFields(logrus.Fields{
   111  			"URL": result.Location,
   112  		}).Debug("S3 upload complete")
   113  	}
   114  	locationURL := result.Location
   115  	if nil != result.VersionID {
   116  		// http://docs.aws.amazon.com/AmazonS3/latest/dev/RetrievingObjectVersions.html
   117  		locationURL = fmt.Sprintf("%s?versionId=%s", locationURL, string(*result.VersionID))
   118  	}
   119  	return locationURL, nil
   120  }
   121  
   122  // BucketVersioningEnabled determines if a given S3 bucket has object
   123  // versioning enabled.
   124  func BucketVersioningEnabled(awsSession *session.Session,
   125  	S3Bucket string,
   126  	logger *logrus.Logger) (bool, error) {
   127  
   128  	s3Svc := s3.New(awsSession)
   129  	params := &s3.GetBucketVersioningInput{
   130  		Bucket: aws.String(S3Bucket), // Required
   131  	}
   132  	versioningEnabled := false
   133  	resp, err := s3Svc.GetBucketVersioning(params)
   134  	if err == nil && resp != nil && resp.Status != nil {
   135  		// What's the versioning policy?
   136  		logger.WithFields(logrus.Fields{
   137  			"VersionPolicy": *resp,
   138  			"BucketName":    S3Bucket,
   139  		}).Debug("Bucket version policy")
   140  		versioningEnabled = (strings.ToLower(*resp.Status) == "enabled")
   141  	}
   142  	return versioningEnabled, err
   143  }
   144  
   145  // BucketRegion returns the AWS region that hosts the bucket
   146  func BucketRegion(awsSession *session.Session,
   147  	S3Bucket string,
   148  	logger *logrus.Logger) (string, error) {
   149  	regionHint := ""
   150  	if awsSession.Config.Region != nil {
   151  		regionHint = *awsSession.Config.Region
   152  	}
   153  	awsContext := aws.BackgroundContext()
   154  	return s3manager.GetBucketRegion(awsContext,
   155  		awsSession,
   156  		S3Bucket,
   157  		regionHint)
   158  }