diff --git a/pkg/analyser/analyzer.go b/pkg/analyser/analyzer.go index 15c37df22..e4f3f3197 100644 --- a/pkg/analyser/analyzer.go +++ b/pkg/analyser/analyzer.go @@ -36,8 +36,13 @@ func (c *ComputedDiffAlert) ShouldIgnoreResource() bool { return false } +type AnalyzerOptions struct { + Deep bool +} + type Analyzer struct { alerter *alerter.Alerter + options AnalyzerOptions } type Filter interface { @@ -45,8 +50,8 @@ type Filter interface { IsFieldIgnored(res resource.Resource, path []string) bool } -func NewAnalyzer(alerter *alerter.Alerter) Analyzer { - return Analyzer{alerter} +func NewAnalyzer(alerter *alerter.Alerter, options AnalyzerOptions) Analyzer { + return Analyzer{alerter, options} } func (a Analyzer) Analyze(remoteResources, resourcesFromState []resource.Resource, filter Filter) (Analysis, error) { @@ -78,6 +83,11 @@ func (a Analyzer) Analyze(remoteResources, resourcesFromState []resource.Resourc filteredRemoteResource = removeResourceByIndex(i, filteredRemoteResource) analysis.AddManaged(stateRes) + // Stop there if we are not in deep mode, we do not want to compute diffs + if !a.options.Deep { + continue + } + var delta diff.Changelog delta, _ = diff.Diff(stateRes.Attributes(), remoteRes.Attributes()) diff --git a/pkg/analyser/analyzer_test.go b/pkg/analyser/analyzer_test.go index e616d291c..0ac0269a9 100644 --- a/pkg/analyser/analyzer_test.go +++ b/pkg/analyser/analyzer_test.go @@ -955,7 +955,7 @@ func TestAnalyze(t *testing.T) { repo := testresource.InitFakeSchemaRepository("aws", "3.19.0") aws.InitResourcesMetadata(repo) - analyzer := NewAnalyzer(al) + analyzer := NewAnalyzer(al, AnalyzerOptions{Deep: true}) for _, res := range c.cloud { addSchemaToRes(res, repo) diff --git a/pkg/cmd/scan.go b/pkg/cmd/scan.go index 826f4768a..f8e76032c 100644 --- a/pkg/cmd/scan.go +++ b/pkg/cmd/scan.go @@ -9,6 +9,7 @@ import ( "syscall" "time" + "github.com/cloudskiff/driftctl/pkg/remote/common" "github.com/cloudskiff/driftctl/pkg/telemetry" "github.com/fatih/color" "github.com/mitchellh/go-homedir" @@ -31,7 +32,7 @@ import ( ) func NewScanCmd() *cobra.Command { - opts := &pkg.ScanOptions{} + opts := &pkg.ScanOptions{Deep: true} opts.BackendOptions = &backend.Options{} cmd := &cobra.Command{ @@ -184,8 +185,10 @@ func scanRun(opts *pkg.ScanOptions) error { signal.Notify(c, os.Interrupt, syscall.SIGTERM) alerter := alerter.NewAlerter() + providerLibrary := terraform.NewProviderLibrary() supplierLibrary := resource.NewSupplierLibrary() + remoteLibrary := common.NewRemoteLibrary() iacProgress := globaloutput.NewProgress("Scanning states", "Scanned states", true) scanProgress := globaloutput.NewProgress("Scanning resources", "Scanned resources", false) @@ -194,7 +197,7 @@ func scanRun(opts *pkg.ScanOptions) error { resFactory := terraform.NewTerraformResourceFactory(resourceSchemaRepository) - err := remote.Activate(opts.To, opts.ProviderVersion, alerter, providerLibrary, supplierLibrary, scanProgress, resourceSchemaRepository, resFactory, opts.ConfigDir) + err := remote.Activate(opts.To, opts.ProviderVersion, alerter, providerLibrary, supplierLibrary, remoteLibrary, scanProgress, resourceSchemaRepository, resFactory, opts.ConfigDir) if err != nil { return err } @@ -206,7 +209,7 @@ func scanRun(opts *pkg.ScanOptions) error { logrus.Trace("Exited") }() - scanner := pkg.NewScanner(supplierLibrary.Suppliers(), alerter) + scanner := pkg.NewScanner(supplierLibrary.Suppliers(), remoteLibrary, alerter, pkg.ScannerOptions{Deep: opts.Deep}) iacSupplier, err := supplier.GetIACSupplier(opts.From, providerLibrary, opts.BackendOptions, iacProgress, resFactory) if err != nil { diff --git a/pkg/driftctl.go b/pkg/driftctl.go index 7c688c20f..a676c746f 100644 --- a/pkg/driftctl.go +++ b/pkg/driftctl.go @@ -32,6 +32,7 @@ type ScanOptions struct { ProviderVersion string ConfigDir string DriftignorePath string + Deep bool } type DriftCTL struct { @@ -58,7 +59,7 @@ func NewDriftCTL(remoteSupplier resource.Supplier, remoteSupplier, iacSupplier, alerter, - analyser.NewAnalyzer(alerter), + analyser.NewAnalyzer(alerter, analyser.AnalyzerOptions{Deep: opts.Deep}), resFactory, scanProgress, iacProgress, diff --git a/pkg/driftctl_test.go b/pkg/driftctl_test.go index 2c8f1b950..24e2344b6 100644 --- a/pkg/driftctl_test.go +++ b/pkg/driftctl_test.go @@ -87,7 +87,7 @@ func runTest(t *testing.T, cases TestCases) { } if c.options == nil { - c.options = &pkg.ScanOptions{} + c.options = &pkg.ScanOptions{Deep: true} } scanProgress := &output.MockProgress{} @@ -140,7 +140,7 @@ func TestDriftctlRun_BasicBehavior(t *testing.T) { result.AssertInfrastructureIsInSync() }, options: func(t *testing.T) *pkg.ScanOptions { - return &pkg.ScanOptions{} + return &pkg.ScanOptions{Deep: true} }(t), }, { @@ -152,9 +152,6 @@ func TestDriftctlRun_BasicBehavior(t *testing.T) { assert: func(result *test.ScanResult, err error) { result.AssertDeletedCount(1) }, - options: func(t *testing.T) *pkg.ScanOptions { - return &pkg.ScanOptions{} - }(t), }, { name: "we should have unmanaged resource", @@ -165,9 +162,6 @@ func TestDriftctlRun_BasicBehavior(t *testing.T) { assert: func(result *test.ScanResult, err error) { result.AssertUnmanagedCount(1) }, - options: func(t *testing.T) *pkg.ScanOptions { - return &pkg.ScanOptions{} - }(t), }, { name: "we should have changes of field update", @@ -199,9 +193,6 @@ func TestDriftctlRun_BasicBehavior(t *testing.T) { Computed: false, }) }, - options: func(t *testing.T) *pkg.ScanOptions { - return &pkg.ScanOptions{} - }(t), }, { name: "we should have changes on computed field", @@ -235,9 +226,6 @@ func TestDriftctlRun_BasicBehavior(t *testing.T) { Computed: true, }) }, - options: func(t *testing.T) *pkg.ScanOptions { - return &pkg.ScanOptions{} - }(t), }, { name: "we should have changes on deleted field", @@ -271,9 +259,6 @@ func TestDriftctlRun_BasicBehavior(t *testing.T) { Computed: false, }) }, - options: func(t *testing.T) *pkg.ScanOptions { - return &pkg.ScanOptions{} - }(t), }, { name: "we should have changes of added field", @@ -307,9 +292,6 @@ func TestDriftctlRun_BasicBehavior(t *testing.T) { Computed: false, }) }, - options: func(t *testing.T) *pkg.ScanOptions { - return &pkg.ScanOptions{} - }(t), }, { name: "we should ignore default AWS IAM role when strict mode is disabled", @@ -396,6 +378,7 @@ func TestDriftctlRun_BasicBehavior(t *testing.T) { options: func(t *testing.T) *pkg.ScanOptions { return &pkg.ScanOptions{ StrictMode: false, + Deep: true, } }(t), }, @@ -484,6 +467,7 @@ func TestDriftctlRun_BasicBehavior(t *testing.T) { options: func(t *testing.T) *pkg.ScanOptions { return &pkg.ScanOptions{ StrictMode: true, + Deep: true, } }(t), }, @@ -581,6 +565,7 @@ func TestDriftctlRun_BasicBehavior(t *testing.T) { return &pkg.ScanOptions{ Filter: f, StrictMode: true, + Deep: true, } }(t), }, @@ -617,7 +602,7 @@ func TestDriftctlRun_BasicFilter(t *testing.T) { t.Fatalf("Unable to build filter expression: %s\n%s", filterStr, err) } - return &pkg.ScanOptions{Filter: f} + return &pkg.ScanOptions{Filter: f, Deep: true} }(t), }, { @@ -646,7 +631,7 @@ func TestDriftctlRun_BasicFilter(t *testing.T) { t.Fatalf("Unable to build filter expression: %s\n%s", filterStr, err) } - return &pkg.ScanOptions{Filter: f} + return &pkg.ScanOptions{Filter: f, Deep: true} }(t), }, { @@ -677,7 +662,7 @@ func TestDriftctlRun_BasicFilter(t *testing.T) { t.Fatalf("Unable to build filter expression: %s\n%s", filterStr, err) } - return &pkg.ScanOptions{Filter: f} + return &pkg.ScanOptions{Filter: f, Deep: true} }(t), }, } @@ -751,7 +736,7 @@ func TestDriftctlRun_Middlewares(t *testing.T) { t.Fatalf("Unable to build filter expression: %s\n%s", filterStr, err) } - return &pkg.ScanOptions{Filter: f} + return &pkg.ScanOptions{Filter: f, Deep: true} }(t), }, { @@ -864,7 +849,7 @@ func TestDriftctlRun_Middlewares(t *testing.T) { t.Fatalf("Unable to build filter expression: %s\n%s", filterStr, err) } - return &pkg.ScanOptions{Filter: f} + return &pkg.ScanOptions{Filter: f, Deep: true} }(t), }, { @@ -963,7 +948,7 @@ func TestDriftctlRun_Middlewares(t *testing.T) { t.Fatalf("Unable to build filter expression: %s\n%s", filterStr, err) } - return &pkg.ScanOptions{Filter: f} + return &pkg.ScanOptions{Filter: f, Deep: true} }(t), }, { @@ -1026,7 +1011,7 @@ func TestDriftctlRun_Middlewares(t *testing.T) { t.Fatalf("Unable to build filter expression: %s\n%s", filterStr, err) } - return &pkg.ScanOptions{Filter: f} + return &pkg.ScanOptions{Filter: f, Deep: true} }(t), }, { @@ -1088,7 +1073,7 @@ func TestDriftctlRun_Middlewares(t *testing.T) { t.Fatalf("Unable to build filter expression: %s\n%s", filterStr, err) } - return &pkg.ScanOptions{Filter: f} + return &pkg.ScanOptions{Filter: f, Deep: true} }(t), }, { @@ -1404,7 +1389,7 @@ func TestDriftctlRun_Middlewares(t *testing.T) { t.Fatalf("Unable to build filter expression: %s\n%s", filterStr, err) } - return &pkg.ScanOptions{Filter: f} + return &pkg.ScanOptions{Filter: f, Deep: true} }(t), }, { @@ -1542,7 +1527,7 @@ func TestDriftctlRun_Middlewares(t *testing.T) { t.Fatalf("Unable to build filter expression: %s\n%s", filterStr, err) } - return &pkg.ScanOptions{Filter: f} + return &pkg.ScanOptions{Filter: f, Deep: true} }(t), }, { diff --git a/pkg/remote/aws/init.go b/pkg/remote/aws/init.go index 3447acbab..9e4586f56 100644 --- a/pkg/remote/aws/init.go +++ b/pkg/remote/aws/init.go @@ -6,6 +6,7 @@ import ( "github.com/cloudskiff/driftctl/pkg/remote/aws/client" "github.com/cloudskiff/driftctl/pkg/remote/aws/repository" "github.com/cloudskiff/driftctl/pkg/remote/cache" + "github.com/cloudskiff/driftctl/pkg/remote/common" "github.com/cloudskiff/driftctl/pkg/resource" "github.com/cloudskiff/driftctl/pkg/resource/aws" "github.com/cloudskiff/driftctl/pkg/terraform" @@ -21,6 +22,7 @@ const RemoteAWSTerraform = "aws+tf" func Init(version string, alerter *alerter.Alerter, providerLibrary *terraform.ProviderLibrary, supplierLibrary *resource.SupplierLibrary, + remoteLibrary *common.RemoteLibrary, progress output.Progress, resourceSchemaRepository *resource.SchemaRepository, factory resource.ResourceFactory, @@ -56,7 +58,9 @@ func Init(version string, alerter *alerter.Alerter, deserializer := resource.NewDeserializer(factory) providerLibrary.AddProvider(terraform.AWS, provider) - supplierLibrary.AddSupplier(NewS3BucketSupplier(provider, s3Repository, deserializer)) + remoteLibrary.AddEnumerator(NewS3BucketEnumerator(s3Repository, factory, provider.Config)) + remoteLibrary.AddDetailsFetcher(aws.AwsS3BucketResourceType, NewS3BucketDetailsFetcher(provider, deserializer)) + supplierLibrary.AddSupplier(NewS3BucketAnalyticSupplier(provider, s3Repository, deserializer)) supplierLibrary.AddSupplier(NewS3BucketInventorySupplier(provider, s3Repository, deserializer)) supplierLibrary.AddSupplier(NewS3BucketMetricSupplier(provider, s3Repository, deserializer)) diff --git a/pkg/remote/aws/repository/mock_S3Repository.go b/pkg/remote/aws/repository/mock_S3Repository.go index dfa261eb9..b03259f8a 100644 --- a/pkg/remote/aws/repository/mock_S3Repository.go +++ b/pkg/remote/aws/repository/mock_S3Repository.go @@ -1,4 +1,4 @@ -// Code generated by mockery v2.3.0. DO NOT EDIT. +// Code generated by mockery v0.0.0-dev. DO NOT EDIT. package repository @@ -12,20 +12,20 @@ type MockS3Repository struct { mock.Mock } -// GetBucketLocation provides a mock function with given fields: bucket -func (_m *MockS3Repository) GetBucketLocation(bucket *s3.Bucket) (string, error) { - ret := _m.Called(bucket) +// GetBucketLocation provides a mock function with given fields: bucketName +func (_m *MockS3Repository) GetBucketLocation(bucketName string) (string, error) { + ret := _m.Called(bucketName) var r0 string - if rf, ok := ret.Get(0).(func(*s3.Bucket) string); ok { - r0 = rf(bucket) + if rf, ok := ret.Get(0).(func(string) string); ok { + r0 = rf(bucketName) } else { r0 = ret.Get(0).(string) } var r1 error - if rf, ok := ret.Get(1).(func(*s3.Bucket) error); ok { - r1 = rf(bucket) + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(bucketName) } else { r1 = ret.Error(1) } diff --git a/pkg/remote/aws/repository/s3_repository.go b/pkg/remote/aws/repository/s3_repository.go index f68db2343..c672d1577 100644 --- a/pkg/remote/aws/repository/s3_repository.go +++ b/pkg/remote/aws/repository/s3_repository.go @@ -17,7 +17,7 @@ type S3Repository interface { 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) - GetBucketLocation(bucket *s3.Bucket) (string, error) + GetBucketLocation(bucketName string) (string, error) } type s3Repository struct { @@ -148,19 +148,19 @@ func (s *s3Repository) ListBucketAnalyticsConfigurations(bucket *s3.Bucket, regi return analyticsConfigurationList, nil } -func (s *s3Repository) GetBucketLocation(bucket *s3.Bucket) (string, error) { - cacheKey := fmt.Sprintf("s3GetBucketLocation_%s", *bucket.Name) +func (s *s3Repository) GetBucketLocation(bucketName string) (string, error) { + cacheKey := fmt.Sprintf("s3GetBucketLocation_%s", bucketName) if v := s.cache.Get(cacheKey); v != nil { return v.(string), nil } - bucketLocationRequest := s3.GetBucketLocationInput{Bucket: bucket.Name} + bucketLocationRequest := s3.GetBucketLocationInput{Bucket: &bucketName} bucketLocationResponse, err := s.clientFactory.GetS3Client(nil).GetBucketLocation(&bucketLocationRequest) if err != nil { awsErr, ok := err.(awserr.Error) if ok && awsErr.Code() == s3.ErrCodeNoSuchBucket { logrus.WithFields(logrus.Fields{ - "bucket": *bucket.Name, + "bucket": bucketName, }).Warning("Unable to retrieve bucket region, this may be an inconsistency in S3 api for fresh deleted bucket, skipping ...") return "", nil } diff --git a/pkg/remote/aws/repository/s3_repository_test.go b/pkg/remote/aws/repository/s3_repository_test.go index 47ed850e3..22cecce3c 100644 --- a/pkg/remote/aws/repository/s3_repository_test.go +++ b/pkg/remote/aws/repository/s3_repository_test.go @@ -562,7 +562,7 @@ func Test_s3Repository_GetBucketLocation(t *testing.T) { factory := client.MockAwsClientFactoryInterface{} factory.On("GetS3Client", (*aws.Config)(nil)).Return(mockedClient).Once() r := NewS3Repository(&factory, store) - got, err := r.GetBucketLocation(tt.bucket) + got, err := r.GetBucketLocation(*tt.bucket.Name) factory.AssertExpectations(t) if err != nil && tt.wantErr == "" { t.Fatalf("Unexpected error %+v", err) @@ -573,7 +573,7 @@ func Test_s3Repository_GetBucketLocation(t *testing.T) { if err == nil && tt.want != "" { // Check that results were cached - cachedData, err := r.GetBucketLocation(tt.bucket) + cachedData, err := r.GetBucketLocation(*tt.bucket.Name) assert.NoError(t, err) assert.Equal(t, got, cachedData) assert.IsType(t, "", store.Get(fmt.Sprintf("s3GetBucketLocation_%s", *tt.bucket.Name))) diff --git a/pkg/remote/aws/s3_bucket_analytic_supplier.go b/pkg/remote/aws/s3_bucket_analytic_supplier.go index 91c596b4c..bd05b958a 100644 --- a/pkg/remote/aws/s3_bucket_analytic_supplier.go +++ b/pkg/remote/aws/s3_bucket_analytic_supplier.go @@ -40,7 +40,7 @@ func (s *S3BucketAnalyticSupplier) Resources() ([]resource.Resource, error) { for _, bucket := range buckets { bucket := *bucket - region, err := s.repository.GetBucketLocation(&bucket) + region, err := s.repository.GetBucketLocation(*bucket.Name) if err != nil { return nil, err } diff --git a/pkg/remote/aws/s3_bucket_analytic_supplier_test.go b/pkg/remote/aws/s3_bucket_analytic_supplier_test.go index e67d4e78b..facbcb5bc 100644 --- a/pkg/remote/aws/s3_bucket_analytic_supplier_test.go +++ b/pkg/remote/aws/s3_bucket_analytic_supplier_test.go @@ -47,7 +47,7 @@ func TestS3BucketAnalyticSupplier_Resources(t *testing.T) { repository.On( "GetBucketLocation", - &s3.Bucket{Name: awssdk.String("bucket-martin-test-drift")}, + "bucket-martin-test-drift", ).Return( "eu-west-1", nil, @@ -55,7 +55,7 @@ func TestS3BucketAnalyticSupplier_Resources(t *testing.T) { repository.On( "GetBucketLocation", - &s3.Bucket{Name: awssdk.String("bucket-martin-test-drift2")}, + "bucket-martin-test-drift2", ).Return( "eu-west-3", nil, @@ -63,7 +63,7 @@ func TestS3BucketAnalyticSupplier_Resources(t *testing.T) { repository.On( "GetBucketLocation", - &s3.Bucket{Name: awssdk.String("bucket-martin-test-drift3")}, + "bucket-martin-test-drift3", ).Return( "ap-northeast-1", nil, @@ -101,7 +101,7 @@ func TestS3BucketAnalyticSupplier_Resources(t *testing.T) { ) repository.On( "GetBucketLocation", - &s3.Bucket{Name: awssdk.String("bucket-martin-test-drift")}, + "bucket-martin-test-drift", ).Return( "eu-west-3", nil, diff --git a/pkg/remote/aws/s3_bucket_detail_fetcher.go b/pkg/remote/aws/s3_bucket_detail_fetcher.go new file mode 100644 index 000000000..355ef8310 --- /dev/null +++ b/pkg/remote/aws/s3_bucket_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 S3BucketDetailsFetcher struct { + reader terraform.ResourceReader + deserializer *resource.Deserializer +} + +func NewS3BucketDetailsFetcher(provider *AWSTerraformProvider, deserializer *resource.Deserializer) *S3BucketDetailsFetcher { + return &S3BucketDetailsFetcher{ + reader: provider, + deserializer: deserializer, + } +} + +func (r *S3BucketDetailsFetcher) ReadDetails(res resource.Resource) (resource.Resource, error) { + ctyVal, err := r.reader.ReadResource(terraform.ReadResourceArgs{ + Ty: aws.AwsS3BucketResourceType, + ID: res.TerraformId(), + Attributes: map[string]string{ + "alias": *res.Attributes().GetString("region"), + }, + }) + if err != nil { + return nil, err + } + deserializedRes, err := r.deserializer.DeserializeOne(aws.AwsS3BucketResourceType, *ctyVal) + if err != nil { + return nil, err + } + + return deserializedRes, nil +} diff --git a/pkg/remote/aws/s3_bucket_enumerator.go b/pkg/remote/aws/s3_bucket_enumerator.go new file mode 100644 index 000000000..28428f3b3 --- /dev/null +++ b/pkg/remote/aws/s3_bucket_enumerator.go @@ -0,0 +1,63 @@ +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 S3BucketEnumerator struct { + repository repository.S3Repository + factory resource.ResourceFactory + providerConfig tf.TerraformProviderConfig +} + +func NewS3BucketEnumerator(repo repository.S3Repository, factory resource.ResourceFactory, providerConfig tf.TerraformProviderConfig) *S3BucketEnumerator { + return &S3BucketEnumerator{ + repository: repo, + factory: factory, + providerConfig: providerConfig, + } +} + +func (e *S3BucketEnumerator) SupportedType() resource.ResourceType { + return aws.AwsS3BucketResourceType +} + +func (e *S3BucketEnumerator) Enumerate() ([]resource.Resource, error) { + buckets, err := e.repository.ListAllBuckets() + if err != nil { + return nil, remoteerror.NewResourceEnumerationError(err, string(e.SupportedType())) + } + + 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 + } + 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_inventory_supplier.go b/pkg/remote/aws/s3_bucket_inventory_supplier.go index 3e6dc6760..586fdf4a6 100644 --- a/pkg/remote/aws/s3_bucket_inventory_supplier.go +++ b/pkg/remote/aws/s3_bucket_inventory_supplier.go @@ -40,7 +40,7 @@ func (s *S3BucketInventorySupplier) Resources() ([]resource.Resource, error) { for _, bucket := range buckets { bucket := *bucket - region, err := s.repository.GetBucketLocation(&bucket) + region, err := s.repository.GetBucketLocation(*bucket.Name) if err != nil { return nil, err } diff --git a/pkg/remote/aws/s3_bucket_inventory_supplier_test.go b/pkg/remote/aws/s3_bucket_inventory_supplier_test.go index 6bd8b9c7b..b276caa46 100644 --- a/pkg/remote/aws/s3_bucket_inventory_supplier_test.go +++ b/pkg/remote/aws/s3_bucket_inventory_supplier_test.go @@ -45,7 +45,7 @@ func TestS3BucketInventorySupplier_Resources(t *testing.T) { repository.On( "GetBucketLocation", - &s3.Bucket{Name: awssdk.String("bucket-martin-test-drift")}, + "bucket-martin-test-drift", ).Return( "eu-west-1", nil, @@ -53,7 +53,7 @@ func TestS3BucketInventorySupplier_Resources(t *testing.T) { repository.On( "GetBucketLocation", - &s3.Bucket{Name: awssdk.String("bucket-martin-test-drift2")}, + "bucket-martin-test-drift2", ).Return( "eu-west-3", nil, @@ -61,7 +61,7 @@ func TestS3BucketInventorySupplier_Resources(t *testing.T) { repository.On( "GetBucketLocation", - &s3.Bucket{Name: awssdk.String("bucket-martin-test-drift3")}, + "bucket-martin-test-drift3", ).Return( "eu-west-1", nil, @@ -98,7 +98,7 @@ func TestS3BucketInventorySupplier_Resources(t *testing.T) { ) repository.On( "GetBucketLocation", - &s3.Bucket{Name: awssdk.String("bucket-martin-test-drift")}, + "bucket-martin-test-drift", ).Return( "eu-west-3", nil, diff --git a/pkg/remote/aws/s3_bucket_metric_supplier_test.go b/pkg/remote/aws/s3_bucket_metric_supplier_test.go index 42ae8eb14..40904d8bd 100644 --- a/pkg/remote/aws/s3_bucket_metric_supplier_test.go +++ b/pkg/remote/aws/s3_bucket_metric_supplier_test.go @@ -45,7 +45,7 @@ func TestS3BucketMetricSupplier_Resources(t *testing.T) { repository.On( "GetBucketLocation", - &s3.Bucket{Name: awssdk.String("bucket-martin-test-drift")}, + "bucket-martin-test-drift", ).Return( "eu-west-1", nil, @@ -53,7 +53,7 @@ func TestS3BucketMetricSupplier_Resources(t *testing.T) { repository.On( "GetBucketLocation", - &s3.Bucket{Name: awssdk.String("bucket-martin-test-drift2")}, + "bucket-martin-test-drift2", ).Return( "eu-west-3", nil, @@ -61,7 +61,7 @@ func TestS3BucketMetricSupplier_Resources(t *testing.T) { repository.On( "GetBucketLocation", - &s3.Bucket{Name: awssdk.String("bucket-martin-test-drift3")}, + "bucket-martin-test-drift3", ).Return( "ap-northeast-1", nil, @@ -98,7 +98,7 @@ func TestS3BucketMetricSupplier_Resources(t *testing.T) { ) repository.On( "GetBucketLocation", - &s3.Bucket{Name: awssdk.String("bucket-martin-test-drift")}, + "bucket-martin-test-drift", ).Return( "eu-west-3", nil, diff --git a/pkg/remote/aws/s3_bucket_metrics_supplier.go b/pkg/remote/aws/s3_bucket_metrics_supplier.go index 7d76c3ff5..b40127973 100644 --- a/pkg/remote/aws/s3_bucket_metrics_supplier.go +++ b/pkg/remote/aws/s3_bucket_metrics_supplier.go @@ -40,7 +40,7 @@ func (s *S3BucketMetricSupplier) Resources() ([]resource.Resource, error) { for _, bucket := range buckets { bucket := *bucket - region, err := s.repository.GetBucketLocation(&bucket) + region, err := s.repository.GetBucketLocation(*bucket.Name) if err != nil { return nil, err } diff --git a/pkg/remote/aws/s3_bucket_notification_supplier.go b/pkg/remote/aws/s3_bucket_notification_supplier.go index 1187b7bf8..4d82703bb 100644 --- a/pkg/remote/aws/s3_bucket_notification_supplier.go +++ b/pkg/remote/aws/s3_bucket_notification_supplier.go @@ -37,7 +37,7 @@ func (s *S3BucketNotificationSupplier) Resources() ([]resource.Resource, error) for _, bucket := range buckets { bucket := *bucket - region, err := s.repository.GetBucketLocation(&bucket) + region, err := s.repository.GetBucketLocation(*bucket.Name) if err != nil { return nil, err } diff --git a/pkg/remote/aws/s3_bucket_notification_supplier_test.go b/pkg/remote/aws/s3_bucket_notification_supplier_test.go index 7d9bce191..f5d5c4735 100644 --- a/pkg/remote/aws/s3_bucket_notification_supplier_test.go +++ b/pkg/remote/aws/s3_bucket_notification_supplier_test.go @@ -44,7 +44,7 @@ func TestS3BucketNotificationSupplier_Resources(t *testing.T) { repository.On( "GetBucketLocation", - &s3.Bucket{Name: awssdk.String("dritftctl-test-no-notifications")}, + "dritftctl-test-no-notifications", ).Return( "eu-west-3", nil, @@ -64,7 +64,7 @@ func TestS3BucketNotificationSupplier_Resources(t *testing.T) { repository.On( "GetBucketLocation", - &s3.Bucket{Name: awssdk.String("bucket-martin-test-drift")}, + "bucket-martin-test-drift", ).Return( "eu-west-1", nil, @@ -72,7 +72,7 @@ func TestS3BucketNotificationSupplier_Resources(t *testing.T) { repository.On( "GetBucketLocation", - &s3.Bucket{Name: awssdk.String("bucket-martin-test-drift2")}, + "bucket-martin-test-drift2", ).Return( "eu-west-3", nil, @@ -80,7 +80,7 @@ func TestS3BucketNotificationSupplier_Resources(t *testing.T) { repository.On( "GetBucketLocation", - &s3.Bucket{Name: awssdk.String("bucket-martin-test-drift3")}, + "bucket-martin-test-drift3", ).Return( "ap-northeast-1", nil, diff --git a/pkg/remote/aws/s3_bucket_policy_supplier.go b/pkg/remote/aws/s3_bucket_policy_supplier.go index fce5f21b5..d93c1245c 100644 --- a/pkg/remote/aws/s3_bucket_policy_supplier.go +++ b/pkg/remote/aws/s3_bucket_policy_supplier.go @@ -37,7 +37,7 @@ func (s *S3BucketPolicySupplier) Resources() ([]resource.Resource, error) { for _, bucket := range buckets { bucket := *bucket - region, err := s.repository.GetBucketLocation(&bucket) + region, err := s.repository.GetBucketLocation(*bucket.Name) if err != nil { return nil, err } diff --git a/pkg/remote/aws/s3_bucket_policy_supplier_test.go b/pkg/remote/aws/s3_bucket_policy_supplier_test.go index 5da564b12..b59357c76 100644 --- a/pkg/remote/aws/s3_bucket_policy_supplier_test.go +++ b/pkg/remote/aws/s3_bucket_policy_supplier_test.go @@ -44,7 +44,7 @@ func TestS3BucketPolicySupplier_Resources(t *testing.T) { repository.On( "GetBucketLocation", - &s3.Bucket{Name: awssdk.String("dritftctl-test-no-policy")}, + "dritftctl-test-no-policy", ).Return( "eu-west-3", nil, @@ -64,7 +64,7 @@ func TestS3BucketPolicySupplier_Resources(t *testing.T) { repository.On( "GetBucketLocation", - &s3.Bucket{Name: awssdk.String("bucket-martin-test-drift")}, + "bucket-martin-test-drift", ).Return( "eu-west-1", nil, @@ -72,7 +72,7 @@ func TestS3BucketPolicySupplier_Resources(t *testing.T) { repository.On( "GetBucketLocation", - &s3.Bucket{Name: awssdk.String("bucket-martin-test-drift2")}, + "bucket-martin-test-drift2", ).Return( "eu-west-3", nil, @@ -80,7 +80,7 @@ func TestS3BucketPolicySupplier_Resources(t *testing.T) { repository.On( "GetBucketLocation", - &s3.Bucket{Name: awssdk.String("bucket-martin-test-drift3")}, + "bucket-martin-test-drift3", ).Return( "ap-northeast-1", nil, diff --git a/pkg/remote/aws/s3_bucket_supplier.go b/pkg/remote/aws/s3_bucket_supplier.go deleted file mode 100644 index 58deccbea..000000000 --- a/pkg/remote/aws/s3_bucket_supplier.go +++ /dev/null @@ -1,74 +0,0 @@ -package aws - -import ( - "github.com/aws/aws-sdk-go/service/s3" - "github.com/zclconf/go-cty/cty" - - "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" -) - -type S3BucketSupplier struct { - reader terraform.ResourceReader - deserializer *resource.Deserializer - repository repository.S3Repository - runner *terraform.ParallelResourceReader - providerConfig tf.TerraformProviderConfig -} - -func NewS3BucketSupplier(provider *AWSTerraformProvider, repository repository.S3Repository, deserializer *resource.Deserializer) *S3BucketSupplier { - return &S3BucketSupplier{ - provider, - deserializer, - repository, - terraform.NewParallelResourceReader(provider.Runner().SubRunner()), - provider.Config, - } -} - -func (s *S3BucketSupplier) Resources() ([]resource.Resource, error) { - buckets, err := s.repository.ListAllBuckets() - if err != nil { - return nil, remoteerror.NewResourceEnumerationError(err, aws.AwsS3BucketResourceType) - } - - for _, bucket := range buckets { - b := *bucket - s.runner.Run(func() (cty.Value, error) { - return s.readBucket(b) - }) - } - values, err := s.runner.Wait() - if err != nil { - return nil, err - } - - return s.deserializer.Deserialize(aws.AwsS3BucketResourceType, values) -} - -func (s *S3BucketSupplier) readBucket(bucket s3.Bucket) (cty.Value, error) { - region, err := s.repository.GetBucketLocation(&bucket) - if err != nil { - return cty.NilVal, err - } - if region == "" || region != s.providerConfig.DefaultAlias { - return cty.NilVal, nil - } - - s3Bucket, err := s.reader.ReadResource(terraform.ReadResourceArgs{ - Ty: aws.AwsS3BucketResourceType, - ID: *bucket.Name, - Attributes: map[string]string{ - "alias": region, - }, - }) - if err != nil { - return cty.NilVal, err - } - return *s3Bucket, err -} diff --git a/pkg/remote/aws/s3_bucket_supplier_test.go b/pkg/remote/aws/s3_bucket_supplier_test.go deleted file mode 100644 index 7dd92e525..000000000 --- a/pkg/remote/aws/s3_bucket_supplier_test.go +++ /dev/null @@ -1,121 +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/remote/cache" - testresource "github.com/cloudskiff/driftctl/test/resource" - - "github.com/cloudskiff/driftctl/pkg/parallel" - "github.com/cloudskiff/driftctl/pkg/remote/aws/client" - "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" - resourceaws "github.com/cloudskiff/driftctl/pkg/resource/aws" - - "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 TestS3BucketSupplier_Resources(t *testing.T) { - - tests := []struct { - test string - dirName string - mocks func(repository *repository.MockS3Repository) - wantErr error - }{ - { - test: "multiple bucket", dirName: "s3_bucket_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", - &s3.Bucket{Name: awssdk.String("bucket-martin-test-drift")}, - ).Return( - "eu-west-1", - nil, - ) - - repository.On( - "GetBucketLocation", - &s3.Bucket{Name: awssdk.String("bucket-martin-test-drift2")}, - ).Return( - "eu-west-3", - nil, - ) - - repository.On( - "GetBucketLocation", - &s3.Bucket{Name: awssdk.String("bucket-martin-test-drift3")}, - ).Return( - "ap-northeast-1", - nil, - ) - }, - }, - { - test: "cannot list bucket", dirName: "s3_bucket_list", - mocks: func(repository *repository.MockS3Repository) { - repository.On("ListAllBuckets").Return(nil, awserr.NewRequestFailure(nil, 403, "")) - }, - wantErr: remoteerror.NewResourceEnumerationError(awserr.NewRequestFailure(nil, 403, ""), 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(NewS3BucketSupplier(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 := &S3BucketSupplier{ - 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/common/library.go b/pkg/remote/common/library.go new file mode 100644 index 000000000..79781bf65 --- /dev/null +++ b/pkg/remote/common/library.go @@ -0,0 +1,42 @@ +package common + +import ( + "github.com/cloudskiff/driftctl/pkg/resource" +) + +type Enumerator interface { + SupportedType() resource.ResourceType + Enumerate() ([]resource.Resource, error) +} + +type DetailsFetcher interface { + ReadDetails(resource.Resource) (resource.Resource, error) +} + +type RemoteLibrary struct { + enumerators []Enumerator + detailsFetchers map[resource.ResourceType]DetailsFetcher +} + +func NewRemoteLibrary() *RemoteLibrary { + return &RemoteLibrary{ + make([]Enumerator, 0), + make(map[resource.ResourceType]DetailsFetcher), + } +} + +func (r *RemoteLibrary) AddEnumerator(enumerator Enumerator) { + r.enumerators = append(r.enumerators, enumerator) +} + +func (r *RemoteLibrary) Enumerators() []Enumerator { + return r.enumerators +} + +func (r *RemoteLibrary) AddDetailsFetcher(ty resource.ResourceType, detailFetcher DetailsFetcher) { + r.detailsFetchers[ty] = detailFetcher +} + +func (r *RemoteLibrary) GetDetailsFetcher(ty resource.ResourceType) DetailsFetcher { + return r.detailsFetchers[ty] +} diff --git a/pkg/remote/github/init.go b/pkg/remote/github/init.go index b8bfd8817..cde306e2a 100644 --- a/pkg/remote/github/init.go +++ b/pkg/remote/github/init.go @@ -4,6 +4,7 @@ import ( "github.com/cloudskiff/driftctl/pkg/alerter" "github.com/cloudskiff/driftctl/pkg/output" "github.com/cloudskiff/driftctl/pkg/remote/cache" + "github.com/cloudskiff/driftctl/pkg/remote/common" "github.com/cloudskiff/driftctl/pkg/resource" "github.com/cloudskiff/driftctl/pkg/resource/github" "github.com/cloudskiff/driftctl/pkg/terraform" @@ -19,6 +20,7 @@ const RemoteGithubTerraform = "github+tf" func Init(version string, alerter *alerter.Alerter, providerLibrary *terraform.ProviderLibrary, supplierLibrary *resource.SupplierLibrary, + remoteLibrary *common.RemoteLibrary, progress output.Progress, resourceSchemaRepository *resource.SchemaRepository, factory resource.ResourceFactory, diff --git a/pkg/remote/remote.go b/pkg/remote/remote.go index f1c9eb86b..d99d1d9d6 100644 --- a/pkg/remote/remote.go +++ b/pkg/remote/remote.go @@ -4,6 +4,7 @@ import ( "github.com/cloudskiff/driftctl/pkg/alerter" "github.com/cloudskiff/driftctl/pkg/output" "github.com/cloudskiff/driftctl/pkg/remote/aws" + "github.com/cloudskiff/driftctl/pkg/remote/common" "github.com/cloudskiff/driftctl/pkg/remote/github" "github.com/cloudskiff/driftctl/pkg/resource" "github.com/cloudskiff/driftctl/pkg/terraform" @@ -27,15 +28,16 @@ func IsSupported(remote string) bool { func Activate(remote, version string, alerter *alerter.Alerter, providerLibrary *terraform.ProviderLibrary, supplierLibrary *resource.SupplierLibrary, + remoteLibrary *common.RemoteLibrary, progress output.Progress, resourceSchemaRepository *resource.SchemaRepository, factory resource.ResourceFactory, configDir string) error { switch remote { case aws.RemoteAWSTerraform: - return aws.Init(version, alerter, providerLibrary, supplierLibrary, progress, resourceSchemaRepository, factory, configDir) + return aws.Init(version, alerter, providerLibrary, supplierLibrary, remoteLibrary, progress, resourceSchemaRepository, factory, configDir) case github.RemoteGithubTerraform: - return github.Init(version, alerter, providerLibrary, supplierLibrary, progress, resourceSchemaRepository, factory, configDir) + return github.Init(version, alerter, providerLibrary, supplierLibrary, remoteLibrary, progress, resourceSchemaRepository, factory, configDir) default: return errors.Errorf("unsupported remote '%s'", remote) } diff --git a/pkg/resource/deserializer.go b/pkg/resource/deserializer.go index 8d42e316a..202b7852b 100644 --- a/pkg/resource/deserializer.go +++ b/pkg/resource/deserializer.go @@ -19,16 +19,23 @@ func (s *Deserializer) Deserialize(ty string, rawList []cty.Value) ([]Resource, resources := make([]Resource, 0) for _, rawResource := range rawList { rawResource := rawResource - var attrs Attributes - - bytes, _ := ctyjson.Marshal(rawResource, rawResource.Type()) - err := json.Unmarshal(bytes, &attrs) + res, err := s.DeserializeOne(ty, rawResource) if err != nil { return nil, err } - - res := s.factory.CreateAbstractResource(ty, rawResource.GetAttr("id").AsString(), attrs) resources = append(resources, res) } return resources, nil } + +func (s *Deserializer) DeserializeOne(ty string, value cty.Value) (Resource, error) { + var attrs Attributes + + bytes, _ := ctyjson.Marshal(value, value.Type()) + err := json.Unmarshal(bytes, &attrs) + if err != nil { + return nil, err + } + + return s.factory.CreateAbstractResource(ty, value.GetAttr("id").AsString(), attrs), nil +} diff --git a/pkg/scanner.go b/pkg/scanner.go index 8b0ef7f8d..c397a23b9 100644 --- a/pkg/scanner.go +++ b/pkg/scanner.go @@ -6,29 +6,62 @@ import ( "github.com/cloudskiff/driftctl/pkg/alerter" "github.com/cloudskiff/driftctl/pkg/parallel" "github.com/cloudskiff/driftctl/pkg/remote" + "github.com/cloudskiff/driftctl/pkg/remote/common" "github.com/cloudskiff/driftctl/pkg/resource" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) +type ScannerOptions struct { + Deep bool +} + type Scanner struct { - resourceSuppliers []resource.Supplier - runner *parallel.ParallelRunner - alerter *alerter.Alerter + resourceSuppliers []resource.Supplier + enumeratorRunner *parallel.ParallelRunner + detailsFetcherRunner *parallel.ParallelRunner + remoteLibrary *common.RemoteLibrary + alerter *alerter.Alerter + options ScannerOptions } -func NewScanner(resourceSuppliers []resource.Supplier, alerter *alerter.Alerter) *Scanner { +func NewScanner(resourceSuppliers []resource.Supplier, remoteLibrary *common.RemoteLibrary, alerter *alerter.Alerter, options ScannerOptions) *Scanner { return &Scanner{ - resourceSuppliers: resourceSuppliers, - runner: parallel.NewParallelRunner(context.TODO(), 10), - alerter: alerter, + resourceSuppliers: resourceSuppliers, + enumeratorRunner: parallel.NewParallelRunner(context.TODO(), 10), + detailsFetcherRunner: parallel.NewParallelRunner(context.TODO(), 10), + remoteLibrary: remoteLibrary, + alerter: alerter, + options: options, } } -func (s *Scanner) Resources() ([]resource.Resource, error) { +func (s *Scanner) retrieveRunnerResults(runner *parallel.ParallelRunner) ([]resource.Resource, error) { + results := make([]resource.Resource, 0) +loop: + for { + select { + case resources, ok := <-runner.Read(): + if !ok || resources == nil { + break loop + } + + for _, res := range resources.([]resource.Resource) { + if res != nil { + results = append(results, res) + } + } + case <-runner.DoneChan(): + break loop + } + } + return results, runner.Err() +} + +func (s *Scanner) legacyScan() ([]resource.Resource, error) { for _, resourceProvider := range s.resourceSuppliers { supplier := resourceProvider - s.runner.Run(func() (interface{}, error) { + s.enumeratorRunner.Run(func() (interface{}, error) { res, err := supplier.Resources() if err != nil { err := remote.HandleResourceEnumerationError(err, s.alerter) @@ -41,29 +74,82 @@ func (s *Scanner) Resources() ([]resource.Resource, error) { logrus.WithFields(logrus.Fields{ "id": resource.TerraformId(), "type": resource.TerraformType(), - }).Debug("Found cloud resource") + }).Debug("[DEPRECATED] Found cloud resource") } return res, nil }) } - results := make([]resource.Resource, 0) -loop: - for { - select { - case resources, ok := <-s.runner.Read(): - if !ok || resources == nil { - break loop + return s.retrieveRunnerResults(s.enumeratorRunner) +} + +func (s *Scanner) scan() ([]resource.Resource, error) { + for _, enumerator := range s.remoteLibrary.Enumerators() { + enumerator := enumerator + s.enumeratorRunner.Run(func() (interface{}, error) { + resources, err := enumerator.Enumerate() + if err != nil { + return nil, err } - results = append(results, resources.([]resource.Resource)...) - case <-s.runner.DoneChan(): - break loop - } + for _, resource := range resources { + if resource == nil { + continue + } + logrus.WithFields(logrus.Fields{ + "id": resource.TerraformId(), + "type": resource.TerraformType(), + }).Debug("Found cloud resource") + } + return resources, nil + }) } - return results, s.runner.Err() + + enumerationResult, err := s.retrieveRunnerResults(s.enumeratorRunner) + if err != nil { + return nil, err + } + + for _, res := range enumerationResult { + res := res + s.detailsFetcherRunner.Run(func() (interface{}, error) { + fetcher := s.remoteLibrary.GetDetailsFetcher(resource.ResourceType(res.TerraformType())) + if fetcher != nil { + // If we are in deep mode, retrieve resource details + if s.options.Deep { + resourceWithDetails, err := fetcher.ReadDetails(res) + if err != nil { + return nil, err + } + return []resource.Resource{resourceWithDetails}, nil + } + } + return []resource.Resource{res}, nil + }) + } + + return s.retrieveRunnerResults(s.detailsFetcherRunner) +} + +func (s *Scanner) Resources() ([]resource.Resource, error) { + + resources, err := s.legacyScan() + if err != nil { + return nil, err + } + + s.enumeratorRunner = parallel.NewParallelRunner(context.TODO(), 10) + + enumerationResult, err := s.scan() + if err != nil { + return nil, err + } + resources = append(resources, enumerationResult...) + + return resources, err } func (s *Scanner) Stop() { logrus.Debug("Stopping scanner") - s.runner.Stop(errors.New("interrupted")) + s.enumeratorRunner.Stop(errors.New("interrupted")) + s.detailsFetcherRunner.Stop(errors.New("interrupted")) }