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 }