From 6b662663f3eb8c00fe6476cd63a05c1efa585eec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ond=C5=99ej=20Budai?= Date: Thu, 9 Jan 2020 09:20:35 +0100 Subject: [PATCH] upload/aws: Add basic S3 upload test The test is very simple, it just uploads object to S3, downloads it back and compares them. --- internal/upload/awsupload/awsupload_test.go | 155 ++++++++++++++++++++ 1 file changed, 155 insertions(+) create mode 100644 internal/upload/awsupload/awsupload_test.go diff --git a/internal/upload/awsupload/awsupload_test.go b/internal/upload/awsupload/awsupload_test.go new file mode 100644 index 000000000..111e9f5bb --- /dev/null +++ b/internal/upload/awsupload/awsupload_test.go @@ -0,0 +1,155 @@ +package awsupload_test + +import ( + "bytes" + "fmt" + "github.com/osbuild/osbuild-composer/internal/upload/awsupload" + "io/ioutil" + "os" + "os/exec" + "path" + "testing" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/s3" + "github.com/aws/aws-sdk-go/service/s3/s3manager" +) + +func handleErrors(t *testing.T, err error) { + if err != nil { + t.Fatal(err) + } +} + +func loadEnvVar(t *testing.T, envVarName string) (string, bool) { + variable, exists := os.LookupEnv(envVarName) + if !exists { + t.Logf("Environment variable does not exist: %s", envVarName) + return "", false + } + return variable, true +} + +func generateRandomFile(pattern string, length int) (string, []byte, error) { + f, err := ioutil.TempFile("/tmp", pattern) + if err != nil { + return "", []byte{}, err + } + fileName := f.Name() + + countArg := fmt.Sprintf("count=%d", length) + cmd := exec.Command("dd", "bs=1", countArg, "if=/dev/urandom", fmt.Sprintf("of=%s", fileName)) + err = cmd.Run() + + contents, err := ioutil.ReadAll(f) + if err != nil { + return "", []byte{}, err + } + + if err := f.Close(); err != nil { + return ",", []byte{}, err + } + + return fileName, contents, err +} + +func loadTestSettings(t *testing.T) (string, string, string, string) { + accessKeyId, akExists := loadEnvVar(t, "AWS_ACCESS_KEY_ID") + secretAccessKey, sakExists := loadEnvVar(t, "AWS_SECRET_ACCESS_KEY") + region, regionExists := loadEnvVar(t, "AWS_REGION") + bucket, bucketExists := loadEnvVar(t, "AWS_BUCKET") + + // Workaround Travis security feature. If non of the variables is set, just ignore the test + if !akExists && !sakExists && !bucketExists && !regionExists { + t.Skip("No AWS configuration provided, assuming that this is running in CI. Skipping the test.") + } + // If only one/two of them are not set, then fail + if !akExists || !sakExists || !bucketExists || !regionExists { + t.Fatal("You need to define all variables for AWS connection.") + } + return accessKeyId, secretAccessKey, region, bucket +} + +func TestAWS_S3Upload(t *testing.T) { + // Load test settings + accessKeyId, secretAccessKey, region, bucket := loadTestSettings(t) + + // There's no way to retrieve session from awsupload, therefore we need to create an extra one in tests + sess, err := createSession(accessKeyId, secretAccessKey, region) + handleErrors(t, err) + + // Generate random file to be uploaded + fileLength := 512 * 512 + filePath, content, err := generateRandomFile("osbuild-composer-test-aws-upload-", fileLength) + handleErrors(t, err) + defer os.Remove(filePath) + + // Use filename as s3 key + s3Key := path.Base(filePath) + + // Test uploader + awsUpload, err := awsupload.New(region, accessKeyId, secretAccessKey) + if err != nil { + t.Fatalf("obtaining session failed: %s", err.Error()) + } + handleErrors(t, err) + + _, err = awsUpload.Upload(filePath, bucket, s3Key) + + if err != nil { + t.Fatalf("upload failed: %s", err.Error()) + } + + // Delete the object from S3 after the test is finished + defer func() { + s := s3.New(sess) + s.DeleteObject(&s3.DeleteObjectInput{ + Bucket: &bucket, + Key: &s3Key, + }) + }() + + // Set up temporary file for downloaded + downloadFile, err := ioutil.TempFile("/tmp", "osbuild-composer-test-aws-download-") + handleErrors(t, err) + defer os.Remove(downloadFile.Name()) + + // Download the object from S3 + downloader := s3manager.NewDownloader(sess) + + n, err := downloader.Download(downloadFile, &s3.GetObjectInput{ + Bucket: &bucket, + Key: &s3Key, + }) + + if err != nil { + t.Fatalf("downloader cannot download s3 object: %s", err.Error()) + } + + if n != int64(fileLength) { + t.Fatalf("downloaded file length is wrong, expected: %d, got: %d", fileLength, n) + } + + // Seek the downloaded file to the beginning + _, err = downloadFile.Seek(0, 0) + handleErrors(t, err) + + downloadedFileContent, err := ioutil.ReadAll(downloadFile) + handleErrors(t, err) + + if !bytes.Equal(content, downloadedFileContent) { + t.Errorf("downloaded file isn't matching the uploaded one") + } +} + +func createSession(accessKeyId string, secretAccessKey string, region string) (*session.Session, error) { + creds := credentials.NewStaticCredentials(accessKeyId, secretAccessKey, "") + + return session.NewSession(&aws.Config{ + Credentials: creds, + Region: ®ion, + }) + +}