github.com/ouraigua/jenkins-library@v0.0.0-20231028010029-fbeaf2f3aa9b/cmd/awsS3Upload.go (about)

     1  package cmd
     2  
     3  import (
     4  	"context"
     5  	"encoding/json"
     6  	"os"
     7  	"path/filepath"
     8  
     9  	"github.com/SAP/jenkins-library/pkg/log"
    10  	"github.com/SAP/jenkins-library/pkg/telemetry"
    11  
    12  	"github.com/aws/aws-sdk-go-v2/config"
    13  	"github.com/aws/aws-sdk-go-v2/service/s3"
    14  )
    15  
    16  // S3PutObjectAPI defines the interface for the PutObject function.
    17  // We use this interface to test the function using a mocked service.
    18  type S3PutObjectAPI interface {
    19  	PutObject(ctx context.Context,
    20  		params *s3.PutObjectInput,
    21  		optFns ...func(*s3.Options)) (*s3.PutObjectOutput, error)
    22  }
    23  
    24  // PutFile uploads a file to an AWS S3 bucket
    25  // The function needs a context (including the AWS Region) and a PutObjectInput for the service call
    26  // The return value is a PutObjectOutput with the result of the upload
    27  func PutFile(c context.Context, api S3PutObjectAPI, input *s3.PutObjectInput) (*s3.PutObjectOutput, error) {
    28  	return api.PutObject(c, input)
    29  }
    30  
    31  // Struct to store the AWS credentials from a specified JSON string
    32  type awsCredentials struct {
    33  	AwsAccessKeyID     string `json:"access_key_id"`
    34  	Bucket             string `json:"bucket"`
    35  	AwsSecretAccessKey string `json:"secret_access_key"`
    36  	AwsRegion          string `json:"region"`
    37  }
    38  
    39  func awsS3Upload(configOptions awsS3UploadOptions, telemetryData *telemetry.CustomData) {
    40  	// Prepare Credentials
    41  	log.Entry().Infoln("Start reading AWS Credentials")
    42  	var obj awsCredentials
    43  
    44  	err := json.Unmarshal([]byte(configOptions.JSONCredentialsAWS), &obj)
    45  	if err != nil {
    46  		log.Entry().
    47  			WithError(err).
    48  			Fatal("Could not read JSONCredentialsAWS")
    49  	}
    50  
    51  	// Set environment variables which are needed to initialize S3 Client
    52  	log.Entry().Infoln("Successfully read AWS Credentials. Setting up environment variables")
    53  	awsRegionSet := setenvIfEmpty("AWS_REGION", obj.AwsRegion)
    54  	awsAccessKeyIDSet := setenvIfEmpty("AWS_ACCESS_KEY_ID", obj.AwsAccessKeyID)
    55  	awsSecretAccessKeySet := setenvIfEmpty("AWS_SECRET_ACCESS_KEY", obj.AwsSecretAccessKey)
    56  
    57  	defer removeEnvIfPreviouslySet("AWS_REGION", awsRegionSet)
    58  	defer removeEnvIfPreviouslySet("AWS_ACCESS_KEY_ID", awsAccessKeyIDSet)
    59  	defer removeEnvIfPreviouslySet("AWS_SECRET_ACCESS_KEY", awsSecretAccessKeySet)
    60  
    61  	// Initialize S3 Client
    62  	log.Entry().Infoln("Loading Configuration for S3 Client")
    63  	cfg, err := config.LoadDefaultConfig(context.TODO())
    64  	if err != nil {
    65  		log.Entry().
    66  			WithError(err).
    67  			Fatal("AWS Client Configuration failed")
    68  	}
    69  	client := s3.NewFromConfig(cfg)
    70  
    71  	err = runAwsS3Upload(&configOptions, client, obj.Bucket)
    72  	if err != nil {
    73  		log.Entry().WithError(err).Fatal("Step execution failed")
    74  	}
    75  }
    76  
    77  func runAwsS3Upload(configOptions *awsS3UploadOptions, client S3PutObjectAPI, bucket string) error {
    78  	// Iterate through directories
    79  	err := filepath.Walk(configOptions.FilePath, func(currentFilePath string, f os.FileInfo, err error) error {
    80  		// Handle Failure to prevent panic (e.g. in case of an invalid filepath)
    81  		if err != nil {
    82  			log.Entry().WithError(err).Warnf("Failed to access path: '%v'", currentFilePath)
    83  			return err
    84  		}
    85  		// Skip directories, only upload files
    86  		if !f.IsDir() {
    87  			log.Entry().Infof("Current target path is: '%v'", currentFilePath)
    88  
    89  			// Open File
    90  			currentFile, e := os.Open(currentFilePath)
    91  			if e != nil {
    92  				log.Entry().WithError(e).Warnf("Could not open the file '%s'", currentFilePath)
    93  				return e
    94  			}
    95  			defer currentFile.Close()
    96  
    97  			// AWS SDK needs UNIX file paths to automatically create directories
    98  			key := filepath.ToSlash(currentFilePath)
    99  
   100  			// Intitialize S3 PutObjectInput
   101  			inputObject := &s3.PutObjectInput{
   102  				Bucket: &bucket,
   103  				Key:    &key,
   104  				Body:   currentFile,
   105  			}
   106  
   107  			// Upload File
   108  			log.Entry().Infof("Start upload of file '%v'", currentFilePath)
   109  			_, e = PutFile(context.TODO(), client, inputObject)
   110  			if e != nil {
   111  				log.Entry().WithError(e).Warnf("There was an error during the upload of file '%v'", currentFilePath)
   112  				return e
   113  			}
   114  
   115  			log.Entry().Infof("Upload of file '%v' was successful!", currentFilePath)
   116  			return e
   117  		}
   118  		return nil
   119  	})
   120  
   121  	if err != nil {
   122  		return err
   123  	}
   124  	log.Entry().Infoln("Upload has successfully finished!")
   125  	return err
   126  }
   127  
   128  // Function to set environment variables if they are not already set
   129  func setenvIfEmpty(env, val string) bool {
   130  	if len(os.Getenv(env)) == 0 {
   131  		os.Setenv(env, val)
   132  		return true
   133  	}
   134  	return false
   135  }
   136  
   137  // Function to remove environment variables if they are set
   138  func removeEnvIfPreviouslySet(env string, previouslySet bool) {
   139  	if previouslySet {
   140  		os.Setenv(env, "")
   141  	}
   142  }