in agent/plugins/downloadcontent/s3resource/s3resource.go [95:210]
func (s3 *S3Resource) DownloadRemoteResource(filesys filemanager.FileSystem, destPath string) (err error, result *remoteresource.DownloadResult) {
var fileURL *url.URL
var unescapedURL string
var folders []string
var localFilePath string
log := s3.context.Log()
result = &remoteresource.DownloadResult{}
isDirTypeDownloaded := true
if destPath == "" {
destPath = appconfig.DownloadRoot
}
log.Info("Downloading S3 artifacts from path - ", s3.Info.Path)
// Change from '+' to '%20' is made as a workaround because s3 uses + for spaces in its URL instead of %20
// This makes the differentiation between '+' and ' ' impossible when we try to manipulate the path of files to download
// Since %20 is the universal escaping for ' ', s3 accepts that as well.
// https://s3.amazonaws.com/aws-executecommand-test/scripts/hello%2Bworld/spaces+file.sh
// new path - https://s3.amazonaws.com/aws-executecommand-test/scripts/hello%2Bworld/spaces%20file.sh
s3.Info.Path = strings.Replace(s3.Info.Path, "+", "%20", -1)
if fileURL, err = url.Parse(s3.Info.Path); err != nil {
return err, nil
}
log.Debug("File URL - ", fileURL.String())
s3.s3Object = s3util.ParseAmazonS3URL(log, fileURL)
log.Debug("S3 object - ", s3.s3Object.String())
if !s3.s3Object.IsValidS3URI {
return fmt.Errorf("invalid S3 path parameter"), nil
}
// Create an object for the source URL. This can be used to list the objects in the folder
if folders, err = dep.ListS3Directory(s3.context, s3.s3Object); err != nil {
if isPathType(s3.s3Object.Key) {
return err, nil
}
log.Infof("Attempting s3 download while assuming s3Object '%s' is a file", s3.s3Object.Key)
folders = []string{}
}
if len(folders) == 0 {
// In case of a file download, append the filename to folders
isDirTypeDownloaded = false
folders = append(folders, s3.s3Object.Key)
}
// The URL till the bucket name will be concatenated with the prefix in the loop
// responsible for download
for _, files := range folders {
log.Debug("Name of file - ", files)
if !isPathType(files) { //Only download in case the URL is a file
subFolderPath := strings.TrimPrefix(files, s3.s3Object.Key)
var bucketURL *url.URL
if bucketURL, err = s3.getS3BucketURLString(); err != nil {
return fmt.Errorf("error while obtaining URL parsing - %v", bucketURL), nil
}
if bucketURL == nil {
return errors.New("URL obtained is nil"), nil
}
log.Debug("S3 bucket URL -", bucketURL.String())
var input artifact.DownloadInput
// Obtain the full URL for the file before download
bucketURL.Path += "/" + files
input.SourceURL = bucketURL.String()
// When s3 object returns the Path, it has + for '+', and %20 for ' ', because of the workaround above.
// Since we are sending this URL for download, S3 manipulates the + to be a space.
// Change from '+' to '%2B' which is the encoding for '+' so that s3 has to interpret %20 to be a space and %2B
// to be a '+'
// https://s3.amazonaws.com/aws-executecommand-test/scripts/hello+world/spaces%20file.sh
// https://s3.amazonaws.com/aws-executecommand-test/scripts/hello%2Bworld/spaces%20file.sh
input.SourceURL = strings.Replace(input.SourceURL, "+", "%2B", -1)
log.Debug("SourceURL ", input.SourceURL)
if unescapedURL, err = url.QueryUnescape(input.SourceURL); err != nil {
return err, nil
}
log.Debug("UnescapedURL ", unescapedURL)
destinationFile := filepath.Base(unescapedURL)
//when the s3 key has sub-folders leading to files, those sub-folders need to be created as well
localFilePath = fileutil.BuildPath(destPath, filepath.Dir(subFolderPath))
if !isDirTypeDownloaded {
// if the file path provided exists as a directory or if it is in the format,
// that would be the localFilePath
if filesys.Exists(destPath) && filesys.IsDirectory(destPath) || isPathType(destPath) {
localFilePath = destPath
} else {
localFilePath = filepath.Dir(destPath)
destinationFile = filepath.Base(destPath)
}
}
input.DestinationDirectory = localFilePath
input.ExpectedBucketOwner = s3.Info.ExpectedBucketOwner
downloadOutput, err := dep.Download(s3.context, input)
if err != nil {
return err, nil
}
if err = system.RenameFile(log, filesys, downloadOutput.LocalFilePath, destinationFile); err != nil {
return fmt.Errorf("Something went wrong when trying to access downloaded content. It is "+
"possible that the content was not downloaded because the path provided is wrong. %v", err),
nil
}
result.Files = append(result.Files, filepath.Join(input.DestinationDirectory, destinationFile))
}
}
return nil, result
}