From bab96aa5c956a1d9ff052de999e9e39d37ab8b9d Mon Sep 17 00:00:00 2001 From: Martin Guibert Date: Tue, 14 Sep 2021 22:07:09 +0200 Subject: [PATCH 1/4] add support for google_storage_bucket_iam_binding --- go.mod | 1 + ...e_storage_bucket_iam_binding_enumerator.go | 59 +++++++++++++++++++ pkg/remote/google/init.go | 3 + pkg/remote/google/repository/storage.go | 56 ++++++++++++++++++ .../google_storage_bucket_iam_binding.go | 17 ++++++ pkg/resource/google/metadatas.go | 1 + pkg/resource/resource_types.go | 11 ++-- 7 files changed, 143 insertions(+), 5 deletions(-) create mode 100644 pkg/remote/google/google_storage_bucket_iam_binding_enumerator.go create mode 100644 pkg/remote/google/repository/storage.go create mode 100644 pkg/resource/google/google_storage_bucket_iam_binding.go diff --git a/go.mod b/go.mod index 5aa87d20..1a74a376 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0 github.com/Azure/azure-sdk-for-go/sdk/network/armnetwork v0.3.0 github.com/Azure/azure-sdk-for-go/sdk/storage/armstorage v0.2.0 + cloud.google.com/go/storage v1.10.0 github.com/Azure/go-autorest/autorest v0.11.3 github.com/aws/aws-sdk-go v1.38.68 github.com/bmatcuk/doublestar/v4 v4.0.1 diff --git a/pkg/remote/google/google_storage_bucket_iam_binding_enumerator.go b/pkg/remote/google/google_storage_bucket_iam_binding_enumerator.go new file mode 100644 index 00000000..4de3a130 --- /dev/null +++ b/pkg/remote/google/google_storage_bucket_iam_binding_enumerator.go @@ -0,0 +1,59 @@ +package google + +import ( + "fmt" + + remoteerror "github.com/cloudskiff/driftctl/pkg/remote/error" + "github.com/cloudskiff/driftctl/pkg/remote/google/repository" + "github.com/cloudskiff/driftctl/pkg/resource" + "github.com/cloudskiff/driftctl/pkg/resource/google" +) + +type GoogleStorageBucketIamBindingEnumerator struct { + repository repository.AssetRepository + storageRepository repository.StorageRepository + factory resource.ResourceFactory +} + +func NewGoogleStorageBucketIamBindingEnumerator(repo repository.AssetRepository, storageRepo repository.StorageRepository, factory resource.ResourceFactory) *GoogleStorageBucketIamBindingEnumerator { + return &GoogleStorageBucketIamBindingEnumerator{ + repository: repo, + storageRepository: storageRepo, + factory: factory, + } +} + +func (e *GoogleStorageBucketIamBindingEnumerator) SupportedType() resource.ResourceType { + return google.GoogleStorageBucketIamBindingResourceType +} + +func (e *GoogleStorageBucketIamBindingEnumerator) Enumerate() ([]*resource.Resource, error) { + resources, err := e.repository.SearchAllBuckets() + + if err != nil { + return nil, remoteerror.NewResourceListingError(err, string(e.SupportedType())) + } + + results := make([]*resource.Resource, len(resources)) + + for _, bucket := range resources { + for roleName, members := range e.storageRepository.ListAllBindings(bucket.DisplayName) { + id := fmt.Sprintf("b/%s/%s", bucket.DisplayName, roleName) + results = append( + results, + e.factory.CreateAbstractResource( + string(e.SupportedType()), + id, + map[string]interface{}{ + "id": id, + "bucket": fmt.Sprintf("b/%s", bucket.DisplayName), + "role": roleName, + "members": members, + }, + ), + ) + } + } + + return results, err +} diff --git a/pkg/remote/google/init.go b/pkg/remote/google/init.go index 901b8530..0f7bc0f1 100644 --- a/pkg/remote/google/init.go +++ b/pkg/remote/google/init.go @@ -40,6 +40,8 @@ func Init(version string, alerter *alerter.Alerter, } assetRepository := repository.NewAssetRepository(assetClient, provider.GetConfig(), repositoryCache) + storageRepository := repository.NewStorageRepository(repositoryCache) + providerLibrary.AddProvider(terraform.GOOGLE, provider) deserializer := resource.NewDeserializer(factory) @@ -52,6 +54,7 @@ func Init(version string, alerter *alerter.Alerter, remoteLibrary.AddEnumerator(NewGoogleComputeRouterEnumerator(assetRepository, factory)) remoteLibrary.AddEnumerator(NewGoogleComputeInstanceEnumerator(assetRepository, factory)) + remoteLibrary.AddEnumerator(NewGoogleStorageBucketIamBindingEnumerator(assetRepository, storageRepository, factory)) remoteLibrary.AddEnumerator(NewGoogleComputeNetworkEnumerator(assetRepository, factory)) remoteLibrary.AddDetailsFetcher(google.GoogleComputeNetworkResourceType, common.NewGenericDetailsFetcher(google.GoogleComputeNetworkResourceType, provider, deserializer)) diff --git a/pkg/remote/google/repository/storage.go b/pkg/remote/google/repository/storage.go new file mode 100644 index 00000000..1b8207ee --- /dev/null +++ b/pkg/remote/google/repository/storage.go @@ -0,0 +1,56 @@ +package repository + +import ( + "context" + "sync" + + "cloud.google.com/go/storage" + "github.com/cloudskiff/driftctl/pkg/remote/cache" +) + +type StorageRepository interface { + ListAllBindings(bucketName string) map[string][]string +} + +type storageRepository struct { + client *storage.Client + cache cache.Cache + lock sync.Locker +} + +func NewStorageRepository(cache cache.Cache) *storageRepository { + client, err := storage.NewClient(context.Background()) + if err != nil { + panic(err) + } + + return &storageRepository{ + client: client, + cache: cache, + lock: &sync.Mutex{}, + } +} + +func (s storageRepository) ListAllBindings(bucketName string) map[string][]string { + + s.lock.Lock() + defer s.lock.Unlock() + if cachedResults := s.cache.Get("ListAllBindings"); cachedResults != nil { + return cachedResults.(map[string][]string) + } + + bucket := s.client.Bucket(bucketName) + policy, err := bucket.IAM().Policy(context.Background()) + if err != nil { + panic(err) + } + bindings := make(map[string][]string) + for _, name := range policy.Roles() { + members := policy.Members(name) + bindings[string(name)] = members + } + + s.cache.Put("ListAllBindings", bindings) + + return bindings +} diff --git a/pkg/resource/google/google_storage_bucket_iam_binding.go b/pkg/resource/google/google_storage_bucket_iam_binding.go new file mode 100644 index 00000000..c6d5cf3e --- /dev/null +++ b/pkg/resource/google/google_storage_bucket_iam_binding.go @@ -0,0 +1,17 @@ +package google + +import "github.com/cloudskiff/driftctl/pkg/resource" + +const GoogleStorageBucketIamBindingResourceType = "google_storage_bucket_iam_binding" + +func initGoogleStorageBucketIamBindingMetadata(resourceSchemaRepository resource.SchemaRepositoryInterface) { + resourceSchemaRepository.SetNormalizeFunc(GoogleStorageBucketIamBindingResourceType, func(res *resource.Resource) { + res.Attributes().SafeDelete([]string{"force_destroy"}) + res.Attributes().SafeDelete([]string{"etag"}) + }) + resourceSchemaRepository.SetResolveReadAttributesFunc(GoogleStorageBucketIamBindingResourceType, func(res *resource.Resource) map[string]string { + return map[string]string{ + "bucket": *res.Attrs.GetString("bucket"), + } + }) +} diff --git a/pkg/resource/google/metadatas.go b/pkg/resource/google/metadatas.go index 61fddce2..c119b1fe 100644 --- a/pkg/resource/google/metadatas.go +++ b/pkg/resource/google/metadatas.go @@ -7,4 +7,5 @@ func InitResourcesMetadata(resourceSchemaRepository resource.SchemaRepositoryInt initGoogleComputeFirewallMetadata(resourceSchemaRepository) initGoogleComputeRouterMetadata(resourceSchemaRepository) initGoogleComputeNetworkMetadata(resourceSchemaRepository) + initGoogleStorageBucketIamBindingMetadata(resourceSchemaRepository) } diff --git a/pkg/resource/resource_types.go b/pkg/resource/resource_types.go index 57530da7..39efd201 100644 --- a/pkg/resource/resource_types.go +++ b/pkg/resource/resource_types.go @@ -70,11 +70,12 @@ var supportedTypes = map[string]struct{}{ "github_team": {}, "github_team_membership": {}, - "google_storage_bucket": {}, - "google_compute_firewall": {}, - "google_compute_router": {}, - "google_compute_instance": {}, - "google_compute_network": {}, + "google_storage_bucket": {}, + "google_compute_firewall": {}, + "google_compute_router": {}, + "google_compute_instance": {}, + "google_compute_network": {}, + "google_storage_bucket_iam_binding": {}, "azurerm_storage_account": {}, "azurerm_storage_container": {}, From 436f8c29e39a02a7187746db8c77cd8dcaa5a67e Mon Sep 17 00:00:00 2001 From: Martin Guibert Date: Wed, 15 Sep 2021 20:36:04 +0200 Subject: [PATCH 2/4] add tests and fixes --- pkg/driftctl.go | 1 + .../google_legacy_bucket_iam_bindings.go | 60 ++++++ .../google_legacy_bucket_iam_bindings_test.go | 139 ++++++++++++++ ...e_storage_bucket_iam_binding_enumerator.go | 9 +- pkg/remote/google/init.go | 10 +- .../google/repository/mock_AssetRepository.go | 82 +++++++++ .../repository/mock_StorageRepository.go | 33 ++++ pkg/remote/google/repository/storage.go | 20 +- pkg/remote/google_storage_scanner_test.go | 171 ++++++++++++++++++ .../resource_enumeration_error_handler.go | 10 +- .../results.golden.json | 42 +++++ .../google_storage_bucket_iam_binding_test.go | 31 ++++ .../terraform.tf | 50 +++++ 13 files changed, 639 insertions(+), 19 deletions(-) create mode 100644 pkg/middlewares/google_legacy_bucket_iam_bindings.go create mode 100644 pkg/middlewares/google_legacy_bucket_iam_bindings_test.go create mode 100644 pkg/remote/google/repository/mock_AssetRepository.go create mode 100644 pkg/remote/google/repository/mock_StorageRepository.go create mode 100755 pkg/remote/test/google_storage_bucket_binding_multiple/results.golden.json create mode 100644 pkg/resource/google/google_storage_bucket_iam_binding_test.go create mode 100644 pkg/resource/google/testdata/acc/google_storage_bucket_iam_binding/terraform.tf diff --git a/pkg/driftctl.go b/pkg/driftctl.go index aa6b71be..d6903adb 100644 --- a/pkg/driftctl.go +++ b/pkg/driftctl.go @@ -110,6 +110,7 @@ func (d DriftCTL) Run() (*analyser.Analysis, error) { middlewares.NewTagsAllManager(), middlewares.NewEipAssociationExpander(d.resourceFactory), middlewares.NewRDSClusterInstanceExpander(d.resourceFactory), + middlewares.NewGoogleLegacyBucketIAMBindings(), ) if !d.opts.StrictMode { diff --git a/pkg/middlewares/google_legacy_bucket_iam_bindings.go b/pkg/middlewares/google_legacy_bucket_iam_bindings.go new file mode 100644 index 00000000..762da12d --- /dev/null +++ b/pkg/middlewares/google_legacy_bucket_iam_bindings.go @@ -0,0 +1,60 @@ +package middlewares + +import ( + "strings" + + "github.com/cloudskiff/driftctl/pkg/resource" + "github.com/cloudskiff/driftctl/pkg/resource/google" + "github.com/sirupsen/logrus" +) + +// Creating buckets add legacy role bindings, this middleware will filter them unless they are managed. +type GoogleLegacyBucketIAMBindings struct{} + +func NewGoogleLegacyBucketIAMBindings() GoogleLegacyBucketIAMBindings { + return GoogleLegacyBucketIAMBindings{} +} + +func (m GoogleLegacyBucketIAMBindings) Execute(remoteResources, resourcesFromState *[]*resource.Resource) error { + + newRemoteResources := make([]*resource.Resource, 0) + + for _, remoteResource := range *remoteResources { + // Ignore all resources other than BucketIamBinding + if remoteResource.ResourceType() != google.GoogleStorageBucketIamBindingResourceType { + newRemoteResources = append(newRemoteResources, remoteResource) + continue + } + + // Ignore all non-legacy bindings + if roleName := remoteResource.Attrs.GetString("role"); roleName != nil && !strings.Contains(*roleName, "legacy") { + newRemoteResources = append(newRemoteResources, remoteResource) + continue + } + + // Check if bindings is managed by IaC + existInState := false + for _, stateResource := range *resourcesFromState { + if remoteResource.Equal(stateResource) { + existInState = true + break + } + } + + // Include resource if it's managed in IaC + if existInState { + newRemoteResources = append(newRemoteResources, remoteResource) + continue + } + + // Else, resource is not added to newRemoteResources slice, so it will be ignored + logrus.WithFields(logrus.Fields{ + "id": remoteResource.ResourceId(), + "type": remoteResource.ResourceType(), + }).Debug("Ignoring legacy bucket bindings as it is not managed by IaC") + } + + *remoteResources = newRemoteResources + + return nil +} diff --git a/pkg/middlewares/google_legacy_bucket_iam_bindings_test.go b/pkg/middlewares/google_legacy_bucket_iam_bindings_test.go new file mode 100644 index 00000000..3a3c5d62 --- /dev/null +++ b/pkg/middlewares/google_legacy_bucket_iam_bindings_test.go @@ -0,0 +1,139 @@ +package middlewares + +import ( + "strings" + "testing" + + "github.com/aws/aws-sdk-go/aws/awsutil" + "github.com/cloudskiff/driftctl/pkg/resource" + "github.com/cloudskiff/driftctl/pkg/resource/google" + "github.com/r3labs/diff/v2" +) + +func TestGoogleLegacyBucketIAMBindings_Execute(t *testing.T) { + tests := []struct { + name string + remoteResources []*resource.Resource + resourcesFromState []*resource.Resource + expected []*resource.Resource + }{ + { + "test that non legacy bindings are not ignored when managed by IaC", + []*resource.Resource{ + { + Id: "fake", + Type: google.GoogleStorageBucketResourceType, + Attrs: &resource.Attributes{}, + }, + { + Id: "admin bucket", + Type: google.GoogleStorageBucketIamBindingResourceType, + Attrs: &resource.Attributes{ + "role": "storage.admin", + }, + }, + { + Id: "legacy", + Type: google.GoogleStorageBucketIamBindingResourceType, + Attrs: &resource.Attributes{ + "role": "storage.legacyBucketOwner", + }, + }, + }, + []*resource.Resource{}, + []*resource.Resource{ + { + Id: "fake", + Type: google.GoogleStorageBucketResourceType, + Attrs: &resource.Attributes{}, + }, + { + Id: "admin bucket", + Type: google.GoogleStorageBucketIamBindingResourceType, + Attrs: &resource.Attributes{ + "role": "storage.admin", + }, + }, + }, + }, + { + "test that legacy are not ignored when managed", + []*resource.Resource{ + { + Id: "fake", + Type: google.GoogleStorageBucketResourceType, + Attrs: &resource.Attributes{}, + }, + { + Id: "admin bucket", + Type: google.GoogleStorageBucketIamBindingResourceType, + Attrs: &resource.Attributes{ + "role": "storage.admin", + }, + }, + { + Id: "legacy", + Type: google.GoogleStorageBucketIamBindingResourceType, + Attrs: &resource.Attributes{ + "role": "storage.legacyBucketOwner", + }, + }, + { + Id: "legacy-managed", + Type: google.GoogleStorageBucketIamBindingResourceType, + Attrs: &resource.Attributes{ + "role": "storage.legacyBucketOwner", + }, + }, + }, + []*resource.Resource{ + { + Id: "legacy-managed", + Type: google.GoogleStorageBucketIamBindingResourceType, + Attrs: &resource.Attributes{ + "role": "storage.legacyBucketOwner", + }, + }, + }, + []*resource.Resource{ + { + Id: "fake", + Type: google.GoogleStorageBucketResourceType, + Attrs: &resource.Attributes{}, + }, + { + Id: "admin bucket", + Type: google.GoogleStorageBucketIamBindingResourceType, + Attrs: &resource.Attributes{ + "role": "storage.admin", + }, + }, + { + Id: "legacy-managed", + Type: google.GoogleStorageBucketIamBindingResourceType, + Attrs: &resource.Attributes{ + "role": "storage.legacyBucketOwner", + }, + }, + }, + }, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + m := NewGoogleLegacyBucketIAMBindings() + err := m.Execute(&tt.remoteResources, &tt.resourcesFromState) + if err != nil { + t.Fatal(err) + } + changelog, err := diff.Diff(tt.expected, tt.remoteResources) + if err != nil { + t.Fatal(err) + } + if len(changelog) > 0 { + for _, change := range changelog { + t.Errorf("%s got = %v, want %v", strings.Join(change.Path, "."), awsutil.Prettify(change.From), awsutil.Prettify(change.To)) + } + } + }) + } +} diff --git a/pkg/remote/google/google_storage_bucket_iam_binding_enumerator.go b/pkg/remote/google/google_storage_bucket_iam_binding_enumerator.go index 4de3a130..ad0f0ffe 100644 --- a/pkg/remote/google/google_storage_bucket_iam_binding_enumerator.go +++ b/pkg/remote/google/google_storage_bucket_iam_binding_enumerator.go @@ -29,15 +29,18 @@ func (e *GoogleStorageBucketIamBindingEnumerator) SupportedType() resource.Resou func (e *GoogleStorageBucketIamBindingEnumerator) Enumerate() ([]*resource.Resource, error) { resources, err := e.repository.SearchAllBuckets() - if err != nil { - return nil, remoteerror.NewResourceListingError(err, string(e.SupportedType())) + return nil, remoteerror.NewResourceListingErrorWithType(err, string(e.SupportedType()), google.GoogleStorageBucketResourceType) } results := make([]*resource.Resource, len(resources)) for _, bucket := range resources { - for roleName, members := range e.storageRepository.ListAllBindings(bucket.DisplayName) { + bindings, err := e.storageRepository.ListAllBindings(bucket.DisplayName) + if err != nil { + return nil, remoteerror.NewResourceListingError(err, string(e.SupportedType())) + } + for roleName, members := range bindings { id := fmt.Sprintf("b/%s/%s", bucket.DisplayName, roleName) results = append( results, diff --git a/pkg/remote/google/init.go b/pkg/remote/google/init.go index 0f7bc0f1..44ab282c 100644 --- a/pkg/remote/google/init.go +++ b/pkg/remote/google/init.go @@ -4,6 +4,7 @@ import ( "context" asset "cloud.google.com/go/asset/apiv1" + "cloud.google.com/go/storage" "github.com/cloudskiff/driftctl/pkg/alerter" "github.com/cloudskiff/driftctl/pkg/output" "github.com/cloudskiff/driftctl/pkg/remote/cache" @@ -38,9 +39,14 @@ func Init(version string, alerter *alerter.Alerter, if err != nil { return err } - assetRepository := repository.NewAssetRepository(assetClient, provider.GetConfig(), repositoryCache) - storageRepository := repository.NewStorageRepository(repositoryCache) + storageClient, err := storage.NewClient(ctx) + if err != nil { + return err + } + + assetRepository := repository.NewAssetRepository(assetClient, provider.GetConfig(), repositoryCache) + storageRepository := repository.NewStorageRepository(storageClient, repositoryCache) providerLibrary.AddProvider(terraform.GOOGLE, provider) deserializer := resource.NewDeserializer(factory) diff --git a/pkg/remote/google/repository/mock_AssetRepository.go b/pkg/remote/google/repository/mock_AssetRepository.go new file mode 100644 index 00000000..1b32e75a --- /dev/null +++ b/pkg/remote/google/repository/mock_AssetRepository.go @@ -0,0 +1,82 @@ +// Code generated by mockery v0.0.0-dev. DO NOT EDIT. + +package repository + +import ( + mock "github.com/stretchr/testify/mock" + asset "google.golang.org/genproto/googleapis/cloud/asset/v1" +) + +// MockAssetRepository is an autogenerated mock type for the AssetRepository type +type MockAssetRepository struct { + mock.Mock +} + +// SearchAllBuckets provides a mock function with given fields: +func (_m *MockAssetRepository) SearchAllBuckets() ([]*asset.ResourceSearchResult, error) { + ret := _m.Called() + + var r0 []*asset.ResourceSearchResult + if rf, ok := ret.Get(0).(func() []*asset.ResourceSearchResult); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*asset.ResourceSearchResult) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// SearchAllFirewalls provides a mock function with given fields: +func (_m *MockAssetRepository) SearchAllFirewalls() ([]*asset.ResourceSearchResult, error) { + ret := _m.Called() + + var r0 []*asset.ResourceSearchResult + if rf, ok := ret.Get(0).(func() []*asset.ResourceSearchResult); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*asset.ResourceSearchResult) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// SearchAllRouters provides a mock function with given fields: +func (_m *MockAssetRepository) SearchAllRouters() ([]*asset.ResourceSearchResult, error) { + ret := _m.Called() + + var r0 []*asset.ResourceSearchResult + if rf, ok := ret.Get(0).(func() []*asset.ResourceSearchResult); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*asset.ResourceSearchResult) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} diff --git a/pkg/remote/google/repository/mock_StorageRepository.go b/pkg/remote/google/repository/mock_StorageRepository.go new file mode 100644 index 00000000..af0be88c --- /dev/null +++ b/pkg/remote/google/repository/mock_StorageRepository.go @@ -0,0 +1,33 @@ +// Code generated by mockery v0.0.0-dev. DO NOT EDIT. + +package repository + +import mock "github.com/stretchr/testify/mock" + +// MockStorageRepository is an autogenerated mock type for the StorageRepository type +type MockStorageRepository struct { + mock.Mock +} + +// ListAllBindings provides a mock function with given fields: bucketName +func (_m *MockStorageRepository) ListAllBindings(bucketName string) (map[string][]string, error) { + ret := _m.Called(bucketName) + + var r0 map[string][]string + if rf, ok := ret.Get(0).(func(string) map[string][]string); ok { + r0 = rf(bucketName) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(map[string][]string) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func(string) error); ok { + r1 = rf(bucketName) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} diff --git a/pkg/remote/google/repository/storage.go b/pkg/remote/google/repository/storage.go index 1b8207ee..228efee9 100644 --- a/pkg/remote/google/repository/storage.go +++ b/pkg/remote/google/repository/storage.go @@ -2,6 +2,7 @@ package repository import ( "context" + "fmt" "sync" "cloud.google.com/go/storage" @@ -9,7 +10,7 @@ import ( ) type StorageRepository interface { - ListAllBindings(bucketName string) map[string][]string + ListAllBindings(bucketName string) (map[string][]string, error) } type storageRepository struct { @@ -18,12 +19,7 @@ type storageRepository struct { lock sync.Locker } -func NewStorageRepository(cache cache.Cache) *storageRepository { - client, err := storage.NewClient(context.Background()) - if err != nil { - panic(err) - } - +func NewStorageRepository(client *storage.Client, cache cache.Cache) *storageRepository { return &storageRepository{ client: client, cache: cache, @@ -31,18 +27,18 @@ func NewStorageRepository(cache cache.Cache) *storageRepository { } } -func (s storageRepository) ListAllBindings(bucketName string) map[string][]string { +func (s storageRepository) ListAllBindings(bucketName string) (map[string][]string, error) { s.lock.Lock() defer s.lock.Unlock() - if cachedResults := s.cache.Get("ListAllBindings"); cachedResults != nil { - return cachedResults.(map[string][]string) + if cachedResults := s.cache.Get(fmt.Sprintf("%s-%s", "ListAllBindings", bucketName)); cachedResults != nil { + return cachedResults.(map[string][]string), nil } bucket := s.client.Bucket(bucketName) policy, err := bucket.IAM().Policy(context.Background()) if err != nil { - panic(err) + return nil, err } bindings := make(map[string][]string) for _, name := range policy.Roles() { @@ -52,5 +48,5 @@ func (s storageRepository) ListAllBindings(bucketName string) map[string][]strin s.cache.Put("ListAllBindings", bindings) - return bindings + return bindings, nil } diff --git a/pkg/remote/google_storage_scanner_test.go b/pkg/remote/google_storage_scanner_test.go index 12f01e30..977f9c50 100644 --- a/pkg/remote/google_storage_scanner_test.go +++ b/pkg/remote/google_storage_scanner_test.go @@ -5,6 +5,7 @@ import ( "testing" asset "cloud.google.com/go/asset/apiv1" + "cloud.google.com/go/storage" "github.com/cloudskiff/driftctl/mocks" "github.com/cloudskiff/driftctl/pkg/filter" "github.com/cloudskiff/driftctl/pkg/remote/alerts" @@ -21,6 +22,7 @@ import ( testgoogle "github.com/cloudskiff/driftctl/test/google" testresource "github.com/cloudskiff/driftctl/test/resource" terraform2 "github.com/cloudskiff/driftctl/test/terraform" + "github.com/pkg/errors" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/mock" assetpb "google.golang.org/genproto/googleapis/cloud/asset/v1" @@ -156,3 +158,172 @@ func TestGoogleStorageBucket(t *testing.T) { }) } } + +func TestGoogleStorageBucketIAMBinding(t *testing.T) { + + cases := []struct { + test string + dirName string + assetRepositoryMock func(assetRepository *repository.MockAssetRepository) + storageRepositoryMock func(storageRepository *repository.MockStorageRepository) + responseErr error + setupAlerterMock func(alerter *mocks.AlerterInterface) + wantErr error + }{ + { + test: "no storage buckets", + dirName: "google_storage_bucket_empty", + assetRepositoryMock: func(assetRepository *repository.MockAssetRepository) { + assetRepository.On("SearchAllBuckets").Return([]*assetpb.ResourceSearchResult{}, nil) + }, + wantErr: nil, + }, + { + test: "multiples storage buckets, no bindings", + dirName: "google_storage_bucket_binding_empty", + assetRepositoryMock: func(assetRepository *repository.MockAssetRepository) { + assetRepository.On("SearchAllBuckets").Return([]*assetpb.ResourceSearchResult{ + { + AssetType: "storage.googleapis.com/Bucket", + DisplayName: "dctlgstoragebucketiambinding-1", + }, + { + AssetType: "storage.googleapis.com/Bucket", + DisplayName: "dctlgstoragebucketiambinding-2", + }, + }, nil) + }, + storageRepositoryMock: func(storageRepository *repository.MockStorageRepository) { + storageRepository.On("ListAllBindings", "dctlgstoragebucketiambinding-1").Return(map[string][]string{}, nil) + storageRepository.On("ListAllBindings", "dctlgstoragebucketiambinding-2").Return(map[string][]string{}, nil) + }, + wantErr: nil, + }, + { + test: "Cannot list bindings", + dirName: "google_storage_bucket_binding_listing_error", + assetRepositoryMock: func(assetRepository *repository.MockAssetRepository) { + assetRepository.On("SearchAllBuckets").Return([]*assetpb.ResourceSearchResult{ + { + AssetType: "storage.googleapis.com/Bucket", + DisplayName: "dctlgstoragebucketiambinding-1", + }, + }, nil) + }, + storageRepositoryMock: func(storageRepository *repository.MockStorageRepository) { + storageRepository.On("ListAllBindings", "dctlgstoragebucketiambinding-1").Return( + map[string][]string{}, + errors.New("googleapi: Error 403: driftctl-acc-circle@driftctl-qa-1.iam.gserviceaccount.com does not have storage.buckets.getIamPolicy access to the Google Cloud Storage bucket., forbidden")) + }, + setupAlerterMock: func(alerter *mocks.AlerterInterface) { + alerter.On( + "SendAlert", + "google_storage_bucket_iam_binding", + alerts.NewRemoteAccessDeniedAlert( + common.RemoteGoogleTerraform, + remoteerr.NewResourceListingError( + errors.New("googleapi: Error 403: driftctl-acc-circle@driftctl-qa-1.iam.gserviceaccount.com does not have storage.buckets.getIamPolicy access to the Google Cloud Storage bucket., forbidden"), + "google_storage_bucket_iam_binding", + ), + alerts.EnumerationPhase, + ), + ).Once() + }, + wantErr: nil, + }, + { + test: "multiples storage buckets, multiple bindings", + dirName: "google_storage_bucket_binding_multiple", + assetRepositoryMock: func(assetRepository *repository.MockAssetRepository) { + assetRepository.On("SearchAllBuckets").Return([]*assetpb.ResourceSearchResult{ + { + AssetType: "storage.googleapis.com/Bucket", + DisplayName: "dctlgstoragebucketiambinding-1", + }, + { + AssetType: "storage.googleapis.com/Bucket", + DisplayName: "dctlgstoragebucketiambinding-2", + }, + }, nil) + }, + storageRepositoryMock: func(storageRepository *repository.MockStorageRepository) { + storageRepository.On("ListAllBindings", "dctlgstoragebucketiambinding-1").Return(map[string][]string{ + "roles/storage.admin": {"user:elie.charra@cloudskiff.com"}, + "roles/storage.objectViewer": {"user:william.beuil@cloudskiff.com"}, + }, nil) + + storageRepository.On("ListAllBindings", "dctlgstoragebucketiambinding-2").Return(map[string][]string{ + "roles/storage.admin": {"user:william.beuil@cloudskiff.com"}, + "roles/storage.objectViewer": {"user:elie.charra@cloudskiff.com"}, + }, nil) + }, + wantErr: nil, + }, + } + + providerVersion := "3.78.0" + resType := resource.ResourceType(googleresource.GoogleStorageBucketIamBindingResourceType) + schemaRepository := testresource.InitFakeSchemaRepository("google", providerVersion) + googleresource.InitResourcesMetadata(schemaRepository) + factory := terraform.NewTerraformResourceFactory(schemaRepository) + deserializer := resource.NewDeserializer(factory) + + for _, c := range cases { + t.Run(c.test, func(tt *testing.T) { + repositoryCache := cache.New(100) + + shouldUpdate := c.dirName == *goldenfile.Update + + scanOptions := ScannerOptions{Deep: true} + providerLibrary := terraform.NewProviderLibrary() + remoteLibrary := common.NewRemoteLibrary() + + // Initialize mocks + alerter := &mocks.AlerterInterface{} + if c.setupAlerterMock != nil { + c.setupAlerterMock(alerter) + } + + storageRepo := &repository.MockStorageRepository{} + if c.storageRepositoryMock != nil { + c.storageRepositoryMock(storageRepo) + } + var storageRepository repository.StorageRepository = storageRepo + storageClient, err := storage.NewClient(context.Background()) + if shouldUpdate { + if err != nil { + panic(err) + } + storageRepository = repository.NewStorageRepository(storageClient, repositoryCache) + } + + assetRepo := &repository.MockAssetRepository{} + if c.assetRepositoryMock != nil { + c.assetRepositoryMock(assetRepo) + } + var assetRepository repository.AssetRepository = assetRepo + + realProvider, err := terraform2.InitTestGoogleProvider(providerLibrary, providerVersion) + if err != nil { + tt.Fatal(err) + } + provider := terraform2.NewFakeTerraformProvider(realProvider) + provider.WithResponse(c.dirName) + + remoteLibrary.AddEnumerator(google.NewGoogleStorageBucketIamBindingEnumerator(assetRepository, storageRepository, factory)) + + testFilter := &filter.MockFilter{} + testFilter.On("IsTypeIgnored", mock.Anything).Return(false) + + s := NewScanner(remoteLibrary, alerter, scanOptions, testFilter) + got, err := s.Resources() + assert.Equal(tt, c.wantErr, err) + if err != nil { + return + } + alerter.AssertExpectations(tt) + testFilter.AssertExpectations(tt) + test.TestAgainstGoldenFile(got, resType.String(), c.dirName, provider, deserializer, shouldUpdate, tt) + }) + } +} diff --git a/pkg/remote/resource_enumeration_error_handler.go b/pkg/remote/resource_enumeration_error_handler.go index 8eb8bd16..929e7c80 100644 --- a/pkg/remote/resource_enumeration_error_handler.go +++ b/pkg/remote/resource_enumeration_error_handler.go @@ -28,6 +28,12 @@ func HandleResourceEnumerationError(err error, alerter alerter.AlerterInterface) return handleGoogleEnumerationError(alerter, listError, status.Convert(rootCause)) } + // at least for storage api google sdk does not return grpc error so we parse the error message. + if shouldHandleGoogleForbiddenError(listError) { + alerts.SendEnumerationAlert(common.RemoteGoogleTerraform, alerter, listError) + return nil + } + reqerr, ok := rootCause.(awserr.RequestFailure) if ok { return handleAWSError(alerter, listError, reqerr) @@ -59,7 +65,7 @@ func HandleResourceDetailsFetchingError(err error, alerter alerter.AlerterInterf rootCause := listError.RootCause() - if shouldHandleGoogleDetailsFetchingError(listError) { + if shouldHandleGoogleForbiddenError(listError) { alerts.SendDetailsFetchingAlert(common.RemoteGoogleTerraform, alerter, listError) return nil } @@ -93,7 +99,7 @@ func handleGoogleEnumerationError(alerter alerter.AlerterInterface, err *remotee return err } -func shouldHandleGoogleDetailsFetchingError(err *remoteerror.ResourceScanningError) bool { +func shouldHandleGoogleForbiddenError(err *remoteerror.ResourceScanningError) bool { errMsg := err.RootCause().Error() // Check if this is a Google related error diff --git a/pkg/remote/test/google_storage_bucket_binding_multiple/results.golden.json b/pkg/remote/test/google_storage_bucket_binding_multiple/results.golden.json new file mode 100755 index 00000000..55d3ce5b --- /dev/null +++ b/pkg/remote/test/google_storage_bucket_binding_multiple/results.golden.json @@ -0,0 +1,42 @@ +[ + { + "bucket": "b/dctlgstoragebucketiambinding-1", + "condition": null, + "etag": null, + "id": "b/dctlgstoragebucketiambinding-1/roles/storage.admin", + "members": [ + "user:elie.charra@cloudskiff.com" + ], + "role": "roles/storage.admin" + }, + { + "bucket": "b/dctlgstoragebucketiambinding-1", + "condition": null, + "etag": null, + "id": "b/dctlgstoragebucketiambinding-1/roles/storage.objectViewer", + "members": [ + "user:william.beuil@cloudskiff.com" + ], + "role": "roles/storage.objectViewer" + }, + { + "bucket": "b/dctlgstoragebucketiambinding-2", + "condition": null, + "etag": null, + "id": "b/dctlgstoragebucketiambinding-2/roles/storage.objectViewer", + "members": [ + "user:elie.charra@cloudskiff.com" + ], + "role": "roles/storage.objectViewer" + }, + { + "bucket": "b/dctlgstoragebucketiambinding-2", + "condition": null, + "etag": null, + "id": "b/dctlgstoragebucketiambinding-2/roles/storage.admin", + "members": [ + "user:william.beuil@cloudskiff.com" + ], + "role": "roles/storage.admin" + } +] \ No newline at end of file diff --git a/pkg/resource/google/google_storage_bucket_iam_binding_test.go b/pkg/resource/google/google_storage_bucket_iam_binding_test.go new file mode 100644 index 00000000..a3d69d42 --- /dev/null +++ b/pkg/resource/google/google_storage_bucket_iam_binding_test.go @@ -0,0 +1,31 @@ +package google_test + +import ( + "testing" + + "github.com/cloudskiff/driftctl/test" + "github.com/cloudskiff/driftctl/test/acceptance" +) + +func TestAcc_Google_StorageBucketIAMBinding(t *testing.T) { + acceptance.Run(t, acceptance.AccTestCase{ + TerraformVersion: "0.15.5", + Paths: []string{"./testdata/acc/google_storage_bucket_iam_binding"}, + Args: []string{ + "scan", + "--to", "gcp+tf", + "--filter", "Type=='google_storage_bucket_iam_binding'", + }, + Checks: []acceptance.AccCheck{ + { + Check: func(result *test.ScanResult, stdout string, err error) { + if err != nil { + t.Fatal(err) + } + result.AssertInfrastructureIsInSync() + result.AssertManagedCount(3) + }, + }, + }, + }) +} diff --git a/pkg/resource/google/testdata/acc/google_storage_bucket_iam_binding/terraform.tf b/pkg/resource/google/testdata/acc/google_storage_bucket_iam_binding/terraform.tf new file mode 100644 index 00000000..e899de44 --- /dev/null +++ b/pkg/resource/google/testdata/acc/google_storage_bucket_iam_binding/terraform.tf @@ -0,0 +1,50 @@ +provider "google" {} + +terraform { + required_version = "~> 0.15.0" + required_providers { + google = { + version = "3.78.0" + } + } +} + +resource "random_string" "postfix" { + length = 6 + upper = false + special = false +} + +resource "google_storage_bucket" "driftctl-unittest" { + name = "driftctl-unittest-1-${random_string.postfix.result}" + location = "EU" +} + +resource "google_storage_bucket_iam_binding" "binding_admin_1" { + bucket = google_storage_bucket.driftctl-unittest.name + role = "roles/storage.admin" + members = [ + "user:elie.charra@cloudskiff.com", + ] +} + +resource "google_storage_bucket_iam_binding" "binding_viewer_1" { + bucket = google_storage_bucket.driftctl-unittest.name + role = "roles/storage.objectViewer" + members = [ + "user:william.beuil@cloudskiff.com", + ] +} + +resource "google_storage_bucket" "driftctl-unittest2" { + name = "driftctl-unittest-2-${random_string.postfix.result}" + location = "EU" +} + +resource "google_storage_bucket_iam_binding" "binding_admin_2" { + bucket = google_storage_bucket.driftctl-unittest2.name + role = "roles/storage.admin" + members = [ + "user:elie.charra@cloudskiff.com", + ] +} From f0b2d4a377b909bf3450e87a722ac4c0ee77fb24 Mon Sep 17 00:00:00 2001 From: Martin Guibert Date: Mon, 27 Sep 2021 11:29:47 +0200 Subject: [PATCH 3/4] add test for state reader --- .../state/terraform_state_reader_test.go | 1 + .../results.golden.json | 50 ++++++++ .../terraform.tfstate | 113 ++++++++++++++++++ .../google/repository/mock_AssetRepository.go | 23 ++++ pkg/remote/google_storage_scanner_test.go | 2 +- .../google_storage_bucket_iam_binding.go | 2 + 6 files changed, 190 insertions(+), 1 deletion(-) create mode 100755 pkg/iac/terraform/state/test/google_bucket_iam_binding/results.golden.json create mode 100644 pkg/iac/terraform/state/test/google_bucket_iam_binding/terraform.tfstate diff --git a/pkg/iac/terraform/state/terraform_state_reader_test.go b/pkg/iac/terraform/state/terraform_state_reader_test.go index e6bbadd1..7c6f9ac6 100644 --- a/pkg/iac/terraform/state/terraform_state_reader_test.go +++ b/pkg/iac/terraform/state/terraform_state_reader_test.go @@ -321,6 +321,7 @@ func TestTerraformStateReader_Google_Resources(t *testing.T) { {name: "compute firewall", dirName: "google_compute_firewall", wantErr: false}, {name: "compute router", dirName: "google_compute_router", wantErr: false}, {name: "compute instance", dirName: "google_compute_instance", wantErr: false}, + {name: "Bucket IAM Bindings", dirName: "google_bucket_iam_binding", wantErr: false}, } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { diff --git a/pkg/iac/terraform/state/test/google_bucket_iam_binding/results.golden.json b/pkg/iac/terraform/state/test/google_bucket_iam_binding/results.golden.json new file mode 100755 index 00000000..79dc0257 --- /dev/null +++ b/pkg/iac/terraform/state/test/google_bucket_iam_binding/results.golden.json @@ -0,0 +1,50 @@ +[ + { + "Id": "b/dctlgstoragebucketiambinding-2/roles/storage.admin", + "Type": "google_storage_bucket_iam_binding", + "Attrs": { + "bucket": "b/dctlgstoragebucketiambinding-2", + "id": "b/dctlgstoragebucketiambinding-2/roles/storage.admin", + "members": [ + "user:william.beuil@cloudskiff.com" + ], + "role": "roles/storage.admin" + } + }, + { + "Id": "b/dctlgstoragebucketiambinding-1/roles/storage.objectViewer", + "Type": "google_storage_bucket_iam_binding", + "Attrs": { + "bucket": "b/dctlgstoragebucketiambinding-1", + "id": "b/dctlgstoragebucketiambinding-1/roles/storage.objectViewer", + "members": [ + "user:william.beuil@cloudskiff.com" + ], + "role": "roles/storage.objectViewer" + } + }, + { + "Id": "b/dctlgstoragebucketiambinding-2/roles/storage.objectViewer", + "Type": "google_storage_bucket_iam_binding", + "Attrs": { + "bucket": "b/dctlgstoragebucketiambinding-2", + "id": "b/dctlgstoragebucketiambinding-2/roles/storage.objectViewer", + "members": [ + "user:elie.charra@cloudskiff.com" + ], + "role": "roles/storage.objectViewer" + } + }, + { + "Id": "b/dctlgstoragebucketiambinding-1/roles/storage.admin", + "Type": "google_storage_bucket_iam_binding", + "Attrs": { + "bucket": "b/dctlgstoragebucketiambinding-1", + "id": "b/dctlgstoragebucketiambinding-1/roles/storage.admin", + "members": [ + "user:elie.charra@cloudskiff.com" + ], + "role": "roles/storage.admin" + } + } +] \ No newline at end of file diff --git a/pkg/iac/terraform/state/test/google_bucket_iam_binding/terraform.tfstate b/pkg/iac/terraform/state/test/google_bucket_iam_binding/terraform.tfstate new file mode 100644 index 00000000..0eeb0873 --- /dev/null +++ b/pkg/iac/terraform/state/test/google_bucket_iam_binding/terraform.tfstate @@ -0,0 +1,113 @@ +{ + "version": 4, + "terraform_version": "0.14.5", + "serial": 53, + "lineage": "c2cd867e-c930-5de3-8592-8eb915e60cb8", + "outputs": {}, + "resources": [ + { + "mode": "managed", + "type": "google_storage_bucket_iam_binding", + "name": "binding_admin_1", + "provider": "provider[\"registry.terraform.io/hashicorp/google\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "bucket": "b/dctlgstoragebucketiambinding-1", + "condition": [], + "etag": "CAM=", + "id": "b/dctlgstoragebucketiambinding-1/roles/storage.admin", + "members": [ + "user:elie.charra@cloudskiff.com" + ], + "role": "roles/storage.admin" + }, + "sensitive_attributes": [], + "private": "bnVsbA==", + "dependencies": [ + "google_storage_bucket.dctlgstoragebucketiambinding-1" + ] + } + ] + }, + { + "mode": "managed", + "type": "google_storage_bucket_iam_binding", + "name": "binding_admin_2", + "provider": "provider[\"registry.terraform.io/hashicorp/google\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "bucket": "b/dctlgstoragebucketiambinding-2", + "condition": [], + "etag": "CAM=", + "id": "b/dctlgstoragebucketiambinding-2/roles/storage.admin", + "members": [ + "user:william.beuil@cloudskiff.com" + ], + "role": "roles/storage.admin" + }, + "sensitive_attributes": [], + "private": "bnVsbA==", + "dependencies": [ + "google_storage_bucket.dctlgstoragebucketiambinding-2" + ] + } + ] + }, + { + "mode": "managed", + "type": "google_storage_bucket_iam_binding", + "name": "binding_viewer_1", + "provider": "provider[\"registry.terraform.io/hashicorp/google\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "bucket": "b/dctlgstoragebucketiambinding-1", + "condition": [], + "etag": "CAM=", + "id": "b/dctlgstoragebucketiambinding-1/roles/storage.objectViewer", + "members": [ + "user:william.beuil@cloudskiff.com" + ], + "role": "roles/storage.objectViewer" + }, + "sensitive_attributes": [], + "private": "bnVsbA==", + "dependencies": [ + "google_storage_bucket.dctlgstoragebucketiambinding-1" + ] + } + ] + }, + { + "mode": "managed", + "type": "google_storage_bucket_iam_binding", + "name": "binding_viewer_2", + "provider": "provider[\"registry.terraform.io/hashicorp/google\"]", + "instances": [ + { + "schema_version": 0, + "attributes": { + "bucket": "b/dctlgstoragebucketiambinding-2", + "condition": [], + "etag": "CAM=", + "id": "b/dctlgstoragebucketiambinding-2/roles/storage.objectViewer", + "members": [ + "user:elie.charra@cloudskiff.com" + ], + "role": "roles/storage.objectViewer" + }, + "sensitive_attributes": [], + "private": "bnVsbA==", + "dependencies": [ + "google_storage_bucket.dctlgstoragebucketiambinding-2" + ] + } + ] + } + ] +} diff --git a/pkg/remote/google/repository/mock_AssetRepository.go b/pkg/remote/google/repository/mock_AssetRepository.go index 1b32e75a..1b19af1e 100644 --- a/pkg/remote/google/repository/mock_AssetRepository.go +++ b/pkg/remote/google/repository/mock_AssetRepository.go @@ -58,6 +58,29 @@ func (_m *MockAssetRepository) SearchAllFirewalls() ([]*asset.ResourceSearchResu return r0, r1 } +// SearchAllInstances provides a mock function with given fields: +func (_m *MockAssetRepository) SearchAllInstances() ([]*asset.ResourceSearchResult, error) { + ret := _m.Called() + + var r0 []*asset.ResourceSearchResult + if rf, ok := ret.Get(0).(func() []*asset.ResourceSearchResult); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*asset.ResourceSearchResult) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // SearchAllRouters provides a mock function with given fields: func (_m *MockAssetRepository) SearchAllRouters() ([]*asset.ResourceSearchResult, error) { ret := _m.Called() diff --git a/pkg/remote/google_storage_scanner_test.go b/pkg/remote/google_storage_scanner_test.go index 977f9c50..dd27e92c 100644 --- a/pkg/remote/google_storage_scanner_test.go +++ b/pkg/remote/google_storage_scanner_test.go @@ -289,8 +289,8 @@ func TestGoogleStorageBucketIAMBinding(t *testing.T) { c.storageRepositoryMock(storageRepo) } var storageRepository repository.StorageRepository = storageRepo - storageClient, err := storage.NewClient(context.Background()) if shouldUpdate { + storageClient, err := storage.NewClient(context.Background()) if err != nil { panic(err) } diff --git a/pkg/resource/google/google_storage_bucket_iam_binding.go b/pkg/resource/google/google_storage_bucket_iam_binding.go index c6d5cf3e..574860da 100644 --- a/pkg/resource/google/google_storage_bucket_iam_binding.go +++ b/pkg/resource/google/google_storage_bucket_iam_binding.go @@ -14,4 +14,6 @@ func initGoogleStorageBucketIamBindingMetadata(resourceSchemaRepository resource "bucket": *res.Attrs.GetString("bucket"), } }) + resourceSchemaRepository.SetFlags(GoogleStorageBucketIamBindingResourceType, resource.FlagDeepMode) + } From 1ca4c19240e5bf4b5876393c6f96962a52e48e0c Mon Sep 17 00:00:00 2001 From: Martin Guibert Date: Fri, 1 Oct 2021 11:48:38 +0200 Subject: [PATCH 4/4] add details fetcher --- go.mod | 2 +- pkg/remote/google/init.go | 2 ++ .../google/repository/mock_AssetRepository.go | 23 +++++++++++++++++++ .../google_storage_bucket_iam_binding.go | 1 + 4 files changed, 27 insertions(+), 1 deletion(-) diff --git a/go.mod b/go.mod index 1a74a376..b6f023f1 100644 --- a/go.mod +++ b/go.mod @@ -4,11 +4,11 @@ go 1.16 require ( cloud.google.com/go/asset v0.1.0 + cloud.google.com/go/storage v1.10.0 github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0 github.com/Azure/azure-sdk-for-go/sdk/azidentity v0.11.0 github.com/Azure/azure-sdk-for-go/sdk/network/armnetwork v0.3.0 github.com/Azure/azure-sdk-for-go/sdk/storage/armstorage v0.2.0 - cloud.google.com/go/storage v1.10.0 github.com/Azure/go-autorest/autorest v0.11.3 github.com/aws/aws-sdk-go v1.38.68 github.com/bmatcuk/doublestar/v4 v4.0.1 diff --git a/pkg/remote/google/init.go b/pkg/remote/google/init.go index 44ab282c..b23d9ad6 100644 --- a/pkg/remote/google/init.go +++ b/pkg/remote/google/init.go @@ -60,7 +60,9 @@ func Init(version string, alerter *alerter.Alerter, remoteLibrary.AddEnumerator(NewGoogleComputeRouterEnumerator(assetRepository, factory)) remoteLibrary.AddEnumerator(NewGoogleComputeInstanceEnumerator(assetRepository, factory)) + remoteLibrary.AddEnumerator(NewGoogleStorageBucketIamBindingEnumerator(assetRepository, storageRepository, factory)) + remoteLibrary.AddDetailsFetcher(google.GoogleStorageBucketIamBindingResourceType, common.NewGenericDetailsFetcher(google.GoogleStorageBucketIamBindingResourceType, provider, deserializer)) remoteLibrary.AddEnumerator(NewGoogleComputeNetworkEnumerator(assetRepository, factory)) remoteLibrary.AddDetailsFetcher(google.GoogleComputeNetworkResourceType, common.NewGenericDetailsFetcher(google.GoogleComputeNetworkResourceType, provider, deserializer)) diff --git a/pkg/remote/google/repository/mock_AssetRepository.go b/pkg/remote/google/repository/mock_AssetRepository.go index 1b19af1e..1c7661dc 100644 --- a/pkg/remote/google/repository/mock_AssetRepository.go +++ b/pkg/remote/google/repository/mock_AssetRepository.go @@ -81,6 +81,29 @@ func (_m *MockAssetRepository) SearchAllInstances() ([]*asset.ResourceSearchResu return r0, r1 } +// SearchAllNetworks provides a mock function with given fields: +func (_m *MockAssetRepository) SearchAllNetworks() ([]*asset.ResourceSearchResult, error) { + ret := _m.Called() + + var r0 []*asset.ResourceSearchResult + if rf, ok := ret.Get(0).(func() []*asset.ResourceSearchResult); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*asset.ResourceSearchResult) + } + } + + var r1 error + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + // SearchAllRouters provides a mock function with given fields: func (_m *MockAssetRepository) SearchAllRouters() ([]*asset.ResourceSearchResult, error) { ret := _m.Called() diff --git a/pkg/resource/google/google_storage_bucket_iam_binding.go b/pkg/resource/google/google_storage_bucket_iam_binding.go index 574860da..0cab7b00 100644 --- a/pkg/resource/google/google_storage_bucket_iam_binding.go +++ b/pkg/resource/google/google_storage_bucket_iam_binding.go @@ -12,6 +12,7 @@ func initGoogleStorageBucketIamBindingMetadata(resourceSchemaRepository resource resourceSchemaRepository.SetResolveReadAttributesFunc(GoogleStorageBucketIamBindingResourceType, func(res *resource.Resource) map[string]string { return map[string]string{ "bucket": *res.Attrs.GetString("bucket"), + "role": *res.Attrs.GetString("role"), } }) resourceSchemaRepository.SetFlags(GoogleStorageBucketIamBindingResourceType, resource.FlagDeepMode)