package cmd import ( "context" "encoding/json" "os" "path/filepath" "github.com/SAP/jenkins-library/pkg/log" "github.com/SAP/jenkins-library/pkg/telemetry" "github.com/aws/aws-sdk-go-v2/config" "github.com/aws/aws-sdk-go-v2/service/s3" ) // S3PutObjectAPI defines the interface for the PutObject function. // We use this interface to test the function using a mocked service. type S3PutObjectAPI interface { PutObject(ctx context.Context, params *s3.PutObjectInput, optFns ...func(*s3.Options)) (*s3.PutObjectOutput, error) } // PutFile uploads a file to an AWS S3 bucket // The function needs a context (including the AWS Region) and a PutObjectInput for the service call // The return value is a PutObjectOutput with the result of the upload func PutFile(c context.Context, api S3PutObjectAPI, input *s3.PutObjectInput) (*s3.PutObjectOutput, error) { return api.PutObject(c, input) } // Struct to store the AWS credentials from a specified JSON string type awsCredentials struct { AwsAccessKeyID string `json:"access_key_id"` Bucket string `json:"bucket"` AwsSecretAccessKey string `json:"secret_access_key"` AwsRegion string `json:"region"` } func awsS3Upload(configOptions awsS3UploadOptions, telemetryData *telemetry.CustomData) { // Prepare Credentials log.Entry().Infoln("Start reading AWS Credentials") var obj awsCredentials err := json.Unmarshal([]byte(configOptions.JSONCredentialsAWS), &obj) if err != nil { log.Entry(). WithError(err). Fatal("Could not read JSONCredentialsAWS") } // Set environment variables which are needed to initialize S3 Client log.Entry().Infoln("Successfully read AWS Credentials. Setting up environment variables") awsRegionSet := setenvIfEmpty("AWS_REGION", obj.AwsRegion) awsAccessKeyIDSet := setenvIfEmpty("AWS_ACCESS_KEY_ID", obj.AwsAccessKeyID) awsSecretAccessKeySet := setenvIfEmpty("AWS_SECRET_ACCESS_KEY", obj.AwsSecretAccessKey) defer removeEnvIfPreviouslySet("AWS_REGION", awsRegionSet) defer removeEnvIfPreviouslySet("AWS_ACCESS_KEY_ID", awsAccessKeyIDSet) defer removeEnvIfPreviouslySet("AWS_SECRET_ACCESS_KEY", awsSecretAccessKeySet) // Initialize S3 Client log.Entry().Infoln("Loading Configuration for S3 Client") cfg, err := config.LoadDefaultConfig(context.TODO()) if err != nil { log.Entry(). WithError(err). Fatal("AWS Client Configuration failed") } client := s3.NewFromConfig(cfg) err = runAwsS3Upload(&configOptions, client, obj.Bucket) if err != nil { log.Entry().WithError(err).Fatal("Step execution failed") } } func runAwsS3Upload(configOptions *awsS3UploadOptions, client S3PutObjectAPI, bucket string) error { // Iterate through directories err := filepath.Walk(configOptions.FilePath, func(currentFilePath string, f os.FileInfo, err error) error { // Handle Failure to prevent panic (e.g. in case of an invalid filepath) if err != nil { log.Entry().WithError(err).Warnf("Failed to access path: '%v'", currentFilePath) return err } // Skip directories, only upload files if !f.IsDir() { log.Entry().Infof("Current target path is: '%v'", currentFilePath) // Open File currentFile, e := os.Open(currentFilePath) if e != nil { log.Entry().WithError(e).Warnf("Could not open the file '%s'", currentFilePath) return e } defer currentFile.Close() // AWS SDK needs UNIX file paths to automatically create directories key := filepath.ToSlash(currentFilePath) // Intitialize S3 PutObjectInput inputObject := &s3.PutObjectInput{ Bucket: &bucket, Key: &key, Body: currentFile, } // Upload File log.Entry().Infof("Start upload of file '%v'", currentFilePath) _, e = PutFile(context.TODO(), client, inputObject) if e != nil { log.Entry().WithError(e).Warnf("There was an error during the upload of file '%v'", currentFilePath) return e } log.Entry().Infof("Upload of file '%v' was successful!", currentFilePath) return e } return nil }) if err != nil { return err } log.Entry().Infoln("Upload has successfully finished!") return err } // Function to set environment variables if they are not already set func setenvIfEmpty(env, val string) bool { if len(os.Getenv(env)) == 0 { os.Setenv(env, val) return true } return false } // Function to remove environment variables if they are set func removeEnvIfPreviouslySet(env string, previouslySet bool) { if previouslySet { os.Setenv(env, "") } }