From 4854d4d997f6e8e81a80a94434068a84342ff38f Mon Sep 17 00:00:00 2001 From: Elie Date: Wed, 30 Jun 2021 11:38:22 +0200 Subject: [PATCH] Split s3_bucket_notification_supplier --- pkg/remote/aws/init.go | 3 +- .../aws/repository/mock_S3Repository.go | 23 +++ pkg/remote/aws/repository/s3_repository.go | 34 ++++ .../aws/repository/s3_repository_test.go | 153 +++++++++++++++ .../s3_bucket_notification_detail_fetcher.go | 38 ++++ .../aws/s3_bucket_notification_enumerator.go | 77 ++++++++ .../aws/s3_bucket_notification_supplier.go | 81 -------- .../s3_bucket_notification_supplier_test.go | 139 -------------- pkg/remote/s3_scanner_test.go | 175 ++++++++++++++++++ .../results.golden.json | 0 .../schema.golden.json | 0 ...tin-test-drift2-eu-west-3.res.golden.json} | 0 .../results.golden.json | 0 .../schema.golden.json | 0 ...tctl-test-no-notifications.res.golden.json | 0 .../results.golden.json | 0 .../schema.golden.json | 0 .../terraform.tf | 0 18 files changed, 502 insertions(+), 221 deletions(-) create mode 100644 pkg/remote/aws/s3_bucket_notification_detail_fetcher.go create mode 100644 pkg/remote/aws/s3_bucket_notification_enumerator.go delete mode 100644 pkg/remote/aws/s3_bucket_notification_supplier.go delete mode 100644 pkg/remote/aws/s3_bucket_notification_supplier_test.go rename pkg/remote/{aws => }/test/s3_bucket_notifications_list_bucket/results.golden.json (100%) rename pkg/remote/{aws => }/test/s3_bucket_notifications_list_bucket/schema.golden.json (100%) rename pkg/remote/{aws/test/s3_bucket_notifications_multiple/aws_s3_bucket_notification-bucket-martin-test-drift2.res.golden.json => test/s3_bucket_notifications_multiple/aws_s3_bucket_notification-bucket-martin-test-drift2-eu-west-3.res.golden.json} (100%) rename pkg/remote/{aws => }/test/s3_bucket_notifications_multiple/results.golden.json (100%) rename pkg/remote/{aws => }/test/s3_bucket_notifications_multiple/schema.golden.json (100%) rename pkg/remote/{aws => }/test/s3_bucket_notifications_no_notif/aws_s3_bucket_notification-dritftctl-test-no-notifications.res.golden.json (100%) rename pkg/remote/{aws => }/test/s3_bucket_notifications_no_notif/results.golden.json (100%) rename pkg/remote/{aws => }/test/s3_bucket_notifications_no_notif/schema.golden.json (100%) rename pkg/remote/{aws => }/test/s3_bucket_notifications_no_notif/terraform.tf (100%) diff --git a/pkg/remote/aws/init.go b/pkg/remote/aws/init.go index d3ac582a..980230aa 100644 --- a/pkg/remote/aws/init.go +++ b/pkg/remote/aws/init.go @@ -63,10 +63,11 @@ func Init(version string, alerter *alerter.Alerter, remoteLibrary.AddEnumerator(NewS3BucketInventoryEnumerator(s3Repository, factory, provider.Config)) remoteLibrary.AddDetailsFetcher(aws.AwsS3BucketInventoryResourceType, NewS3BucketInventoryDetailsFetcher(provider, deserializer)) + remoteLibrary.AddEnumerator(NewS3BucketNotificationEnumerator(s3Repository, factory, provider.Config)) + remoteLibrary.AddDetailsFetcher(aws.AwsS3BucketNotificationResourceType, NewS3BucketNotificationDetailsFetcher(provider, deserializer)) supplierLibrary.AddSupplier(NewS3BucketAnalyticSupplier(provider, s3Repository, deserializer)) supplierLibrary.AddSupplier(NewS3BucketMetricSupplier(provider, s3Repository, deserializer)) - supplierLibrary.AddSupplier(NewS3BucketNotificationSupplier(provider, s3Repository, deserializer)) supplierLibrary.AddSupplier(NewS3BucketPolicySupplier(provider, s3Repository, deserializer)) supplierLibrary.AddSupplier(NewEC2EipSupplier(provider, ec2repository, deserializer)) supplierLibrary.AddSupplier(NewEC2EipAssociationSupplier(provider, deserializer, ec2repository)) diff --git a/pkg/remote/aws/repository/mock_S3Repository.go b/pkg/remote/aws/repository/mock_S3Repository.go index b03259f8..526ad138 100644 --- a/pkg/remote/aws/repository/mock_S3Repository.go +++ b/pkg/remote/aws/repository/mock_S3Repository.go @@ -33,6 +33,29 @@ func (_m *MockS3Repository) GetBucketLocation(bucketName string) (string, error) return r0, r1 } +// GetBucketNotification provides a mock function with given fields: bucketName, region +func (_m *MockS3Repository) GetBucketNotification(bucketName string, region string) (*s3.NotificationConfiguration, error) { + ret := _m.Called(bucketName, region) + + var r0 *s3.NotificationConfiguration + if rf, ok := ret.Get(0).(func(string, string) *s3.NotificationConfiguration); ok { + r0 = rf(bucketName, region) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*s3.NotificationConfiguration) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(string, string) error); ok { + r1 = rf(bucketName, region) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // ListAllBuckets provides a mock function with given fields: func (_m *MockS3Repository) ListAllBuckets() ([]*s3.Bucket, error) { ret := _m.Called() diff --git a/pkg/remote/aws/repository/s3_repository.go b/pkg/remote/aws/repository/s3_repository.go index c672d157..a8c1c248 100644 --- a/pkg/remote/aws/repository/s3_repository.go +++ b/pkg/remote/aws/repository/s3_repository.go @@ -14,6 +14,7 @@ import ( type S3Repository interface { ListAllBuckets() ([]*s3.Bucket, error) + GetBucketNotification(bucketName, region string) (*s3.NotificationConfiguration, error) ListBucketInventoryConfigurations(bucket *s3.Bucket, region string) ([]*s3.InventoryConfiguration, error) ListBucketMetricsConfigurations(bucket *s3.Bucket, region string) ([]*s3.MetricsConfiguration, error) ListBucketAnalyticsConfigurations(bucket *s3.Bucket, region string) ([]*s3.AnalyticsConfiguration, error) @@ -45,6 +46,39 @@ func (s *s3Repository) ListAllBuckets() ([]*s3.Bucket, error) { return out.Buckets, nil } +func (s *s3Repository) GetBucketNotification(bucketName, region string) (*s3.NotificationConfiguration, error) { + cacheKey := fmt.Sprintf("s3GetBucketNotification_%s_%s", bucketName, region) + if v := s.cache.Get(cacheKey); v != nil { + return v.(*s3.NotificationConfiguration), nil + } + bucketNotificationConfig, err := s.clientFactory. + GetS3Client(&awssdk.Config{Region: ®ion}). + GetBucketNotificationConfiguration( + &s3.GetBucketNotificationConfigurationRequest{Bucket: &bucketName}, + ) + if err != nil { + return nil, errors.Wrapf( + err, + "Error listing bucket notification configuration %s", + bucketName, + ) + } + + result := bucketNotificationConfig + if s.notificationIsEmpty(bucketNotificationConfig) { + result = nil + } + + s.cache.Put(cacheKey, result) + return result, nil +} + +func (s *s3Repository) notificationIsEmpty(notification *s3.NotificationConfiguration) bool { + return notification.TopicConfigurations == nil && + notification.QueueConfigurations == nil && + notification.LambdaFunctionConfigurations == nil +} + func (s *s3Repository) ListBucketInventoryConfigurations(bucket *s3.Bucket, region string) ([]*s3.InventoryConfiguration, error) { cacheKey := fmt.Sprintf("s3ListBucketInventoryConfigurations_%s_%s", *bucket.Name, region) if v := s.cache.Get(cacheKey); v != nil { diff --git a/pkg/remote/aws/repository/s3_repository_test.go b/pkg/remote/aws/repository/s3_repository_test.go index 22cecce3..d62f83b4 100644 --- a/pkg/remote/aws/repository/s3_repository_test.go +++ b/pkg/remote/aws/repository/s3_repository_test.go @@ -89,6 +89,159 @@ func Test_s3Repository_ListAllBuckets(t *testing.T) { } } +func Test_s3Repository_GetBucketNotification(t *testing.T) { + + tests := []struct { + name string + bucketName, region string + mocks func(client *awstest.MockFakeS3) + want *s3.NotificationConfiguration + wantErr string + }{ + { + name: "get empty bucket notification", + bucketName: "test-bucket", + region: "us-east-1", + mocks: func(client *awstest.MockFakeS3) { + client.On("GetBucketNotificationConfiguration", &s3.GetBucketNotificationConfigurationRequest{ + Bucket: aws.String("test-bucket"), + }).Return( + &s3.NotificationConfiguration{}, + nil, + ).Once() + }, + want: nil, + }, + { + name: "get bucket notification with lambda config", + bucketName: "test-bucket", + region: "us-east-1", + mocks: func(client *awstest.MockFakeS3) { + client.On("GetBucketNotificationConfiguration", &s3.GetBucketNotificationConfigurationRequest{ + Bucket: aws.String("test-bucket"), + }).Return( + &s3.NotificationConfiguration{ + LambdaFunctionConfigurations: []*s3.LambdaFunctionConfiguration{ + { + Id: aws.String("test"), + }, + }, + }, + nil, + ).Once() + }, + want: &s3.NotificationConfiguration{ + LambdaFunctionConfigurations: []*s3.LambdaFunctionConfiguration{ + { + Id: aws.String("test"), + }, + }, + }, + }, + { + name: "get bucket notification with queue config", + bucketName: "test-bucket", + region: "us-east-1", + mocks: func(client *awstest.MockFakeS3) { + client.On("GetBucketNotificationConfiguration", &s3.GetBucketNotificationConfigurationRequest{ + Bucket: aws.String("test-bucket"), + }).Return( + &s3.NotificationConfiguration{ + QueueConfigurations: []*s3.QueueConfiguration{ + { + Id: awssdk.String("test"), + }, + }, + }, + nil, + ).Once() + }, + want: &s3.NotificationConfiguration{ + QueueConfigurations: []*s3.QueueConfiguration{ + { + Id: awssdk.String("test"), + }, + }, + }, + }, + { + name: "get bucket notification with topic config", + bucketName: "test-bucket", + region: "us-east-1", + mocks: func(client *awstest.MockFakeS3) { + client.On("GetBucketNotificationConfiguration", &s3.GetBucketNotificationConfigurationRequest{ + Bucket: aws.String("test-bucket"), + }).Return( + &s3.NotificationConfiguration{ + TopicConfigurations: []*s3.TopicConfiguration{ + { + Id: awssdk.String("test"), + }, + }, + }, + nil, + ).Once() + }, + want: &s3.NotificationConfiguration{ + TopicConfigurations: []*s3.TopicConfiguration{ + { + Id: awssdk.String("test"), + }, + }, + }, + }, + { + name: "get bucket location when error", + bucketName: "test-bucket", + region: "us-east-1", + mocks: func(client *awstest.MockFakeS3) { + client.On("GetBucketNotificationConfiguration", &s3.GetBucketNotificationConfigurationRequest{ + Bucket: aws.String("test-bucket"), + }).Return( + nil, + awserr.New("UnknownError", "aws error", nil), + ).Once() + }, + wantErr: "Error listing bucket notification configuration test-bucket: UnknownError: aws error", + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + store := cache.New(1) + mockedClient := &awstest.MockFakeS3{} + tt.mocks(mockedClient) + factory := client.MockAwsClientFactoryInterface{} + factory.On("GetS3Client", &aws.Config{Region: &tt.region}).Return(mockedClient).Once() + r := NewS3Repository(&factory, store) + got, err := r.GetBucketNotification(tt.bucketName, tt.region) + factory.AssertExpectations(t) + if err != nil && tt.wantErr == "" { + t.Fatalf("Unexpected error %+v", err) + } + if err != nil { + assert.Equal(t, tt.wantErr, err.Error()) + } + + if err == nil && tt.want != nil { + // Check that results were cached + cachedData, err := r.GetBucketNotification(tt.bucketName, tt.region) + assert.NoError(t, err) + assert.Equal(t, got, cachedData) + assert.IsType(t, &s3.NotificationConfiguration{}, store.Get(fmt.Sprintf("s3GetBucketNotification_%s_%s", tt.bucketName, tt.region))) + } + + changelog, err := diff.Diff(got, tt.want) + assert.Nil(t, err) + if len(changelog) > 0 { + for _, change := range changelog { + t.Errorf("%s: %s -> %s", strings.Join(change.Path, "."), change.From, change.To) + } + t.Fail() + } + }) + } +} + func Test_s3Repository_ListBucketInventoryConfigurations(t *testing.T) { tests := []struct { name string diff --git a/pkg/remote/aws/s3_bucket_notification_detail_fetcher.go b/pkg/remote/aws/s3_bucket_notification_detail_fetcher.go new file mode 100644 index 00000000..b14fe9bf --- /dev/null +++ b/pkg/remote/aws/s3_bucket_notification_detail_fetcher.go @@ -0,0 +1,38 @@ +package aws + +import ( + "github.com/cloudskiff/driftctl/pkg/resource" + "github.com/cloudskiff/driftctl/pkg/resource/aws" + "github.com/cloudskiff/driftctl/pkg/terraform" +) + +type S3BucketNotificationDetailsFetcher struct { + reader terraform.ResourceReader + deserializer *resource.Deserializer +} + +func NewS3BucketNotificationDetailsFetcher(provider terraform.ResourceReader, deserializer *resource.Deserializer) *S3BucketNotificationDetailsFetcher { + return &S3BucketNotificationDetailsFetcher{ + reader: provider, + deserializer: deserializer, + } +} + +func (r *S3BucketNotificationDetailsFetcher) ReadDetails(res resource.Resource) (resource.Resource, error) { + ctyVal, err := r.reader.ReadResource(terraform.ReadResourceArgs{ + Ty: aws.AwsS3BucketNotificationResourceType, + ID: res.TerraformId(), + Attributes: map[string]string{ + "alias": *res.Attributes().GetString("region"), + }, + }) + if err != nil { + return nil, err + } + deserializedRes, err := r.deserializer.DeserializeOne(aws.AwsS3BucketNotificationResourceType, *ctyVal) + if err != nil { + return nil, err + } + + return deserializedRes, nil +} diff --git a/pkg/remote/aws/s3_bucket_notification_enumerator.go b/pkg/remote/aws/s3_bucket_notification_enumerator.go new file mode 100644 index 00000000..08d914aa --- /dev/null +++ b/pkg/remote/aws/s3_bucket_notification_enumerator.go @@ -0,0 +1,77 @@ +package aws + +import ( + "github.com/cloudskiff/driftctl/pkg/remote/aws/repository" + remoteerror "github.com/cloudskiff/driftctl/pkg/remote/error" + tf "github.com/cloudskiff/driftctl/pkg/remote/terraform" + "github.com/cloudskiff/driftctl/pkg/resource" + "github.com/cloudskiff/driftctl/pkg/resource/aws" + "github.com/sirupsen/logrus" +) + +type S3BucketNotificationEnumerator struct { + repository repository.S3Repository + factory resource.ResourceFactory + providerConfig tf.TerraformProviderConfig +} + +func NewS3BucketNotificationEnumerator(repo repository.S3Repository, factory resource.ResourceFactory, providerConfig tf.TerraformProviderConfig) *S3BucketNotificationEnumerator { + return &S3BucketNotificationEnumerator{ + repository: repo, + factory: factory, + providerConfig: providerConfig, + } +} + +func (e *S3BucketNotificationEnumerator) SupportedType() resource.ResourceType { + return aws.AwsS3BucketNotificationResourceType +} + +func (e *S3BucketNotificationEnumerator) Enumerate() ([]resource.Resource, error) { + buckets, err := e.repository.ListAllBuckets() + if err != nil { + return nil, remoteerror.NewResourceEnumerationErrorWithType(err, string(e.SupportedType()), aws.AwsS3BucketResourceType) + } + + results := make([]resource.Resource, len(buckets)) + + for _, bucket := range buckets { + region, err := e.repository.GetBucketLocation(*bucket.Name) + if err != nil { + return nil, err + } + if region == "" || region != e.providerConfig.DefaultAlias { + logrus.WithFields(logrus.Fields{ + "region": region, + "bucket": *bucket.Name, + }).Debug("Skipped bucket") + continue + } + + notification, err := e.repository.GetBucketNotification(*bucket.Name, region) + if err != nil { + return nil, remoteerror.NewResourceEnumerationError(err, string(e.SupportedType())) + } + + if notification == nil { + logrus.WithFields(logrus.Fields{ + "region": region, + "bucket": *bucket.Name, + }).Debug("Skipped empty bucket notification") + continue + } + + results = append( + results, + e.factory.CreateAbstractResource( + string(e.SupportedType()), + *bucket.Name, + map[string]interface{}{ + "region": region, + }, + ), + ) + } + + return results, err +} diff --git a/pkg/remote/aws/s3_bucket_notification_supplier.go b/pkg/remote/aws/s3_bucket_notification_supplier.go deleted file mode 100644 index 4d82703b..00000000 --- a/pkg/remote/aws/s3_bucket_notification_supplier.go +++ /dev/null @@ -1,81 +0,0 @@ -package aws - -import ( - "github.com/cloudskiff/driftctl/pkg/remote/aws/repository" - remoteerror "github.com/cloudskiff/driftctl/pkg/remote/error" - tf "github.com/cloudskiff/driftctl/pkg/remote/terraform" - "github.com/cloudskiff/driftctl/pkg/resource" - "github.com/cloudskiff/driftctl/pkg/resource/aws" - - "github.com/cloudskiff/driftctl/pkg/terraform" - "github.com/zclconf/go-cty/cty" -) - -type S3BucketNotificationSupplier struct { - reader terraform.ResourceReader - deserializer *resource.Deserializer - repository repository.S3Repository - runner *terraform.ParallelResourceReader - providerConfig tf.TerraformProviderConfig -} - -func NewS3BucketNotificationSupplier(provider *AWSTerraformProvider, repository repository.S3Repository, deserializer *resource.Deserializer) *S3BucketNotificationSupplier { - return &S3BucketNotificationSupplier{ - provider, - deserializer, - repository, - terraform.NewParallelResourceReader(provider.Runner().SubRunner()), - provider.Config, - } -} - -func (s *S3BucketNotificationSupplier) Resources() ([]resource.Resource, error) { - buckets, err := s.repository.ListAllBuckets() - if err != nil { - return nil, remoteerror.NewResourceEnumerationErrorWithType(err, aws.AwsS3BucketNotificationResourceType, aws.AwsS3BucketResourceType) - } - - for _, bucket := range buckets { - bucket := *bucket - region, err := s.repository.GetBucketLocation(*bucket.Name) - if err != nil { - return nil, err - } - if region == "" || region != s.providerConfig.DefaultAlias { - continue - } - s.runner.Run(func() (cty.Value, error) { - s3BucketPolicy, err := s.reader.ReadResource(terraform.ReadResourceArgs{ - Ty: aws.AwsS3BucketNotificationResourceType, - ID: *bucket.Name, - Attributes: map[string]string{ - "alias": region, - }, - }) - if err != nil { - return cty.NilVal, err - } - return *s3BucketPolicy, err - }) - } - ctyVals, err := s.runner.Wait() - if err != nil { - return nil, err - } - deserializedValues, err := s.deserializer.Deserialize(aws.AwsS3BucketNotificationResourceType, ctyVals) - results := make([]resource.Resource, 0, len(deserializedValues)) - if err != nil { - return deserializedValues, err - } - for _, val := range deserializedValues { - res, _ := val.(*resource.AbstractResource) - - if ((*res.Attrs)["lambda_function"] != nil && len((*res.Attrs)["lambda_function"].([]interface{})) > 0) || - ((*res.Attrs)["queue"] != nil && len((*res.Attrs)["queue"].([]interface{})) > 0) || - ((*res.Attrs)["topic"] != nil && len((*res.Attrs)["topic"].([]interface{})) > 0) { - results = append(results, res) - } - - } - return results, nil -} diff --git a/pkg/remote/aws/s3_bucket_notification_supplier_test.go b/pkg/remote/aws/s3_bucket_notification_supplier_test.go deleted file mode 100644 index f5d5c473..00000000 --- a/pkg/remote/aws/s3_bucket_notification_supplier_test.go +++ /dev/null @@ -1,139 +0,0 @@ -package aws - -import ( - "context" - "testing" - - awssdk "github.com/aws/aws-sdk-go/aws" - "github.com/aws/aws-sdk-go/aws/awserr" - "github.com/aws/aws-sdk-go/service/s3" - "github.com/cloudskiff/driftctl/pkg/parallel" - "github.com/cloudskiff/driftctl/pkg/remote/aws/client" - "github.com/cloudskiff/driftctl/pkg/remote/aws/repository" - "github.com/cloudskiff/driftctl/pkg/remote/cache" - remoteerror "github.com/cloudskiff/driftctl/pkg/remote/error" - tf "github.com/cloudskiff/driftctl/pkg/remote/terraform" - "github.com/cloudskiff/driftctl/pkg/resource" - resourceaws "github.com/cloudskiff/driftctl/pkg/resource/aws" - testresource "github.com/cloudskiff/driftctl/test/resource" - - "github.com/cloudskiff/driftctl/pkg/terraform" - "github.com/cloudskiff/driftctl/test" - "github.com/cloudskiff/driftctl/test/goldenfile" - "github.com/cloudskiff/driftctl/test/mocks" - "github.com/stretchr/testify/assert" -) - -func TestS3BucketNotificationSupplier_Resources(t *testing.T) { - - tests := []struct { - test string - dirName string - mocks func(repository *repository.MockS3Repository) - wantErr error - }{ - { - test: "single bucket without notifications", - dirName: "s3_bucket_notifications_no_notif", - mocks: func(repository *repository.MockS3Repository) { - repository.On( - "ListAllBuckets", - ).Return([]*s3.Bucket{ - {Name: awssdk.String("dritftctl-test-no-notifications")}, - }, nil) - - repository.On( - "GetBucketLocation", - "dritftctl-test-no-notifications", - ).Return( - "eu-west-3", - nil, - ) - }, - }, - { - test: "multiple bucket with notifications", dirName: "s3_bucket_notifications_multiple", - mocks: func(repository *repository.MockS3Repository) { - repository.On( - "ListAllBuckets", - ).Return([]*s3.Bucket{ - {Name: awssdk.String("bucket-martin-test-drift")}, - {Name: awssdk.String("bucket-martin-test-drift2")}, - {Name: awssdk.String("bucket-martin-test-drift3")}, - }, nil) - - repository.On( - "GetBucketLocation", - "bucket-martin-test-drift", - ).Return( - "eu-west-1", - nil, - ) - - repository.On( - "GetBucketLocation", - "bucket-martin-test-drift2", - ).Return( - "eu-west-3", - nil, - ) - - repository.On( - "GetBucketLocation", - "bucket-martin-test-drift3", - ).Return( - "ap-northeast-1", - nil, - ) - }, - }, - { - test: "Cannot list bucket", dirName: "s3_bucket_notifications_list_bucket", - mocks: func(repository *repository.MockS3Repository) { - repository.On("ListAllBuckets").Return(nil, awserr.NewRequestFailure(nil, 403, "")) - }, - wantErr: remoteerror.NewResourceEnumerationErrorWithType(awserr.NewRequestFailure(nil, 403, ""), resourceaws.AwsS3BucketNotificationResourceType, resourceaws.AwsS3BucketResourceType), - }, - } - for _, tt := range tests { - shouldUpdate := tt.dirName == *goldenfile.Update - - providerLibrary := terraform.NewProviderLibrary() - supplierLibrary := resource.NewSupplierLibrary() - - repo := testresource.InitFakeSchemaRepository("aws", "3.19.0") - resourceaws.InitResourcesMetadata(repo) - factory := terraform.NewTerraformResourceFactory(repo) - - deserializer := resource.NewDeserializer(factory) - if shouldUpdate { - provider, err := InitTestAwsProvider(providerLibrary) - if err != nil { - t.Fatal(err) - } - repository := repository.NewS3Repository(client.NewAWSClientFactory(provider.session), cache.New(0)) - supplierLibrary.AddSupplier(NewS3BucketNotificationSupplier(provider, repository, deserializer)) - } - - t.Run(tt.test, func(t *testing.T) { - - mock := repository.MockS3Repository{} - tt.mocks(&mock) - - provider := mocks.NewMockedGoldenTFProvider(tt.dirName, providerLibrary.Provider(terraform.AWS), shouldUpdate) - s := &S3BucketNotificationSupplier{ - provider, - deserializer, - &mock, - terraform.NewParallelResourceReader(parallel.NewParallelRunner(context.TODO(), 10)), - tf.TerraformProviderConfig{ - Name: "test", - DefaultAlias: "eu-west-3", - }, - } - got, err := s.Resources() - assert.Equal(t, err, tt.wantErr) - test.CtyTestDiff(got, tt.dirName, provider, deserializer, shouldUpdate, t) - }) - } -} diff --git a/pkg/remote/s3_scanner_test.go b/pkg/remote/s3_scanner_test.go index 77e393ac..e1b1b26b 100644 --- a/pkg/remote/s3_scanner_test.go +++ b/pkg/remote/s3_scanner_test.go @@ -285,3 +285,178 @@ func TestS3BucketInventory(t *testing.T) { }) } } + +func TestS3BucketNotification(t *testing.T) { + + tests := []struct { + test string + dirName string + mocks func(repository *repository.MockS3Repository) + wantErr error + }{ + { + test: "single bucket without notifications", + dirName: "s3_bucket_notifications_no_notif", + mocks: func(repository *repository.MockS3Repository) { + repository.On( + "ListAllBuckets", + ).Return([]*s3.Bucket{ + {Name: awssdk.String("dritftctl-test-no-notifications")}, + }, nil) + + repository.On( + "GetBucketLocation", + "dritftctl-test-no-notifications", + ).Return( + "eu-west-3", + nil, + ) + + repository.On( + "GetBucketNotification", + "dritftctl-test-no-notifications", + "eu-west-3", + ).Return( + nil, + nil, + ) + }, + }, + { + test: "multiple bucket with notifications", dirName: "s3_bucket_notifications_multiple", + mocks: func(repository *repository.MockS3Repository) { + repository.On( + "ListAllBuckets", + ).Return([]*s3.Bucket{ + {Name: awssdk.String("bucket-martin-test-drift")}, + {Name: awssdk.String("bucket-martin-test-drift2")}, + {Name: awssdk.String("bucket-martin-test-drift3")}, + }, nil) + + repository.On( + "GetBucketLocation", + "bucket-martin-test-drift", + ).Return( + "eu-west-1", + nil, + ) + + repository.On( + "GetBucketLocation", + "bucket-martin-test-drift2", + ).Return( + "eu-west-3", + nil, + ) + + repository.On( + "GetBucketNotification", + "bucket-martin-test-drift2", + "eu-west-3", + ).Return( + &s3.NotificationConfiguration{ + LambdaFunctionConfigurations: []*s3.LambdaFunctionConfiguration{ + { + Id: awssdk.String("tf-s3-lambda-20201103165354926600000001"), + }, + { + Id: awssdk.String("tf-s3-lambda-20201103165354926600000002"), + }, + }, + }, + nil, + ) + + repository.On( + "GetBucketLocation", + "bucket-martin-test-drift3", + ).Return( + "ap-northeast-1", + nil, + ) + }, + }, + { + test: "Cannot get bucket notification", dirName: "s3_bucket_notifications_list_bucket", + mocks: func(repository *repository.MockS3Repository) { + repository.On( + "ListAllBuckets", + ).Return([]*s3.Bucket{ + {Name: awssdk.String("dritftctl-test-notifications-error")}, + }, nil) + repository.On( + "GetBucketLocation", + "dritftctl-test-notifications-error", + ).Return( + "eu-west-3", + nil, + ) + repository.On("GetBucketNotification", "dritftctl-test-notifications-error", "eu-west-3").Return(nil, awserr.NewRequestFailure(nil, 403, "")) + }, + wantErr: remoteerror.NewResourceEnumerationError(awserr.NewRequestFailure(nil, 403, ""), resourceaws.AwsS3BucketNotificationResourceType), + }, + { + test: "Cannot list bucket", dirName: "s3_bucket_notifications_list_bucket", + mocks: func(repository *repository.MockS3Repository) { + repository.On("ListAllBuckets").Return(nil, awserr.NewRequestFailure(nil, 403, "")) + }, + wantErr: remoteerror.NewResourceEnumerationErrorWithType(awserr.NewRequestFailure(nil, 403, ""), resourceaws.AwsS3BucketNotificationResourceType, resourceaws.AwsS3BucketResourceType), + }, + } + + schemaRepository := testresource.InitFakeSchemaRepository("aws", "3.19.0") + resourceaws.InitResourcesMetadata(schemaRepository) + factory := terraform.NewTerraformResourceFactory(schemaRepository) + deserializer := resource.NewDeserializer(factory) + alerter := &mocks.AlerterInterface{} + + for _, c := range tests { + t.Run(c.test, func(tt *testing.T) { + shouldUpdate := c.dirName == *goldenfile.Update + + session := session.Must(session.NewSessionWithOptions(session.Options{ + SharedConfigState: session.SharedConfigEnable, + })) + + scanOptions := ScannerOptions{Deep: true} + providerLibrary := terraform.NewProviderLibrary() + remoteLibrary := common.NewRemoteLibrary() + + // Initialize mocks + fakeRepo := &repository.MockS3Repository{} + c.mocks(fakeRepo) + var repo repository.S3Repository = fakeRepo + providerVersion := "3.19.0" + realProvider, err := terraform2.InitTestAwsProvider(providerLibrary, providerVersion) + if err != nil { + t.Fatal(err) + } + provider := terraform2.NewFakeTerraformProvider(realProvider) + provider.WithResponse(c.dirName) + + // Replace mock by real resources if we are in update mode + if shouldUpdate { + err := realProvider.Init() + if err != nil { + t.Fatal(err) + } + provider.ShouldUpdate() + repo = repository.NewS3Repository(client.NewAWSClientFactory(session), cache.New(0)) + } + + remoteLibrary.AddEnumerator(aws.NewS3BucketNotificationEnumerator(repo, factory, tf.TerraformProviderConfig{ + Name: "test", + DefaultAlias: "eu-west-3", + })) + remoteLibrary.AddDetailsFetcher(resourceaws.AwsS3BucketNotificationResourceType, aws.NewS3BucketNotificationDetailsFetcher(provider, deserializer)) + + s := NewScanner(nil, remoteLibrary, alerter, scanOptions) + got, err := s.Resources() + assert.Equal(tt, err, c.wantErr) + if err != nil { + return + } + test.TestAgainstGoldenFile(got, resourceaws.AwsS3BucketNotificationResourceType, c.dirName, provider, deserializer, shouldUpdate, tt) + }) + } +} diff --git a/pkg/remote/aws/test/s3_bucket_notifications_list_bucket/results.golden.json b/pkg/remote/test/s3_bucket_notifications_list_bucket/results.golden.json similarity index 100% rename from pkg/remote/aws/test/s3_bucket_notifications_list_bucket/results.golden.json rename to pkg/remote/test/s3_bucket_notifications_list_bucket/results.golden.json diff --git a/pkg/remote/aws/test/s3_bucket_notifications_list_bucket/schema.golden.json b/pkg/remote/test/s3_bucket_notifications_list_bucket/schema.golden.json similarity index 100% rename from pkg/remote/aws/test/s3_bucket_notifications_list_bucket/schema.golden.json rename to pkg/remote/test/s3_bucket_notifications_list_bucket/schema.golden.json diff --git a/pkg/remote/aws/test/s3_bucket_notifications_multiple/aws_s3_bucket_notification-bucket-martin-test-drift2.res.golden.json b/pkg/remote/test/s3_bucket_notifications_multiple/aws_s3_bucket_notification-bucket-martin-test-drift2-eu-west-3.res.golden.json similarity index 100% rename from pkg/remote/aws/test/s3_bucket_notifications_multiple/aws_s3_bucket_notification-bucket-martin-test-drift2.res.golden.json rename to pkg/remote/test/s3_bucket_notifications_multiple/aws_s3_bucket_notification-bucket-martin-test-drift2-eu-west-3.res.golden.json diff --git a/pkg/remote/aws/test/s3_bucket_notifications_multiple/results.golden.json b/pkg/remote/test/s3_bucket_notifications_multiple/results.golden.json similarity index 100% rename from pkg/remote/aws/test/s3_bucket_notifications_multiple/results.golden.json rename to pkg/remote/test/s3_bucket_notifications_multiple/results.golden.json diff --git a/pkg/remote/aws/test/s3_bucket_notifications_multiple/schema.golden.json b/pkg/remote/test/s3_bucket_notifications_multiple/schema.golden.json similarity index 100% rename from pkg/remote/aws/test/s3_bucket_notifications_multiple/schema.golden.json rename to pkg/remote/test/s3_bucket_notifications_multiple/schema.golden.json diff --git a/pkg/remote/aws/test/s3_bucket_notifications_no_notif/aws_s3_bucket_notification-dritftctl-test-no-notifications.res.golden.json b/pkg/remote/test/s3_bucket_notifications_no_notif/aws_s3_bucket_notification-dritftctl-test-no-notifications.res.golden.json similarity index 100% rename from pkg/remote/aws/test/s3_bucket_notifications_no_notif/aws_s3_bucket_notification-dritftctl-test-no-notifications.res.golden.json rename to pkg/remote/test/s3_bucket_notifications_no_notif/aws_s3_bucket_notification-dritftctl-test-no-notifications.res.golden.json diff --git a/pkg/remote/aws/test/s3_bucket_notifications_no_notif/results.golden.json b/pkg/remote/test/s3_bucket_notifications_no_notif/results.golden.json similarity index 100% rename from pkg/remote/aws/test/s3_bucket_notifications_no_notif/results.golden.json rename to pkg/remote/test/s3_bucket_notifications_no_notif/results.golden.json diff --git a/pkg/remote/aws/test/s3_bucket_notifications_no_notif/schema.golden.json b/pkg/remote/test/s3_bucket_notifications_no_notif/schema.golden.json similarity index 100% rename from pkg/remote/aws/test/s3_bucket_notifications_no_notif/schema.golden.json rename to pkg/remote/test/s3_bucket_notifications_no_notif/schema.golden.json diff --git a/pkg/remote/aws/test/s3_bucket_notifications_no_notif/terraform.tf b/pkg/remote/test/s3_bucket_notifications_no_notif/terraform.tf similarity index 100% rename from pkg/remote/aws/test/s3_bucket_notifications_no_notif/terraform.tf rename to pkg/remote/test/s3_bucket_notifications_no_notif/terraform.tf