From 9350edc28a717343d6d0cbb27f352d21be910991 Mon Sep 17 00:00:00 2001 From: sundowndev Date: Mon, 4 Oct 2021 12:40:55 +0200 Subject: [PATCH 1/2] feat: add azurerm_resource_group --- go.mod | 1 + go.sum | 3 + .../azurerm_resource_group_enumerator.go | 47 ++++++ pkg/remote/azurerm/init.go | 2 + .../repository/mock_ResourcesRepository.go | 36 +++++ .../repository/mock_resourcesClient.go | 29 ++++ .../repository/mock_resourcesListPager.go | 58 +++++++ pkg/remote/azurerm/repository/resources.go | 67 ++++++++ .../azurerm/repository/resources_test.go | 152 ++++++++++++++++++ pkg/remote/azurerm_resources_scanner_test.go | 110 +++++++++++++ .../azurerm/azurerm_resource_group.go | 16 ++ .../azurerm/azurerm_resource_group_test.go | 31 ++++ pkg/resource/azurerm/metadata.go | 1 + .../.terraform.lock.hcl | 21 +++ .../acc/azurerm_resource_group/terraform.tf | 22 +++ pkg/resource/resource_types.go | 1 + 16 files changed, 597 insertions(+) create mode 100644 pkg/remote/azurerm/azurerm_resource_group_enumerator.go create mode 100644 pkg/remote/azurerm/repository/mock_ResourcesRepository.go create mode 100644 pkg/remote/azurerm/repository/mock_resourcesClient.go create mode 100644 pkg/remote/azurerm/repository/mock_resourcesListPager.go create mode 100644 pkg/remote/azurerm/repository/resources.go create mode 100644 pkg/remote/azurerm/repository/resources_test.go create mode 100644 pkg/remote/azurerm_resources_scanner_test.go create mode 100644 pkg/resource/azurerm/azurerm_resource_group.go create mode 100644 pkg/resource/azurerm/azurerm_resource_group_test.go create mode 100755 pkg/resource/azurerm/testdata/acc/azurerm_resource_group/.terraform.lock.hcl create mode 100644 pkg/resource/azurerm/testdata/acc/azurerm_resource_group/terraform.tf diff --git a/go.mod b/go.mod index b6f023f11..226a6cdba 100644 --- a/go.mod +++ b/go.mod @@ -8,6 +8,7 @@ require ( 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/resources/armresources v0.3.0 github.com/Azure/azure-sdk-for-go/sdk/storage/armstorage v0.2.0 github.com/Azure/go-autorest/autorest v0.11.3 github.com/aws/aws-sdk-go v1.38.68 diff --git a/go.sum b/go.sum index 0f4b5d219..821008ae7 100644 --- a/go.sum +++ b/go.sum @@ -48,6 +48,7 @@ cloud.google.com/go/storage v1.10.0/go.mod h1:FLPqc6j+Ki4BU591ie1oL6qBQGu2Bl/tZ9 dmitri.shuralyov.com/gpu/mtl v0.0.0-20190408044501-666a987793e9/go.mod h1:H6x//7gZCb22OMCxBHrMx7a5I7Hp++hsVxbQ4BYO7hU= github.com/AndreasBriese/bbloom v0.0.0-20190306092124-e2d15f34fcf9/go.mod h1:bOvUY6CB00SOBii9/FifXqc0awNKxLFCL/+pkDPuyl8= github.com/Azure/azure-sdk-for-go v45.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go v57.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go v57.1.0+incompatible h1:TKQ3ieyB0vVKkF6t9dsWbMjq56O1xU3eh3Ec09v6ajM= github.com/Azure/azure-sdk-for-go v57.1.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go/sdk/azcore v0.19.0 h1:lhSJz9RMbJcTgxifR1hUNJnn6CNYtbgEDtQV22/9RBA= @@ -59,6 +60,8 @@ github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0 h1:v9p9TfTbf7AwNb5NYQt7hI4 github.com/Azure/azure-sdk-for-go/sdk/internal v0.7.0/go.mod h1:yqy467j36fJxcRV2TzfVZ1pCb5vxm4BtZPUdYWe/Xo8= github.com/Azure/azure-sdk-for-go/sdk/network/armnetwork v0.3.0 h1:3ICM5L/XRaknp4DUNqdcNtiOzs6Mc3VKeyQp81+JS2Y= github.com/Azure/azure-sdk-for-go/sdk/network/armnetwork v0.3.0/go.mod h1:YSO+0IW+22kuLybFl2GAYaTDh1VWxNid83hqY/DkpGQ= +github.com/Azure/azure-sdk-for-go/sdk/resources/armresources v0.3.0 h1:I1cONUC2nKiGU3JXm2jRB4+QIs06lGqkplVpwy4ie9o= +github.com/Azure/azure-sdk-for-go/sdk/resources/armresources v0.3.0/go.mod h1:LdmyxRi5+2XPnbuv0X9c6ymGle+UkoNvqsBvG+oG53M= github.com/Azure/azure-sdk-for-go/sdk/storage/armstorage v0.2.0 h1:LOq4ZG6rMgTAZTyGbYHyxL1EVfZdngpUDRY/KvBToMs= github.com/Azure/azure-sdk-for-go/sdk/storage/armstorage v0.2.0/go.mod h1:mIFJgQ93RCQPBsN2jBDzDOfwJpLacGwXIxmirNQMiq4= github.com/Azure/go-autorest v14.2.0+incompatible h1:V5VMDjClD3GiElqLWO7mz2MxNAK/vTfRHdAubSIPRgs= diff --git a/pkg/remote/azurerm/azurerm_resource_group_enumerator.go b/pkg/remote/azurerm/azurerm_resource_group_enumerator.go new file mode 100644 index 000000000..c4e4f0823 --- /dev/null +++ b/pkg/remote/azurerm/azurerm_resource_group_enumerator.go @@ -0,0 +1,47 @@ +package azurerm + +import ( + "github.com/cloudskiff/driftctl/pkg/remote/azurerm/repository" + remoteerror "github.com/cloudskiff/driftctl/pkg/remote/error" + "github.com/cloudskiff/driftctl/pkg/resource" + "github.com/cloudskiff/driftctl/pkg/resource/azurerm" +) + +type AzurermResourceGroupEnumerator struct { + repository repository.ResourcesRepository + factory resource.ResourceFactory +} + +func NewAzurermResourceGroupEnumerator(repo repository.ResourcesRepository, factory resource.ResourceFactory) *AzurermResourceGroupEnumerator { + return &AzurermResourceGroupEnumerator{ + repository: repo, + factory: factory, + } +} + +func (e *AzurermResourceGroupEnumerator) SupportedType() resource.ResourceType { + return azurerm.AzureResourceGroupResourceType +} + +func (e *AzurermResourceGroupEnumerator) Enumerate() ([]*resource.Resource, error) { + groups, err := e.repository.ListAllResourceGroups() + if err != nil { + return nil, remoteerror.NewResourceListingError(err, string(e.SupportedType())) + } + + results := make([]*resource.Resource, 0) + for _, group := range groups { + results = append( + results, + e.factory.CreateAbstractResource( + string(e.SupportedType()), + *group.ID, + map[string]interface{}{ + "name": *group.Name, + }, + ), + ) + } + + return results, err +} diff --git a/pkg/remote/azurerm/init.go b/pkg/remote/azurerm/init.go index 0647d42da..76c59c9c6 100644 --- a/pkg/remote/azurerm/init.go +++ b/pkg/remote/azurerm/init.go @@ -43,6 +43,7 @@ func Init( storageAccountRepo := repository.NewStorageRepository(con, providerConfig, c) networkRepo := repository.NewNetworkRepository(con, providerConfig, c) + armResourcesRepo := repository.NewResourcesRepository(con, providerConfig, c) providerLibrary.AddProvider(terraform.AZURE, provider) @@ -50,6 +51,7 @@ func Init( remoteLibrary.AddEnumerator(NewAzurermStorageContainerEnumerator(storageAccountRepo, factory)) remoteLibrary.AddEnumerator(NewAzurermVirtualNetworkEnumerator(networkRepo, factory)) remoteLibrary.AddEnumerator(NewAzurermRouteTableEnumerator(networkRepo, factory)) + remoteLibrary.AddEnumerator(NewAzurermResourceGroupEnumerator(armResourcesRepo, factory)) err = resourceSchemaRepository.Init(terraform.AZURE, provider.Version(), provider.Schema()) if err != nil { diff --git a/pkg/remote/azurerm/repository/mock_ResourcesRepository.go b/pkg/remote/azurerm/repository/mock_ResourcesRepository.go new file mode 100644 index 000000000..154c5ad3e --- /dev/null +++ b/pkg/remote/azurerm/repository/mock_ResourcesRepository.go @@ -0,0 +1,36 @@ +// Code generated by mockery v0.0.0-dev. DO NOT EDIT. + +package repository + +import ( + armresources "github.com/Azure/azure-sdk-for-go/sdk/resources/armresources" + mock "github.com/stretchr/testify/mock" +) + +// MockResourcesRepository is an autogenerated mock type for the ResourcesRepository type +type MockResourcesRepository struct { + mock.Mock +} + +// ListAllResourceGroups provides a mock function with given fields: +func (_m *MockResourcesRepository) ListAllResourceGroups() ([]*armresources.ResourceGroup, error) { + ret := _m.Called() + + var r0 []*armresources.ResourceGroup + if rf, ok := ret.Get(0).(func() []*armresources.ResourceGroup); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]*armresources.ResourceGroup) + } + } + + 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/azurerm/repository/mock_resourcesClient.go b/pkg/remote/azurerm/repository/mock_resourcesClient.go new file mode 100644 index 000000000..0681eefa8 --- /dev/null +++ b/pkg/remote/azurerm/repository/mock_resourcesClient.go @@ -0,0 +1,29 @@ +// Code generated by mockery v0.0.0-dev. DO NOT EDIT. + +package repository + +import ( + armresources "github.com/Azure/azure-sdk-for-go/sdk/resources/armresources" + mock "github.com/stretchr/testify/mock" +) + +// mockResourcesClient is an autogenerated mock type for the resourcesClient type +type mockResourcesClient struct { + mock.Mock +} + +// List provides a mock function with given fields: options +func (_m *mockResourcesClient) List(options *armresources.ResourceGroupsListOptions) resourcesListPager { + ret := _m.Called(options) + + var r0 resourcesListPager + if rf, ok := ret.Get(0).(func(*armresources.ResourceGroupsListOptions) resourcesListPager); ok { + r0 = rf(options) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(resourcesListPager) + } + } + + return r0 +} diff --git a/pkg/remote/azurerm/repository/mock_resourcesListPager.go b/pkg/remote/azurerm/repository/mock_resourcesListPager.go new file mode 100644 index 000000000..2e31f32a0 --- /dev/null +++ b/pkg/remote/azurerm/repository/mock_resourcesListPager.go @@ -0,0 +1,58 @@ +// Code generated by mockery v0.0.0-dev. DO NOT EDIT. + +package repository + +import ( + context "context" + + armresources "github.com/Azure/azure-sdk-for-go/sdk/resources/armresources" + + mock "github.com/stretchr/testify/mock" +) + +// mockResourcesListPager is an autogenerated mock type for the resourcesListPager type +type mockResourcesListPager struct { + mock.Mock +} + +// Err provides a mock function with given fields: +func (_m *mockResourcesListPager) Err() error { + ret := _m.Called() + + var r0 error + if rf, ok := ret.Get(0).(func() error); ok { + r0 = rf() + } else { + r0 = ret.Error(0) + } + + return r0 +} + +// NextPage provides a mock function with given fields: ctx +func (_m *mockResourcesListPager) NextPage(ctx context.Context) bool { + ret := _m.Called(ctx) + + var r0 bool + if rf, ok := ret.Get(0).(func(context.Context) bool); ok { + r0 = rf(ctx) + } else { + r0 = ret.Get(0).(bool) + } + + return r0 +} + +// PageResponse provides a mock function with given fields: +func (_m *mockResourcesListPager) PageResponse() armresources.ResourceGroupsListResponse { + ret := _m.Called() + + var r0 armresources.ResourceGroupsListResponse + if rf, ok := ret.Get(0).(func() armresources.ResourceGroupsListResponse); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(armresources.ResourceGroupsListResponse) + } + + return r0 +} diff --git a/pkg/remote/azurerm/repository/resources.go b/pkg/remote/azurerm/repository/resources.go new file mode 100644 index 000000000..155efb04c --- /dev/null +++ b/pkg/remote/azurerm/repository/resources.go @@ -0,0 +1,67 @@ +package repository + +import ( + "context" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore/arm" + "github.com/Azure/azure-sdk-for-go/sdk/resources/armresources" + "github.com/cloudskiff/driftctl/pkg/remote/azurerm/common" + "github.com/cloudskiff/driftctl/pkg/remote/cache" +) + +type ResourcesRepository interface { + ListAllResourceGroups() ([]*armresources.ResourceGroup, error) +} + +type resourcesListPager interface { + pager + PageResponse() armresources.ResourceGroupsListResponse +} + +type resourcesClient interface { + List(options *armresources.ResourceGroupsListOptions) resourcesListPager +} + +type resourcesClientImpl struct { + client *armresources.ResourceGroupsClient +} + +func (c resourcesClientImpl) List(options *armresources.ResourceGroupsListOptions) resourcesListPager { + return c.client.List(options) +} + +type resourcesRepository struct { + client resourcesClient + cache cache.Cache +} + +func NewResourcesRepository(con *arm.Connection, config common.AzureProviderConfig, cache cache.Cache) *resourcesRepository { + return &resourcesRepository{ + &resourcesClientImpl{armresources.NewResourceGroupsClient(con, config.SubscriptionID)}, + cache, + } +} + +func (s *resourcesRepository) ListAllResourceGroups() ([]*armresources.ResourceGroup, error) { + cacheKey := "resourcesListAllResourceGroups" + if v := s.cache.Get(cacheKey); v != nil { + return v.([]*armresources.ResourceGroup), nil + } + + pager := s.client.List(nil) + results := make([]*armresources.ResourceGroup, 0) + for pager.NextPage(context.Background()) { + resp := pager.PageResponse() + if err := pager.Err(); err != nil { + return nil, err + } + results = append(results, resp.ResourceGroupsListResult.Value...) + } + if err := pager.Err(); err != nil { + return nil, err + } + + s.cache.Put(cacheKey, results) + + return results, nil +} diff --git a/pkg/remote/azurerm/repository/resources_test.go b/pkg/remote/azurerm/repository/resources_test.go new file mode 100644 index 000000000..f7a79f088 --- /dev/null +++ b/pkg/remote/azurerm/repository/resources_test.go @@ -0,0 +1,152 @@ +package repository + +import ( + "reflect" + "testing" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" + "github.com/Azure/azure-sdk-for-go/sdk/resources/armresources" + "github.com/cloudskiff/driftctl/pkg/remote/cache" + "github.com/pkg/errors" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +func Test_ArmResources_ListAllResourceGroups(t *testing.T) { + expectedResults := []*armresources.ResourceGroup{ + { + ID: to.StringPtr("/subscriptions/008b5f48-1b66-4d92-a6b6-d215b4c9b473/resourceGroups/elie-dev"), + Name: to.StringPtr("elie-dev"), + }, + { + ID: to.StringPtr("/subscriptions/008b5f48-1b66-4d92-a6b6-d215b4c9b473/resourceGroups/william-dev"), + Name: to.StringPtr("william-dev"), + }, + { + ID: to.StringPtr("/subscriptions/008b5f48-1b66-4d92-a6b6-d215b4c9b473/resourceGroups/driftctl-sj-tests"), + Name: to.StringPtr("driftctl-sj-tests"), + }, + } + + testcases := []struct { + name string + mocks func(*mockResourcesListPager, *cache.MockCache) + expected []*armresources.ResourceGroup + wantErr string + }{ + { + name: "should return resource groups", + mocks: func(mockPager *mockResourcesListPager, mockCache *cache.MockCache) { + mockPager.On("Err").Return(nil).Times(3) + mockPager.On("NextPage", mock.Anything).Return(true).Times(2) + mockPager.On("NextPage", mock.Anything).Return(false).Times(1) + mockPager.On("PageResponse").Return(armresources.ResourceGroupsListResponse{ + ResourceGroupsListResult: armresources.ResourceGroupsListResult{ + ResourceGroupListResult: armresources.ResourceGroupListResult{ + Value: []*armresources.ResourceGroup{ + { + ID: to.StringPtr("/subscriptions/008b5f48-1b66-4d92-a6b6-d215b4c9b473/resourceGroups/elie-dev"), + Name: to.StringPtr("elie-dev"), + }, + { + ID: to.StringPtr("/subscriptions/008b5f48-1b66-4d92-a6b6-d215b4c9b473/resourceGroups/william-dev"), + Name: to.StringPtr("william-dev"), + }, + }, + }, + }, + }).Times(1) + mockPager.On("PageResponse").Return(armresources.ResourceGroupsListResponse{ + ResourceGroupsListResult: armresources.ResourceGroupsListResult{ + ResourceGroupListResult: armresources.ResourceGroupListResult{ + Value: []*armresources.ResourceGroup{ + { + ID: to.StringPtr("/subscriptions/008b5f48-1b66-4d92-a6b6-d215b4c9b473/resourceGroups/driftctl-sj-tests"), + Name: to.StringPtr("driftctl-sj-tests"), + }, + }, + }, + }, + }).Times(1) + + mockCache.On("Get", "resourcesListAllResourceGroups").Return(nil).Times(1) + mockCache.On("Put", "resourcesListAllResourceGroups", expectedResults).Return(true).Times(1) + }, + expected: expectedResults, + }, + { + name: "should hit cache and return resource groups", + mocks: func(mockPager *mockResourcesListPager, mockCache *cache.MockCache) { + mockCache.On("Get", "resourcesListAllResourceGroups").Return(expectedResults).Times(1) + }, + expected: expectedResults, + }, + { + name: "should return remote error", + mocks: func(mockPager *mockResourcesListPager, mockCache *cache.MockCache) { + mockPager.On("NextPage", mock.Anything).Return(true).Times(1) + mockPager.On("PageResponse").Return(armresources.ResourceGroupsListResponse{ + ResourceGroupsListResult: armresources.ResourceGroupsListResult{ + ResourceGroupListResult: armresources.ResourceGroupListResult{ + Value: []*armresources.ResourceGroup{}, + }, + }, + }).Times(1) + mockPager.On("Err").Return(errors.New("remote error")).Times(1) + + mockCache.On("Get", "resourcesListAllResourceGroups").Return(nil).Times(1) + }, + wantErr: "remote error", + }, + { + name: "should return remote error after fetching all pages", + mocks: func(mockPager *mockResourcesListPager, mockCache *cache.MockCache) { + mockPager.On("NextPage", mock.Anything).Return(true).Times(1) + mockPager.On("NextPage", mock.Anything).Return(false).Times(1) + mockPager.On("PageResponse").Return(armresources.ResourceGroupsListResponse{ + ResourceGroupsListResult: armresources.ResourceGroupsListResult{ + ResourceGroupListResult: armresources.ResourceGroupListResult{ + Value: []*armresources.ResourceGroup{}, + }, + }, + }).Times(1) + mockPager.On("Err").Return(nil).Times(1) + mockPager.On("Err").Return(errors.New("remote error")).Times(1) + + mockCache.On("Get", "resourcesListAllResourceGroups").Return(nil).Times(1) + }, + wantErr: "remote error", + }, + } + + for _, tt := range testcases { + t.Run(tt.name, func(t *testing.T) { + fakeClient := &mockResourcesClient{} + mockPager := &mockResourcesListPager{} + mockCache := &cache.MockCache{} + + fakeClient.On("List", mock.Anything).Maybe().Return(mockPager) + + tt.mocks(mockPager, mockCache) + + s := &resourcesRepository{ + client: fakeClient, + cache: mockCache, + } + got, err := s.ListAllResourceGroups() + if tt.wantErr != "" { + assert.EqualError(t, err, tt.wantErr) + } else { + assert.Nil(t, err) + } + + fakeClient.AssertExpectations(t) + mockPager.AssertExpectations(t) + mockCache.AssertExpectations(t) + + if !reflect.DeepEqual(got, tt.expected) { + t.Errorf("ListAllResourceGroups() got = %v, want %v", got, tt.expected) + } + }) + } +} diff --git a/pkg/remote/azurerm_resources_scanner_test.go b/pkg/remote/azurerm_resources_scanner_test.go new file mode 100644 index 000000000..bade6a635 --- /dev/null +++ b/pkg/remote/azurerm_resources_scanner_test.go @@ -0,0 +1,110 @@ +package remote + +import ( + "testing" + + "github.com/Azure/azure-sdk-for-go/sdk/azcore/to" + "github.com/Azure/azure-sdk-for-go/sdk/resources/armresources" + "github.com/cloudskiff/driftctl/mocks" + "github.com/cloudskiff/driftctl/pkg/filter" + "github.com/cloudskiff/driftctl/pkg/remote/azurerm" + "github.com/cloudskiff/driftctl/pkg/remote/azurerm/repository" + "github.com/cloudskiff/driftctl/pkg/remote/common" + error2 "github.com/cloudskiff/driftctl/pkg/remote/error" + "github.com/cloudskiff/driftctl/pkg/resource" + resourceazure "github.com/cloudskiff/driftctl/pkg/resource/azurerm" + "github.com/cloudskiff/driftctl/pkg/terraform" + testresource "github.com/cloudskiff/driftctl/test/resource" + "github.com/pkg/errors" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" +) + +func TestAzurermResourceGroups(t *testing.T) { + + dummyError := errors.New("this is an error") + + tests := []struct { + test string + mocks func(*repository.MockResourcesRepository, *mocks.AlerterInterface) + assertExpected func(t *testing.T, got []*resource.Resource) + wantErr error + }{ + { + test: "no resource group", + mocks: func(repository *repository.MockResourcesRepository, alerter *mocks.AlerterInterface) { + repository.On("ListAllResourceGroups").Return([]*armresources.ResourceGroup{}, nil) + }, + assertExpected: func(t *testing.T, got []*resource.Resource) { + assert.Len(t, got, 0) + }, + }, + { + test: "error listing resource groups", + mocks: func(repository *repository.MockResourcesRepository, alerter *mocks.AlerterInterface) { + repository.On("ListAllResourceGroups").Return(nil, dummyError) + }, + wantErr: error2.NewResourceListingError(dummyError, resourceazure.AzureResourceGroupResourceType), + }, + { + test: "multiple resource groups", + mocks: func(repository *repository.MockResourcesRepository, alerter *mocks.AlerterInterface) { + repository.On("ListAllResourceGroups").Return([]*armresources.ResourceGroup{ + { + ID: to.StringPtr("group1"), + Name: to.StringPtr("group1"), + }, + { + ID: to.StringPtr("group2"), + Name: to.StringPtr("group2"), + }, + }, nil) + }, + assertExpected: func(t *testing.T, got []*resource.Resource) { + assert.Len(t, got, 2) + + assert.Equal(t, got[0].ResourceId(), "group1") + assert.Equal(t, got[0].ResourceType(), resourceazure.AzureResourceGroupResourceType) + + assert.Equal(t, got[1].ResourceId(), "group2") + assert.Equal(t, got[1].ResourceType(), resourceazure.AzureResourceGroupResourceType) + }, + }, + } + + providerVersion := "2.71.0" + schemaRepository := testresource.InitFakeSchemaRepository("azurerm", providerVersion) + resourceazure.InitResourcesMetadata(schemaRepository) + factory := terraform.NewTerraformResourceFactory(schemaRepository) + + for _, c := range tests { + t.Run(c.test, func(tt *testing.T) { + + scanOptions := ScannerOptions{} + remoteLibrary := common.NewRemoteLibrary() + + // Initialize mocks + alerter := &mocks.AlerterInterface{} + fakeRepo := &repository.MockResourcesRepository{} + c.mocks(fakeRepo, alerter) + + var repo repository.ResourcesRepository = fakeRepo + + remoteLibrary.AddEnumerator(azurerm.NewAzurermResourceGroupEnumerator(repo, 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 + } + + c.assertExpected(tt, got) + alerter.AssertExpectations(tt) + fakeRepo.AssertExpectations(tt) + }) + } +} diff --git a/pkg/resource/azurerm/azurerm_resource_group.go b/pkg/resource/azurerm/azurerm_resource_group.go new file mode 100644 index 000000000..85a0a86e1 --- /dev/null +++ b/pkg/resource/azurerm/azurerm_resource_group.go @@ -0,0 +1,16 @@ +package azurerm + +import "github.com/cloudskiff/driftctl/pkg/resource" + +const AzureResourceGroupResourceType = "azurerm_resource_group" + +func initAzureResourceGroupMetadata(resourceSchemaRepository resource.SchemaRepositoryInterface) { + resourceSchemaRepository.SetHumanReadableAttributesFunc(AzureResourceGroupResourceType, func(res *resource.Resource) map[string]string { + val := res.Attrs + attrs := make(map[string]string) + if name := val.GetString("name"); name != nil && *name != "" { + attrs["Name"] = *name + } + return attrs + }) +} diff --git a/pkg/resource/azurerm/azurerm_resource_group_test.go b/pkg/resource/azurerm/azurerm_resource_group_test.go new file mode 100644 index 000000000..8df4938ae --- /dev/null +++ b/pkg/resource/azurerm/azurerm_resource_group_test.go @@ -0,0 +1,31 @@ +package azurerm_test + +import ( + "testing" + + "github.com/cloudskiff/driftctl/test" + "github.com/cloudskiff/driftctl/test/acceptance" +) + +func TestAcc_Azure_ResourceGroup(t *testing.T) { + acceptance.Run(t, acceptance.AccTestCase{ + TerraformVersion: "0.15.5", + Paths: []string{"./testdata/acc/azurerm_resource_group"}, + Args: []string{ + "scan", + "--to", "azure+tf", + "--filter", "Type=='azurerm_resource_group' && contains(Id, 'acc-test-res-group-')", + }, + Checks: []acceptance.AccCheck{ + { + Check: func(result *test.ScanResult, stdout string, err error) { + if err != nil { + t.Fatal(err) + } + result.AssertInfrastructureIsInSync() + result.AssertManagedCount(2) + }, + }, + }, + }) +} diff --git a/pkg/resource/azurerm/metadata.go b/pkg/resource/azurerm/metadata.go index babdc8f27..a21761189 100644 --- a/pkg/resource/azurerm/metadata.go +++ b/pkg/resource/azurerm/metadata.go @@ -5,4 +5,5 @@ import "github.com/cloudskiff/driftctl/pkg/resource" func InitResourcesMetadata(resourceSchemaRepository resource.SchemaRepositoryInterface) { initAzureVirtualNetworkMetaData(resourceSchemaRepository) initAzureRouteTableMetaData(resourceSchemaRepository) + initAzureResourceGroupMetadata(resourceSchemaRepository) } diff --git a/pkg/resource/azurerm/testdata/acc/azurerm_resource_group/.terraform.lock.hcl b/pkg/resource/azurerm/testdata/acc/azurerm_resource_group/.terraform.lock.hcl new file mode 100755 index 000000000..67ef8eaf0 --- /dev/null +++ b/pkg/resource/azurerm/testdata/acc/azurerm_resource_group/.terraform.lock.hcl @@ -0,0 +1,21 @@ +# This file is maintained automatically by "terraform init". +# Manual edits may be lost in future updates. + +provider "registry.terraform.io/hashicorp/azurerm" { + version = "2.71.0" + constraints = "~> 2.71.0" + hashes = [ + "h1:RiFIxNI4Yr9CqleqEdgg1ydLAZ5JiYiz6l5iTD3WcuU=", + "zh:2b9d8a703a0222f72cbceb8d2bdb580066afdcd7f28b6ad65d5ed935319b5433", + "zh:332988f4c1747bcc8ebd32734bf8de2bea4c13a6fbd08d7eb97d0c43d335b15e", + "zh:3a902470276ba48e23ad4dd6baff16a9ce3b60b29c0b07064dbe96ce4640a31c", + "zh:5eaa0d0c2c6554913421be10fbf4bb6a9ef98fbbd750d3d1f02c99798aae2c22", + "zh:67859f40ed2f770f33ace9d3911e8b9c9be505947b38a0578e6d097f5db1d4bf", + "zh:7cd9bf4899fe383fc7eeede03cad138d637244878cd295a7a1044ca20ca0652c", + "zh:afcb82c1382a1a9d63a41137321e077144aad768e4e46057a7ea604d067b4181", + "zh:c6e358759ed00a628dcfe7adb0906b2c98576ac3056fdd70930786d404e1da66", + "zh:cb3390c34f6790ad656929d0268ab3bc082678e8cbe2add0a177cf7896068844", + "zh:cc213dbf59cf41506e86b83492ccfef6ef5f34d4d00d9e49fc8a01fee253f4ee", + "zh:d1e8c9b507e2d187ea2447ae156028ba3f76db2164674761987c14217d04fee5", + ] +} diff --git a/pkg/resource/azurerm/testdata/acc/azurerm_resource_group/terraform.tf b/pkg/resource/azurerm/testdata/acc/azurerm_resource_group/terraform.tf new file mode 100644 index 000000000..ddb18018d --- /dev/null +++ b/pkg/resource/azurerm/testdata/acc/azurerm_resource_group/terraform.tf @@ -0,0 +1,22 @@ +terraform { + required_providers { + azurerm = { + source = "hashicorp/azurerm" + version = "~> 2.71.0" + } + } +} + +provider "azurerm" { + features {} +} + +resource "azurerm_resource_group" "test-1" { + name = "acc-test-res-group-1" + location = "West Europe" +} + +resource "azurerm_resource_group" "test-2" { + name = "acc-test-res-group-2" + location = "West Europe" +} diff --git a/pkg/resource/resource_types.go b/pkg/resource/resource_types.go index a83be32f8..7ce95386d 100644 --- a/pkg/resource/resource_types.go +++ b/pkg/resource/resource_types.go @@ -84,6 +84,7 @@ var supportedTypes = map[string]struct{}{ "azurerm_storage_container": {}, "azurerm_virtual_network": {}, "azurerm_route_table": {}, + "azurerm_resource_group": {}, } func IsResourceTypeSupported(ty string) bool { From 0cf7f8b4a0a98a13c5d81c171f2c327bcf615999 Mon Sep 17 00:00:00 2001 From: sundowndev Date: Mon, 4 Oct 2021 13:59:13 +0200 Subject: [PATCH 2/2] refactor(azurerm): remove arm from resources repo --- pkg/remote/azurerm/init.go | 4 ++-- pkg/remote/azurerm/repository/resources_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkg/remote/azurerm/init.go b/pkg/remote/azurerm/init.go index 76c59c9c6..e3fc8e8f5 100644 --- a/pkg/remote/azurerm/init.go +++ b/pkg/remote/azurerm/init.go @@ -43,7 +43,7 @@ func Init( storageAccountRepo := repository.NewStorageRepository(con, providerConfig, c) networkRepo := repository.NewNetworkRepository(con, providerConfig, c) - armResourcesRepo := repository.NewResourcesRepository(con, providerConfig, c) + resourcesRepo := repository.NewResourcesRepository(con, providerConfig, c) providerLibrary.AddProvider(terraform.AZURE, provider) @@ -51,7 +51,7 @@ func Init( remoteLibrary.AddEnumerator(NewAzurermStorageContainerEnumerator(storageAccountRepo, factory)) remoteLibrary.AddEnumerator(NewAzurermVirtualNetworkEnumerator(networkRepo, factory)) remoteLibrary.AddEnumerator(NewAzurermRouteTableEnumerator(networkRepo, factory)) - remoteLibrary.AddEnumerator(NewAzurermResourceGroupEnumerator(armResourcesRepo, factory)) + remoteLibrary.AddEnumerator(NewAzurermResourceGroupEnumerator(resourcesRepo, factory)) err = resourceSchemaRepository.Init(terraform.AZURE, provider.Version(), provider.Schema()) if err != nil { diff --git a/pkg/remote/azurerm/repository/resources_test.go b/pkg/remote/azurerm/repository/resources_test.go index f7a79f088..07b9cd077 100644 --- a/pkg/remote/azurerm/repository/resources_test.go +++ b/pkg/remote/azurerm/repository/resources_test.go @@ -12,7 +12,7 @@ import ( "github.com/stretchr/testify/mock" ) -func Test_ArmResources_ListAllResourceGroups(t *testing.T) { +func Test_Resources_ListAllResourceGroups(t *testing.T) { expectedResults := []*armresources.ResourceGroup{ { ID: to.StringPtr("/subscriptions/008b5f48-1b66-4d92-a6b6-d215b4c9b473/resourceGroups/elie-dev"),