From 9d7d1374df11aa07f2ceaf7348a484d08e295348 Mon Sep 17 00:00:00 2001 From: Damian Debkowski Date: Tue, 4 Jul 2023 10:42:34 -0700 Subject: [PATCH] test: add storage service aws test --- testing/e2e_host_test.go | 202 +----------- testing/e2e_storage_test.go | 361 ++++++++++++++++++++++ testing/testdata/storage/iam.tf | 206 ++++++++++++ testing/testdata/storage/main.tf | 11 + testing/testdata/storage/output.tf | 36 +++ testing/testdata/storage/s3.tf | 8 + testing/testdata/storage/test_object_data | 1 + testing/testdata/storage/variable.tf | 13 + testing/testing.go | 212 +++++++++++++ 9 files changed, 852 insertions(+), 198 deletions(-) create mode 100644 testing/e2e_storage_test.go create mode 100644 testing/testdata/storage/iam.tf create mode 100644 testing/testdata/storage/main.tf create mode 100644 testing/testdata/storage/output.tf create mode 100644 testing/testdata/storage/s3.tf create mode 100644 testing/testdata/storage/test_object_data create mode 100644 testing/testdata/storage/variable.tf diff --git a/testing/e2e_host_test.go b/testing/e2e_host_test.go index 7c8906b..e47888c 100644 --- a/testing/e2e_host_test.go +++ b/testing/e2e_host_test.go @@ -11,26 +11,20 @@ import ( "testing" "time" - "github.com/aws/aws-sdk-go/aws/awserr" "github.com/hashicorp/boundary-plugin-aws/internal/credential" - "github.com/hashicorp/boundary-plugin-aws/internal/values" "github.com/hashicorp/boundary-plugin-aws/plugin/service/host" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostcatalogs" "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/hostsets" pb "github.com/hashicorp/boundary/sdk/pbs/plugin" - "github.com/hashicorp/go-secure-stdlib/awsutil" "github.com/stretchr/testify/require" "google.golang.org/protobuf/types/known/structpb" ) -const ( - expectedIamUserCount = 6 - expectedEc2InstanceCount = 5 -) +const expectedEc2InstanceCount = 5 var expectedTags = []string{"foo", "bar", "baz"} -func TestPlugin(t *testing.T) { +func TestHostPlugin(t *testing.T) { region := os.Getenv("AWS_REGION") if region == "" { t.Skip("set AWS_REGION to use this test") @@ -195,36 +189,7 @@ func testPluginOnCreateCatalog(ctx context.Context, t *testing.T, p *host.HostPl require.NotNil(response) persisted := response.GetPersisted() require.NotNil(persisted) - persistedSecrets := persisted.GetSecrets() - require.NotNil(persistedSecrets) - persistedAccessKeyId, err := values.GetStringValue(persistedSecrets, credential.ConstAccessKeyId, true) - require.NoError(err) - require.NotZero(persistedAccessKeyId) - if rotate { - require.NotEqual(accessKeyId, persistedAccessKeyId) - } else { - require.Equal(accessKeyId, persistedAccessKeyId) - } - - persistedSecretAccessKey, err := values.GetStringValue(persistedSecrets, credential.ConstSecretAccessKey, true) - require.NoError(err) - require.NotZero(persistedSecretAccessKey) - if rotate { - require.NotEqual(secretAccessKey, persistedSecretAccessKey) - } else { - require.Equal(secretAccessKey, persistedSecretAccessKey) - } - - persistedCredsLastRotatedTime, err := values.GetTimeValue(persistedSecrets, credential.ConstCredsLastRotatedTime) - require.NoError(err) - if rotate { - require.NotZero(persistedCredsLastRotatedTime) - requireCredentialsInvalid(t, accessKeyId, secretAccessKey) - } else { - require.Zero(persistedCredsLastRotatedTime) - } - - return persistedAccessKeyId, persistedSecretAccessKey + return validatePersistedSecrets(t, persisted.GetSecrets(), accessKeyId, secretAccessKey, rotate) } func testPluginOnUpdateCatalog( @@ -293,116 +258,7 @@ func testPluginOnUpdateCatalog( require.NotNil(response) persisted := response.GetPersisted() require.NotNil(persisted) - persistedSecrets := persisted.GetSecrets() - require.NotNil(persistedSecrets) - - // Complex checks based on the scenarios. - persistedAccessKeyId, err := values.GetStringValue(persistedSecrets, credential.ConstAccessKeyId, true) - require.NoError(err) - require.NotZero(persistedAccessKeyId) - persistedSecretAccessKey, err := values.GetStringValue(persistedSecrets, credential.ConstSecretAccessKey, true) - require.NoError(err) - require.NotZero(persistedSecretAccessKey) - persistedCredsLastRotatedTime, err := values.GetTimeValue(persistedSecrets, credential.ConstCredsLastRotatedTime) - require.NoError(err) - - // Our test scenarios are complex due the multi-dimensional nature - // of criteria, so we lay them out in a switch below. - switch { - case newAccessKeyId != "" && rotated && rotate: - // The new access key ID was provided, we had previously rotated - // the credentials before, and the new credential set is to be - // rotated as well. In this case, the old credentials should have - // been deleted, and the new credentials should have been rotated, - // hence, should not match the new credentials initially - // provided. Rotation time should be non-zero and updated. - requireCredentialsInvalid(t, currentAccessKeyId, currentSecretAccessKey) - require.NotEqual(persistedAccessKeyId, newAccessKeyId) - require.NotEqual(persistedSecretAccessKey, newSecretAccessKey) - require.NotZero(persistedCredsLastRotatedTime) - require.True(persistedCredsLastRotatedTime.After(currentCredsLastRotatedTime)) - - case newAccessKeyId != "" && rotated && !rotate: - // The new access key ID was provided, we had previously rotated - // the credentials before, and the new credential is *not* - // rotated. In this case, the old credentials should have - // been deleted, But the new credentials should have not been - // rotated, and hence should be the same. Rotation time should be - // zero. - requireCredentialsInvalid(t, currentAccessKeyId, currentSecretAccessKey) - require.Equal(persistedAccessKeyId, newAccessKeyId) - require.Equal(persistedSecretAccessKey, newSecretAccessKey) - require.Zero(persistedCredsLastRotatedTime) - - case newAccessKeyId != "" && !rotated && rotate: - // The new access key ID was provided, we *have not* previously - // rotated the credentials, and the new credential set is to be - // rotated. In this case, the old credentials should have been - // left alone, and the new credentials should have been rotated, - // hence, should not match the new credentials initially - // provided. Rotation time should be non-zero, but updated. - requireCredentialsValid(t, currentAccessKeyId, currentSecretAccessKey) - require.NotEqual(persistedAccessKeyId, newAccessKeyId) - require.NotEqual(persistedSecretAccessKey, newSecretAccessKey) - require.NotZero(persistedCredsLastRotatedTime) - require.True(persistedCredsLastRotatedTime.After(currentCredsLastRotatedTime)) - - case newAccessKeyId != "" && !rotated && !rotate: - // The new access key ID was provided, but we have not rotated - // the credentials previously and we still don't plan on rotating - // them. In this case, the old credentials should still be valid, - // and the persisted ones should match the new ones provided. - // Rotation time should be zero. - requireCredentialsValid(t, currentAccessKeyId, currentSecretAccessKey) - require.Equal(persistedAccessKeyId, newAccessKeyId) - require.Equal(persistedSecretAccessKey, newSecretAccessKey) - require.Zero(persistedCredsLastRotatedTime) - - case newAccessKeyId == "" && rotated && rotate: - // No new credentials have been provided, but we have previously - // rotated and are still rotating credentials. This is a no-op. - // Existing credentials should still be valid and match the ones - // persisted to state. Rotation time should be identical since - // no new rotation occurred. - requireCredentialsValid(t, currentAccessKeyId, currentSecretAccessKey) - require.Equal(persistedAccessKeyId, currentAccessKeyId) - require.Equal(persistedSecretAccessKey, currentSecretAccessKey) - require.NotZero(persistedCredsLastRotatedTime) - require.True(currentCredsLastRotatedTime.Equal(persistedCredsLastRotatedTime)) - - case newAccessKeyId == "" && rotated && !rotate: - // No new credentials have been provided, and we have previously - // rotated the credentials. This is actually an error, but we - // don't test it here; it's covered in unit testing (see - // plugin_test.go). - require.FailNow("testing rotated-to-not-rotated scenario not implemented by this helper") - - case newAccessKeyId == "" && !rotated && rotate: - // No new credentials have been provided, and while we did not - // rotate before, we want to switch to rotation. In this case, - // the existing persisted credentials should have been rotated, - // with a new non-zero timestamp. - requireCredentialsInvalid(t, currentAccessKeyId, currentSecretAccessKey) - require.NotEqual(persistedAccessKeyId, currentAccessKeyId) - require.NotEqual(persistedSecretAccessKey, currentSecretAccessKey) - require.NotZero(persistedCredsLastRotatedTime) - - case newAccessKeyId == "" && !rotated && !rotate: - // No new credentials have been provided and we have not, nor do - // not, plan on rotating the credentials. This is a no-op. - // Existing credentials should still be valid and match the ones - // persisted to state. Rotation time should remain at zero. - requireCredentialsValid(t, currentAccessKeyId, currentSecretAccessKey) - require.Equal(persistedAccessKeyId, currentAccessKeyId) - require.Equal(persistedSecretAccessKey, currentSecretAccessKey) - require.Zero(persistedCredsLastRotatedTime) - - default: - // Scenario was reached that was not covered by this function. - require.FailNow("unknown test scenario") - } - - return persistedAccessKeyId, persistedSecretAccessKey + return validateUpdateSecrets(t, persisted.GetSecrets(), currentCredsLastRotatedTime, currentAccessKeyId, currentSecretAccessKey, newAccessKeyId, newSecretAccessKey, rotated, rotate) } func testPluginOnDeleteCatalog(ctx context.Context, t *testing.T, p *host.HostPlugin, region, accessKeyId, secretAccessKey string, rotated bool) { @@ -588,53 +444,3 @@ func testPluginListHosts(ctx context.Context, t *testing.T, p *host.HostPlugin, // Success t.Logf("testing ListHosts: success (region=%s, tags=%v, expected/actual=(len=%d, ids=%s))", region, tags, len(actualInstances), actualInstances) } - -func requireCredentialsInvalid(t *testing.T, accessKeyId, secretAccessKey string) { - t.Helper() - require := require.New(t) - - c, err := awsutil.NewCredentialsConfig( - awsutil.WithAccessKey(accessKeyId), - awsutil.WithSecretKey(secretAccessKey), - ) - require.NoError(err) - - // We need to wait for invalidation as while awsutil waits for - // credential creation, deletion of the old credentials returns - // immediately. - timeoutCtx, cancel := context.WithTimeout(context.Background(), time.Second*30) - defer cancel() -waitErr: - for { - _, err = c.GetCallerIdentity() - if err != nil { - break - } - - select { - case <-time.After(time.Second): - // pass - - case <-timeoutCtx.Done(): - break waitErr - } - } - - require.NotNil(err) - awsErr, ok := err.(awserr.Error) - require.True(ok) - require.Equal("InvalidClientTokenId", awsErr.Code()) -} - -func requireCredentialsValid(t *testing.T, accessKeyId, secretAccessKey string) { - t.Helper() - require := require.New(t) - - c, err := awsutil.NewCredentialsConfig( - awsutil.WithAccessKey(accessKeyId), - awsutil.WithSecretKey(secretAccessKey), - ) - require.NoError(err) - _, err = c.GetCallerIdentity() - require.NoError(err) -} diff --git a/testing/e2e_storage_test.go b/testing/e2e_storage_test.go new file mode 100644 index 0000000..b3c7fe5 --- /dev/null +++ b/testing/e2e_storage_test.go @@ -0,0 +1,361 @@ +// Copyright (c) HashiCorp, Inc. +// SPDX-License-Identifier: MPL-2.0 + +package testing + +import ( + "context" + "io" + "net" + "os" + "path/filepath" + "testing" + "time" + + "github.com/google/uuid" + "github.com/hashicorp/boundary-plugin-aws/internal/credential" + "github.com/hashicorp/boundary-plugin-aws/plugin/service/storage" + "github.com/hashicorp/boundary/sdk/pbs/controller/api/resources/storagebuckets" + pb "github.com/hashicorp/boundary/sdk/pbs/plugin" + "github.com/stretchr/testify/require" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/protobuf/types/known/structpb" +) + +func TestStoragePlugin(t *testing.T) { + region := os.Getenv("AWS_REGION") + if region == "" { + t.Skip("set AWS_REGION to use this test") + } + if envAccessKeyId := os.Getenv("AWS_ACCESS_KEY_ID"); envAccessKeyId == "" { + t.Skip("set AWS_ACCESS_KEY_ID to use this test") + } + if envSecretAccessKey := os.Getenv("AWS_SECRET_ACCESS_KEY"); envSecretAccessKey == "" { + t.Skip("set AWS_SECRET_ACCESS_KEY to use this test") + } + + require := require.New(t) + tf, err := NewTestTerraformer("testdata/storage") + require.NoError(err) + require.NotNil(tf) + + t.Log("===== deploying test Terraform workspace =====") + err = tf.Deploy() + require.NoError(err) + + defer func() { + t.Log("===== destroying test Terraform workspace =====") + if err := tf.Destroy(); err != nil { + t.Logf("WARNING: could not run Terraform destroy: %s", err) + } + }() + + bucketName, err := tf.GetOutputString("bucket_name") + require.NoError(err) + require.NotEmpty(bucketName) + + iamAccessKeyIds, err := tf.GetOutputSlice("iam_access_key_ids") + require.NoError(err) + require.Len(iamAccessKeyIds, expectedIamUserCount) + + iamSecretAccessKeys, err := tf.GetOutputSlice("iam_secret_access_keys") + require.NoError(err) + require.Len(iamSecretAccessKeys, expectedIamUserCount) + + // Start the workflow now. Set up the host catalog. Note that this + // will cause the state to go out of drift above in the sense that + // the access key ID/secret access key will no longer be valid. We + // will assert this through the returned state. + p := new(storage.StoragePlugin) + ctx := context.Background() + + // ******************** + // * OnCreateStorageBucket + // ******************** + // + // Test non-rotation (using primary user). + keyid, secret := testPluginOnCreateStorageBucket(ctx, t, p, bucketName, region, iamAccessKeyIds[0].(string), iamSecretAccessKeys[0].(string), false) + // Test rotation next. + keyid, secret = testPluginOnCreateStorageBucket(ctx, t, p, bucketName, region, keyid, secret, true) + + // ******************** + // * OnUpdateStorageBucket + // ******************** + // + // Test no-op non-rotation. + keyid, secret = testPluginOnUpdateStorageBucket(ctx, t, p, bucketName, region, keyid, secret, "", "", false, false) + // Switch to rotation. + keyid, secret = testPluginOnUpdateStorageBucket(ctx, t, p, bucketName, region, keyid, secret, "", "", false, true) + // Test no-op with rotation. + keyid, secret = testPluginOnUpdateStorageBucket(ctx, t, p, bucketName, region, keyid, secret, "", "", true, true) + // Switch credentials to next user. Don't rotate. + keyid, secret = testPluginOnUpdateStorageBucket(ctx, t, p, bucketName, region, keyid, secret, iamAccessKeyIds[1].(string), iamSecretAccessKeys[1].(string), true, false) + // Switch credentials to next user. Add rotation. + keyid, secret = testPluginOnUpdateStorageBucket(ctx, t, p, bucketName, region, keyid, secret, iamAccessKeyIds[2].(string), iamSecretAccessKeys[2].(string), false, true) + // Switch to next user, with rotation disabled. + keyid, secret = testPluginOnUpdateStorageBucket(ctx, t, p, bucketName, region, keyid, secret, iamAccessKeyIds[3].(string), iamSecretAccessKeys[3].(string), true, false) + // Last case - switch to another user and keep rotation off. + keyid, secret = testPluginOnUpdateStorageBucket(ctx, t, p, bucketName, region, keyid, secret, iamAccessKeyIds[4].(string), iamSecretAccessKeys[4].(string), false, false) + + // ******************** + // * OnDeleteStorageBucket + // ******************** + // + // Test non-rotated. + testPluginOnDeleteStorageBucket(ctx, t, p, bucketName, region, keyid, secret, false) + // Test as if we had rotated these credentials (note that this + // makes this test set unusable). + testPluginOnDeleteStorageBucket(ctx, t, p, bucketName, region, keyid, secret, true) + + // ******************** + // * Object Methods: PutObject, GetObject, HeadObject + // ******************** + // + // Reassign the keyid and secret first. + keyid, secret = iamAccessKeyIds[5].(string), iamSecretAccessKeys[5].(string) + testPluginObjectMethods(ctx, t, p, bucketName, region, keyid, secret) +} + +func testPluginOnCreateStorageBucket(ctx context.Context, t *testing.T, p *storage.StoragePlugin, bucketName, region, accessKeyId, secretAccessKey string, rotate bool) (string, string) { + t.Helper() + t.Logf("testing OnCreateStorageBucket (region=%s, rotate=%t)", region, rotate) + require := require.New(t) + + reqAttrs, err := structpb.NewStruct(map[string]any{ + credential.ConstRegion: region, + credential.ConstDisableCredentialRotation: !rotate, + }) + require.NoError(err) + reqSecrets, err := structpb.NewStruct(map[string]any{ + credential.ConstAccessKeyId: accessKeyId, + credential.ConstSecretAccessKey: secretAccessKey, + }) + require.NoError(err) + request := &pb.OnCreateStorageBucketRequest{ + Bucket: &storagebuckets.StorageBucket{ + BucketName: bucketName, + Secrets: reqSecrets, + Attributes: reqAttrs, + }, + } + response, err := p.OnCreateStorageBucket(ctx, request) + require.NoError(err) + require.NotNil(response) + persisted := response.GetPersisted() + require.NotNil(persisted) + return validatePersistedSecrets(t, persisted.GetData(), accessKeyId, secretAccessKey, rotate) +} + +func testPluginOnUpdateStorageBucket( + ctx context.Context, t *testing.T, p *storage.StoragePlugin, bucketName, + region, currentAccessKeyId, currentSecretAccessKey, newAccessKeyId, newSecretAccessKey string, + rotated, rotate bool, +) (string, string) { + t.Helper() + t.Logf("testing OnUpdateStorageBucket (region=%s, newcreds=%t, rotated=%t, rotate=%t)", region, newAccessKeyId != "" && newSecretAccessKey != "", rotated, rotate) + require := require.New(t) + + // Take a timestamp of the current time to get a point in time to + // reference, ensuring that we are updating credential rotation + // timestamps. + currentCredsLastRotatedTime := time.Now() + + reqCurrentAttrs, err := structpb.NewStruct(map[string]any{ + credential.ConstRegion: region, + credential.ConstDisableCredentialRotation: !rotated, + }) + require.NoError(err) + reqNewAttrs, err := structpb.NewStruct(map[string]any{ + credential.ConstRegion: region, + credential.ConstDisableCredentialRotation: !rotate, + }) + require.NoError(err) + var reqSecrets *structpb.Struct + if newAccessKeyId != "" && newSecretAccessKey != "" { + reqSecrets, err = structpb.NewStruct(map[string]any{ + credential.ConstAccessKeyId: newAccessKeyId, + credential.ConstSecretAccessKey: newSecretAccessKey, + }) + require.NoError(err) + } + reqPersistedSecrets, err := structpb.NewStruct(map[string]any{ + credential.ConstAccessKeyId: currentAccessKeyId, + credential.ConstSecretAccessKey: currentSecretAccessKey, + credential.ConstCredsLastRotatedTime: func() string { + if rotated { + return currentCredsLastRotatedTime.Format(time.RFC3339Nano) + } + + return (time.Time{}).Format(time.RFC3339Nano) + }(), + }) + require.NoError(err) + require.NotNil(reqPersistedSecrets) + request := &pb.OnUpdateStorageBucketRequest{ + CurrentBucket: &storagebuckets.StorageBucket{ + BucketName: bucketName, + Attributes: reqCurrentAttrs, + }, + NewBucket: &storagebuckets.StorageBucket{ + BucketName: bucketName, + Attributes: reqNewAttrs, + Secrets: reqSecrets, + }, + Persisted: &storagebuckets.StorageBucketPersisted{ + Data: reqPersistedSecrets, + }, + } + response, err := p.OnUpdateStorageBucket(ctx, request) + require.NoError(err) + require.NotNil(response) + persisted := response.GetPersisted() + require.NotNil(persisted) + return validateUpdateSecrets(t, persisted.GetData(), currentCredsLastRotatedTime, currentAccessKeyId, currentSecretAccessKey, newAccessKeyId, newSecretAccessKey, rotated, rotate) +} + +func testPluginOnDeleteStorageBucket(ctx context.Context, t *testing.T, p *storage.StoragePlugin, bucketName, region, accessKeyId, secretAccessKey string, rotated bool) { + t.Helper() + t.Logf("testing OnDeleteStorageBucket (region=%s, rotated=%t)", region, rotated) + require := require.New(t) + + reqAttrs, err := structpb.NewStruct(map[string]any{ + credential.ConstRegion: region, + credential.ConstDisableCredentialRotation: !rotated, + }) + require.NoError(err) + reqSecrets, err := structpb.NewStruct(map[string]any{ + credential.ConstAccessKeyId: accessKeyId, + credential.ConstSecretAccessKey: secretAccessKey, + }) + require.NoError(err) + reqPersistedSecrets, err := structpb.NewStruct(map[string]any{ + credential.ConstAccessKeyId: accessKeyId, + credential.ConstSecretAccessKey: secretAccessKey, + credential.ConstCredsLastRotatedTime: func() string { + if rotated { + return time.Now().Format(time.RFC3339Nano) + } + + return (time.Time{}).Format(time.RFC3339Nano) + }(), + }) + require.NoError(err) + request := &pb.OnDeleteStorageBucketRequest{ + Bucket: &storagebuckets.StorageBucket{ + BucketName: bucketName, + Attributes: reqAttrs, + Secrets: reqSecrets, + }, + Persisted: &storagebuckets.StorageBucketPersisted{ + Data: reqPersistedSecrets, + }, + } + response, err := p.OnDeleteStorageBucket(ctx, request) + require.NoError(err) + require.NotNil(response) + + // We want to test the validity of the credentials post-deletion. + if rotated { + // The credentials should no longer be valid. + requireCredentialsInvalid(t, accessKeyId, secretAccessKey) + } else { + // The credentials should still be valid. Sleep 10s first just to + // be sure, since we're not rotating. + time.Sleep(time.Second * 10) + requireCredentialsValid(t, accessKeyId, secretAccessKey) + } +} + +func testPluginObjectMethods(ctx context.Context, t *testing.T, p *storage.StoragePlugin, bucketName, region, accessKeyId, secretAccessKey string) { + t.Helper() + t.Logf("testing PutObject") + require := require.New(t) + + testDataPath, err := filepath.Abs("./testdata/storage/test_object_data") + require.NoError(err) + require.FileExists(testDataPath) + + reqAttrs, err := structpb.NewStruct(map[string]any{ + credential.ConstRegion: region, + credential.ConstDisableCredentialRotation: true, + }) + require.NoError(err) + reqSecrets, err := structpb.NewStruct(map[string]any{ + credential.ConstAccessKeyId: accessKeyId, + credential.ConstSecretAccessKey: secretAccessKey, + }) + require.NoError(err) + + objectKey := uuid.New().String() + putObjReq := &pb.PutObjectRequest{ + Bucket: &storagebuckets.StorageBucket{ + BucketName: bucketName, + Attributes: reqAttrs, + Secrets: reqSecrets, + }, + Key: objectKey, + Path: testDataPath, + } + putObjResp, err := p.PutObject(ctx, putObjReq) + require.NoError(err) + require.NotNil(putObjResp) + require.NotEmpty(putObjResp.GetChecksumSha_256()) + + t.Logf("testing HeadObject") + headObjReq := &pb.HeadObjectRequest{ + Bucket: &storagebuckets.StorageBucket{ + BucketName: bucketName, + Attributes: reqAttrs, + Secrets: reqSecrets, + }, + Key: objectKey, + } + headObjResp, err := p.HeadObject(ctx, headObjReq) + require.NoError(err) + require.NotNil(headObjResp) + require.NotEmpty(headObjResp.GetContentLength()) + require.NotEmpty(headObjResp.GetLastModified()) + + // Need to create a GRPC server to test GetObject + t.Logf("testing GetObject") + lis, err := net.Listen("tcp", "localhost:2030") + require.NoError(err) + grpcServer := grpc.NewServer() + pb.RegisterStoragePluginServiceServer(grpcServer, &storage.StoragePlugin{}) + go func() { + grpcServer.Serve(lis) + }() + + conn, err := grpc.Dial("localhost:2030", grpc.WithTransportCredentials(insecure.NewCredentials())) + require.NoError(err) + defer conn.Close() + client := pb.NewStoragePluginServiceClient(conn) + + stream, err := client.GetObject(ctx, &pb.GetObjectRequest{ + Bucket: &storagebuckets.StorageBucket{ + BucketName: bucketName, + Attributes: reqAttrs, + Secrets: reqSecrets, + }, + Key: objectKey, + }) + require.NoError(err) + + var actualData []byte + expectedData, err := os.ReadFile(testDataPath) + require.NoError(err) + for { + resp, err := stream.Recv() + if err == io.EOF { + require.NoError(stream.CloseSend()) + break + } else if err != nil { + require.NoError(err) + } + actualData = append(actualData, resp.GetFileChunk()...) + } + require.Equal(expectedData, actualData) + require.NoError(conn.Close()) + grpcServer.Stop() +} diff --git a/testing/testdata/storage/iam.tf b/testing/testdata/storage/iam.tf new file mode 100644 index 0000000..f910a56 --- /dev/null +++ b/testing/testdata/storage/iam.tf @@ -0,0 +1,206 @@ +# Copyright (c) HashiCorp, Inc. +# SPDX-License-Identifier: MPL-2.0 + +resource "aws_iam_role" "valid" { + name = "${random_id.prefix.dec}-valid" + + assume_role_policy = jsonencode({ + Version = "2012-10-17" + Statement = [ + { + Action = "sts:AssumeRole" + Effect = "Allow" + Sid = "" + Principal = { + AWS: data.aws_caller_identity.current.user_id + } + }, + ] + }) +} + +resource "aws_iam_policy" "valid" { + name = "${random_id.prefix.dec}-valid" + policy = <